First commit, not compiling (stuck in RadioHead library).
This commit is contained in:
commit
873f8befe1
|
@ -0,0 +1,3 @@
|
||||||
|
*.tmp
|
||||||
|
build/
|
||||||
|
deps/
|
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright 2017 Cesanta Software Limited
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Arduino Adafruit SSD1306 C/C++ example
|
||||||
|
|
||||||
|
This example shows how to use Adafruit SSD1306 on Mongoose OS in C/C++.
|
|
@ -0,0 +1,12 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>FastClock-Master</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<ul>
|
||||||
|
<li><a href="ca.pem">ca.pem</a></li>
|
||||||
|
<li><a href="conf0.json">conf0.json</a></li>
|
||||||
|
<li><a href="conf9.json">conf9.json</a></li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,67 @@
|
||||||
|
author: mongoose-os
|
||||||
|
description: Fastclock Master Controller
|
||||||
|
version: 1.0.1
|
||||||
|
name: fastclock-master
|
||||||
|
|
||||||
|
libs_version: ${mos.version}
|
||||||
|
modules_version: ${mos.version}
|
||||||
|
mongoose_os_version: ${mos.version}
|
||||||
|
|
||||||
|
platform: esp8266
|
||||||
|
|
||||||
|
sources:
|
||||||
|
- src
|
||||||
|
- src/RadioHead
|
||||||
|
|
||||||
|
includes:
|
||||||
|
- src/RadioHead
|
||||||
|
|
||||||
|
filesystem:
|
||||||
|
- fs
|
||||||
|
|
||||||
|
cdefs:
|
||||||
|
MPIDE: 0
|
||||||
|
TEENSYDUINO: 0
|
||||||
|
RH_PLATFORM: RH_PLATFORM_ESP8266
|
||||||
|
|
||||||
|
libs:
|
||||||
|
# common mgos libs
|
||||||
|
- origin: https://github.com/mongoose-os-libs/boards
|
||||||
|
- origin: https://github.com/mongoose-os-libs/ca-bundle
|
||||||
|
- origin: https://github.com/mongoose-os-libs/core
|
||||||
|
- origin: https://github.com/mongoose-os-libs/dash
|
||||||
|
- origin: https://github.com/mongoose-os-libs/i2c
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-service-config
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-service-fs
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-uart
|
||||||
|
- origin: https://github.com/mongoose-os-libs/spi
|
||||||
|
- origin: https://github.com/mongoose-os-libs/http-server
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-service-config
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-service-fs
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-service-ota
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-service-wifi
|
||||||
|
- origin: https://github.com/mongoose-os-libs/rpc-uart
|
||||||
|
- origin: https://github.com/mongoose-os-libs/ota-http-server
|
||||||
|
- origin: https://github.com/mongoose-os-libs/ota-shadow
|
||||||
|
- origin: https://github.com/mongoose-os-libs/wifi
|
||||||
|
- origin: https://github.com/mongoose-os-libs/arduino-compat
|
||||||
|
- origin: https://github.com/mongoose-os-libs/arduino-adafruit-ssd1306
|
||||||
|
- origin: https://github.com/mongoose-os-libs/arduino-spi
|
||||||
|
# - origin: https://gitea.pmpark.de/dirk/Mongoose_Arduino_RadioHead.git
|
||||||
|
|
||||||
|
config_schema:
|
||||||
|
- ["i2c.enable", true]
|
||||||
|
- ["i2c.scl_gpio", 5]
|
||||||
|
- ["i2c.sda_gpio", 4]
|
||||||
|
- ["i2c.freq", 1000000]
|
||||||
|
- ["wifi.ap.ssid", "Fastclock-Master-??????"]
|
||||||
|
- ["wifi.ap.pass", "Fastclock-Master"]
|
||||||
|
- ["wifi.sta.ssid", "Pinguin"]
|
||||||
|
- ["wifi.sta.pass", "PaulchenAufmKlo34"]
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- c
|
||||||
|
- arduino
|
||||||
|
- hw
|
||||||
|
|
||||||
|
manifest_version: 2017-05-18
|
|
@ -0,0 +1,120 @@
|
||||||
|
// 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();
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
// RHDatagram.h
|
||||||
|
// Author: Mike McCauley (mikem@airspayce.com)
|
||||||
|
// Copyright (C) 2011 Mike McCauley
|
||||||
|
// $Id: RHDatagram.h,v 1.14 2015/08/12 23:18:51 mikem Exp $
|
||||||
|
|
||||||
|
#ifndef RHDatagram_h
|
||||||
|
#define RHDatagram_h
|
||||||
|
|
||||||
|
#include "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
|
|
@ -0,0 +1,184 @@
|
||||||
|
// RHGenericDriver.cpp
|
||||||
|
//
|
||||||
|
// Copyright (C) 2014 Mike McCauley
|
||||||
|
// $Id: RHGenericDriver.cpp,v 1.19 2015/12/11 01:10:24 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
|
@ -0,0 +1,265 @@
|
||||||
|
// RHGenericDriver.h
|
||||||
|
// Author: Mike McCauley (mikem@airspayce.com)
|
||||||
|
// Copyright (C) 2014 Mike McCauley
|
||||||
|
// $Id: RHGenericDriver.h,v 1.17 2016/04/04 01:40:12 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
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
/// \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.
|
||||||
|
} 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 loads a message into the transmitter and starts the transmitter. Note that a message length
|
||||||
|
/// of 0 is NOT permitted. If the message is too long for the underlying radio technology, send() will
|
||||||
|
/// return false and will not send the message.
|
||||||
|
/// \param[in] data Array of data to be sent
|
||||||
|
/// \param[in] len Number of bytes of data to send (> 0)
|
||||||
|
/// \return true if the message length was valid and it was correctly queued for transmit
|
||||||
|
virtual bool send(const uint8_t* data, uint8_t len) = 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 RF22 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);
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,30 @@
|
||||||
|
// 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;
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
|
||||||
|
#include <SPI.h> // for SPI_HAS_TRANSACTION and SPISettings
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
/// \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);
|
||||||
|
|
||||||
|
// Try to add SPI Transaction support
|
||||||
|
// Note: Maybe add some way to set SPISettings?
|
||||||
|
virtual void beginTransaction() {};
|
||||||
|
virtual void endTransaction() {};
|
||||||
|
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
|
|
@ -0,0 +1,411 @@
|
||||||
|
// 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 mikem $
|
||||||
|
|
||||||
|
#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(SPI_HAS_TRANSACTION)
|
||||||
|
uint32_t frequency32;
|
||||||
|
if (_frequency == Frequency16MHz) {
|
||||||
|
frequency32 = 16000000;
|
||||||
|
} else if (_frequency == Frequency8MHz) {
|
||||||
|
frequency32 = 8000000;
|
||||||
|
} else if (_frequency == Frequency4MHz) {
|
||||||
|
frequency32 = 4000000;
|
||||||
|
} else if (_frequency == Frequency2MHz) {
|
||||||
|
frequency32 = 2000000;
|
||||||
|
} else {
|
||||||
|
frequency32 = 1000000;
|
||||||
|
}
|
||||||
|
_settings = SPISettings(frequency32,
|
||||||
|
(_bitOrder == BitOrderLSBFirst) ? LSBFIRST : MSBFIRST,
|
||||||
|
dataMode);
|
||||||
|
//Serial.print("SPISettings: "); Serial.println(frequency32, DEC);
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our platform is arduino and we support transactions then lets use the begin/end transaction
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(SPI_HAS_TRANSACTION)
|
||||||
|
void RHHardwareSPI::beginTransaction()
|
||||||
|
{
|
||||||
|
SPI.beginTransaction(_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RHHardwareSPI::endTransaction()
|
||||||
|
{
|
||||||
|
SPI.endTransaction();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,73 @@
|
||||||
|
// RHHardwareSPI.h
|
||||||
|
// Author: Mike McCauley (mikem@airspayce.com)
|
||||||
|
// Copyright (C) 2011 Mike McCauley
|
||||||
|
// Contributed by Joanna Rutkowska
|
||||||
|
// $Id: RHHardwareSPI.h,v 1.9 2014/08/12 00:54:52 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();
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(SPI_HAS_TRANSACTION)
|
||||||
|
public:
|
||||||
|
void beginTransaction();
|
||||||
|
void endTransaction();
|
||||||
|
SPISettings _settings;
|
||||||
|
#endif
|
||||||
|
#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
|
|
@ -0,0 +1,111 @@
|
||||||
|
// 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;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
digitalWrite(_slaveSelectPin, LOW);
|
||||||
|
status = _spi.transfer(command);
|
||||||
|
digitalWrite(_slaveSelectPin, HIGH);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RHNRFSPIDriver::spiRead(uint8_t reg)
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
digitalWrite(_slaveSelectPin, LOW);
|
||||||
|
_spi.transfer(reg); // Send the address, discard the status
|
||||||
|
val = _spi.transfer(0); // The written value is ignored, reg value is read
|
||||||
|
digitalWrite(_slaveSelectPin, HIGH);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RHNRFSPIDriver::spiWrite(uint8_t reg, uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
digitalWrite(_slaveSelectPin, LOW);
|
||||||
|
status = _spi.transfer(reg); // Send the address
|
||||||
|
_spi.transfer(val); // New value follows
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined(__arm__) && defined(CORE_TEENSY)
|
||||||
|
// Sigh: some devices, such as MRF89XA dont work properly on Teensy 3.1:
|
||||||
|
// At 1MHz, the clock returns low _after_ slave select goes high, which prevents SPI
|
||||||
|
// write working. This delay gixes time for the clock to return low.
|
||||||
|
delayMicroseconds(5);
|
||||||
|
#endif
|
||||||
|
digitalWrite(_slaveSelectPin, HIGH);
|
||||||
|
_spi.endTransaction();
|
||||||
|
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;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
digitalWrite(_slaveSelectPin, LOW);
|
||||||
|
status = _spi.transfer(reg); // Send the start address
|
||||||
|
while (len--)
|
||||||
|
*dest++ = _spi.transfer(0);
|
||||||
|
digitalWrite(_slaveSelectPin, HIGH);
|
||||||
|
_spi.endTransaction();
|
||||||
|
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;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
digitalWrite(_slaveSelectPin, LOW);
|
||||||
|
status = _spi.transfer(reg); // Send the start address
|
||||||
|
while (len--)
|
||||||
|
_spi.transfer(*src++);
|
||||||
|
digitalWrite(_slaveSelectPin, HIGH);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RHNRFSPIDriver::setSlaveSelectPin(uint8_t slaveSelectPin)
|
||||||
|
{
|
||||||
|
_slaveSelectPin = slaveSelectPin;
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
// 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
|
|
@ -0,0 +1,186 @@
|
||||||
|
// 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.15 2015/12/11 01:10:24 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// 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();
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
// 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
|
|
@ -0,0 +1,305 @@
|
||||||
|
// 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;
|
||||||
|
}
|
|
@ -0,0 +1,327 @@
|
||||||
|
// 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
|
|
@ -0,0 +1,91 @@
|
||||||
|
// 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;
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
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);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RHSPIDriver::spiWrite(uint8_t reg, uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
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);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RHSPIDriver::spiBurstRead(uint8_t reg, uint8_t* dest, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
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);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RHSPIDriver::spiBurstWrite(uint8_t reg, const uint8_t* src, uint8_t len)
|
||||||
|
{
|
||||||
|
uint8_t status = 0;
|
||||||
|
ATOMIC_BLOCK_START;
|
||||||
|
_spi.beginTransaction();
|
||||||
|
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);
|
||||||
|
_spi.endTransaction();
|
||||||
|
ATOMIC_BLOCK_END;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RHSPIDriver::setSlaveSelectPin(uint8_t slaveSelectPin)
|
||||||
|
{
|
||||||
|
_slaveSelectPin = slaveSelectPin;
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,165 @@
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
// 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
|
|
@ -0,0 +1,338 @@
|
||||||
|
// NRF24.cpp
|
||||||
|
//
|
||||||
|
// Copyright (C) 2012 Mike McCauley
|
||||||
|
// $Id: RH_NRF24.cpp,v 1.22 2016/04/04 01:40:12 mikem Exp $
|
||||||
|
|
||||||
|
#include "RH_NRF24.h"
|
||||||
|
|
||||||
|
RH_NRF24::RH_NRF24(uint8_t chipEnablePin, uint8_t slaveSelectPin, RHGenericSPI& spi)
|
||||||
|
:
|
||||||
|
RHNRFSPIDriver(slaveSelectPin, spi),
|
||||||
|
_rxBufValid(0)
|
||||||
|
{
|
||||||
|
_configuration = RH_NRF24_EN_CRC | RH_NRF24_CRCO; // Default: 2 byte CRC enabled
|
||||||
|
_chipEnablePin = chipEnablePin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::init()
|
||||||
|
{
|
||||||
|
// Teensy with nRF24 is unreliable at 8MHz:
|
||||||
|
// so is Arduino with RF73
|
||||||
|
_spi.setFrequency(RHGenericSPI::Frequency1MHz);
|
||||||
|
if (!RHNRFSPIDriver::init())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Initialise the slave select pin
|
||||||
|
pinMode(_chipEnablePin, OUTPUT);
|
||||||
|
digitalWrite(_chipEnablePin, LOW);
|
||||||
|
|
||||||
|
// Clear interrupts
|
||||||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR | RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
|
||||||
|
// Enable dynamic payload length on all pipes
|
||||||
|
spiWriteRegister(RH_NRF24_REG_1C_DYNPD, RH_NRF24_DPL_ALL);
|
||||||
|
// Enable dynamic payload length, disable payload-with-ack, enable noack
|
||||||
|
spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK);
|
||||||
|
// Test if there is actually a device connected and responding
|
||||||
|
// CAUTION: RFM73 and version 2.0 silicon may require ACTIVATE
|
||||||
|
if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK))
|
||||||
|
{
|
||||||
|
spiWrite(RH_NRF24_COMMAND_ACTIVATE, 0x73);
|
||||||
|
// Enable dynamic payload length, disable payload-with-ack, enable noack
|
||||||
|
spiWriteRegister(RH_NRF24_REG_1D_FEATURE, RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK);
|
||||||
|
if (spiReadRegister(RH_NRF24_REG_1D_FEATURE) != (RH_NRF24_EN_DPL | RH_NRF24_EN_DYN_ACK))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we are powered down
|
||||||
|
setModeIdle();
|
||||||
|
|
||||||
|
// Flush FIFOs
|
||||||
|
flushTx();
|
||||||
|
flushRx();
|
||||||
|
|
||||||
|
setChannel(2); // The default, in case it was set by another app without powering down
|
||||||
|
setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the register commands to read and write the registers
|
||||||
|
uint8_t RH_NRF24::spiReadRegister(uint8_t reg)
|
||||||
|
{
|
||||||
|
return spiRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::spiWriteRegister(uint8_t reg, uint8_t val)
|
||||||
|
{
|
||||||
|
return spiWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len)
|
||||||
|
{
|
||||||
|
return spiBurstRead((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_R_REGISTER, dest, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len)
|
||||||
|
{
|
||||||
|
return spiBurstWrite((reg & RH_NRF24_REGISTER_MASK) | RH_NRF24_COMMAND_W_REGISTER, src, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::statusRead()
|
||||||
|
{
|
||||||
|
// status is a side-effect of NOP, faster than reading reg 07
|
||||||
|
return spiCommand(RH_NRF24_COMMAND_NOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::flushTx()
|
||||||
|
{
|
||||||
|
return spiCommand(RH_NRF24_COMMAND_FLUSH_TX);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::flushRx()
|
||||||
|
{
|
||||||
|
return spiCommand(RH_NRF24_COMMAND_FLUSH_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::setChannel(uint8_t channel)
|
||||||
|
{
|
||||||
|
spiWriteRegister(RH_NRF24_REG_05_RF_CH, channel & RH_NRF24_RF_CH);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::setOpMode(uint8_t mode)
|
||||||
|
{
|
||||||
|
_configuration = mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::setNetworkAddress(uint8_t* address, uint8_t len)
|
||||||
|
{
|
||||||
|
if (len < 3 || len > 5)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Set both TX_ADDR and RX_ADDR_P0 for auto-ack with Enhanced shockwave
|
||||||
|
spiWriteRegister(RH_NRF24_REG_03_SETUP_AW, len-2); // Mapping [3..5] = [1..3]
|
||||||
|
spiBurstWriteRegister(RH_NRF24_REG_0A_RX_ADDR_P0, address, len);
|
||||||
|
spiBurstWriteRegister(RH_NRF24_REG_10_TX_ADDR, address, len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::setRF(DataRate data_rate, TransmitPower power)
|
||||||
|
{
|
||||||
|
uint8_t value = (power << 1) & RH_NRF24_PWR;
|
||||||
|
// Ugly mapping of data rates to noncontiguous 2 bits:
|
||||||
|
if (data_rate == DataRate250kbps)
|
||||||
|
value |= RH_NRF24_RF_DR_LOW;
|
||||||
|
else if (data_rate == DataRate2Mbps)
|
||||||
|
value |= RH_NRF24_RF_DR_HIGH;
|
||||||
|
// else DataRate1Mbps, 00
|
||||||
|
|
||||||
|
// RFM73 needs this:
|
||||||
|
value |= RH_NRF24_LNA_HCURR;
|
||||||
|
|
||||||
|
spiWriteRegister(RH_NRF24_REG_06_RF_SETUP, value);
|
||||||
|
// If we were using auto-ack, we would have to set the appropriate timeout in reg 4 here
|
||||||
|
// see NRF24::setRF()
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RH_NRF24::setModeIdle()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeIdle)
|
||||||
|
{
|
||||||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration);
|
||||||
|
digitalWrite(_chipEnablePin, LOW);
|
||||||
|
_mode = RHModeIdle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::sleep()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeSleep)
|
||||||
|
{
|
||||||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, 0); // Power Down mode
|
||||||
|
digitalWrite(_chipEnablePin, LOW);
|
||||||
|
_mode = RHModeSleep;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // Already there?
|
||||||
|
}
|
||||||
|
|
||||||
|
void RH_NRF24::setModeRx()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeRx)
|
||||||
|
{
|
||||||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP | RH_NRF24_PRIM_RX);
|
||||||
|
digitalWrite(_chipEnablePin, HIGH);
|
||||||
|
_mode = RHModeRx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RH_NRF24::setModeTx()
|
||||||
|
{
|
||||||
|
if (_mode != RHModeTx)
|
||||||
|
{
|
||||||
|
// Its the CE rising edge that puts us into TX mode
|
||||||
|
// CE staying high makes us go to standby-II when the packet is sent
|
||||||
|
digitalWrite(_chipEnablePin, LOW);
|
||||||
|
// Ensure DS is not set
|
||||||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
|
||||||
|
spiWriteRegister(RH_NRF24_REG_00_CONFIG, _configuration | RH_NRF24_PWR_UP);
|
||||||
|
digitalWrite(_chipEnablePin, HIGH);
|
||||||
|
_mode = RHModeTx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::send(const uint8_t* data, uint8_t len)
|
||||||
|
{
|
||||||
|
if (len > RH_NRF24_MAX_MESSAGE_LEN)
|
||||||
|
return false;
|
||||||
|
// Set up the headers
|
||||||
|
_buf[0] = _txHeaderTo;
|
||||||
|
_buf[1] = _txHeaderFrom;
|
||||||
|
_buf[2] = _txHeaderId;
|
||||||
|
_buf[3] = _txHeaderFlags;
|
||||||
|
memcpy(_buf+RH_NRF24_HEADER_LEN, data, len);
|
||||||
|
spiBurstWrite(RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK, _buf, len + RH_NRF24_HEADER_LEN);
|
||||||
|
setModeTx();
|
||||||
|
// Radio will return to Standby II mode after transmission is complete
|
||||||
|
_txGood++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::waitPacketSent()
|
||||||
|
{
|
||||||
|
// If we are not currently in transmit mode, there is no packet to wait for
|
||||||
|
if (_mode != RHModeTx)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Wait for either the Data Sent or Max ReTries flag, signalling the
|
||||||
|
// end of transmission
|
||||||
|
// We dont actually use auto-ack, so prob dont expect to see RH_NRF24_MAX_RT
|
||||||
|
uint8_t status;
|
||||||
|
while (!((status = statusRead()) & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT)))
|
||||||
|
YIELD;
|
||||||
|
|
||||||
|
// Must clear RH_NRF24_MAX_RT if it is set, else no further comm
|
||||||
|
if (status & RH_NRF24_MAX_RT)
|
||||||
|
flushTx();
|
||||||
|
setModeIdle();
|
||||||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_TX_DS | RH_NRF24_MAX_RT);
|
||||||
|
// Return true if data sent, false if MAX_RT
|
||||||
|
return status & RH_NRF24_TX_DS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::isSending()
|
||||||
|
{
|
||||||
|
return !(spiReadRegister(RH_NRF24_REG_00_CONFIG) & RH_NRF24_PRIM_RX) &&
|
||||||
|
!(statusRead() & (RH_NRF24_TX_DS | RH_NRF24_MAX_RT));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::printRegisters()
|
||||||
|
{
|
||||||
|
#ifdef RH_HAVE_SERIAL
|
||||||
|
// Iterate over register range, but don't process registers not in use.
|
||||||
|
for (uint8_t r = RH_NRF24_REG_00_CONFIG; r <= RH_NRF24_REG_1D_FEATURE; r++)
|
||||||
|
{
|
||||||
|
if ((r <= RH_NRF24_REG_17_FIFO_STATUS) || (r >= RH_NRF24_REG_1C_DYNPD))
|
||||||
|
{
|
||||||
|
Serial.print(r, HEX);
|
||||||
|
Serial.print(": ");
|
||||||
|
uint8_t len = 1;
|
||||||
|
// Address registers are 5 bytes in size
|
||||||
|
if ( (RH_NRF24_REG_0A_RX_ADDR_P0 == r)
|
||||||
|
|| (RH_NRF24_REG_0B_RX_ADDR_P1 == r)
|
||||||
|
|| (RH_NRF24_REG_10_TX_ADDR == r) )
|
||||||
|
{
|
||||||
|
len = 5;
|
||||||
|
}
|
||||||
|
uint8_t buf[5];
|
||||||
|
spiBurstReadRegister(r, buf, len);
|
||||||
|
for (uint8_t j = 0; j < len; ++j)
|
||||||
|
{
|
||||||
|
Serial.print(buf[j], HEX);
|
||||||
|
Serial.print(" ");
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the latest received message is complete and uncorrupted
|
||||||
|
void RH_NRF24::validateRxBuf()
|
||||||
|
{
|
||||||
|
if (_bufLen < 4)
|
||||||
|
return; // Too short to be a real message
|
||||||
|
// Extract the 4 headers
|
||||||
|
_rxHeaderTo = _buf[0];
|
||||||
|
_rxHeaderFrom = _buf[1];
|
||||||
|
_rxHeaderId = _buf[2];
|
||||||
|
_rxHeaderFlags = _buf[3];
|
||||||
|
if (_promiscuous ||
|
||||||
|
_rxHeaderTo == _thisAddress ||
|
||||||
|
_rxHeaderTo == RH_BROADCAST_ADDRESS)
|
||||||
|
{
|
||||||
|
_rxGood++;
|
||||||
|
_rxBufValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::available()
|
||||||
|
{
|
||||||
|
if (!_rxBufValid)
|
||||||
|
{
|
||||||
|
if (_mode == RHModeTx)
|
||||||
|
return false;
|
||||||
|
setModeRx();
|
||||||
|
if (spiReadRegister(RH_NRF24_REG_17_FIFO_STATUS) & RH_NRF24_RX_EMPTY)
|
||||||
|
return false;
|
||||||
|
// Manual says that messages > 32 octets should be discarded
|
||||||
|
uint8_t len = spiRead(RH_NRF24_COMMAND_R_RX_PL_WID);
|
||||||
|
if (len > 32)
|
||||||
|
{
|
||||||
|
flushRx();
|
||||||
|
clearRxBuf();
|
||||||
|
setModeIdle();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Clear read interrupt
|
||||||
|
spiWriteRegister(RH_NRF24_REG_07_STATUS, RH_NRF24_RX_DR);
|
||||||
|
// Get the message into the RX buffer, so we can inspect the headers
|
||||||
|
spiBurstRead(RH_NRF24_COMMAND_R_RX_PAYLOAD, _buf, len);
|
||||||
|
_bufLen = len;
|
||||||
|
// 140 microsecs (32 octet payload)
|
||||||
|
validateRxBuf();
|
||||||
|
if (_rxBufValid)
|
||||||
|
setModeIdle(); // Got one
|
||||||
|
}
|
||||||
|
return _rxBufValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RH_NRF24::clearRxBuf()
|
||||||
|
{
|
||||||
|
_rxBufValid = false;
|
||||||
|
_bufLen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RH_NRF24::recv(uint8_t* buf, uint8_t* len)
|
||||||
|
{
|
||||||
|
if (!available())
|
||||||
|
return false;
|
||||||
|
if (buf && len)
|
||||||
|
{
|
||||||
|
// Skip the 4 headers that are at the beginning of the rxBuf
|
||||||
|
if (*len > _bufLen-RH_NRF24_HEADER_LEN)
|
||||||
|
*len = _bufLen-RH_NRF24_HEADER_LEN;
|
||||||
|
memcpy(buf, _buf+RH_NRF24_HEADER_LEN, *len);
|
||||||
|
}
|
||||||
|
clearRxBuf(); // This message accepted and cleared
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t RH_NRF24::maxMessageLength()
|
||||||
|
{
|
||||||
|
return RH_NRF24_MAX_MESSAGE_LEN;
|
||||||
|
}
|
|
@ -0,0 +1,639 @@
|
||||||
|
// RH_NRF24.h
|
||||||
|
// Author: Mike McCauley
|
||||||
|
// Copyright (C) 2012 Mike McCauley
|
||||||
|
// $Id: RH_NRF24.h,v 1.19 2016/07/07 00:02:53 mikem Exp mikem $
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef RH_NRF24_h
|
||||||
|
#define RH_NRF24_h
|
||||||
|
|
||||||
|
#include "RHGenericSPI.h"
|
||||||
|
#include "RHNRFSPIDriver.h"
|
||||||
|
|
||||||
|
// This is the maximum number of bytes that can be carried by the nRF24.
|
||||||
|
// We use some for headers, keeping fewer for RadioHead messages
|
||||||
|
#define RH_NRF24_MAX_PAYLOAD_LEN 32
|
||||||
|
|
||||||
|
// The length of the headers we add.
|
||||||
|
// The headers are inside the nRF24 payload
|
||||||
|
#define RH_NRF24_HEADER_LEN 4
|
||||||
|
|
||||||
|
// This is the maximum RadioHead user message length that can be supported by this library. Limited by
|
||||||
|
// the supported message lengths in the nRF24
|
||||||
|
#define RH_NRF24_MAX_MESSAGE_LEN (RH_NRF24_MAX_PAYLOAD_LEN-RH_NRF24_HEADER_LEN)
|
||||||
|
|
||||||
|
// SPI Command names
|
||||||
|
#define RH_NRF24_COMMAND_R_REGISTER 0x00
|
||||||
|
#define RH_NRF24_COMMAND_W_REGISTER 0x20
|
||||||
|
#define RH_NRF24_COMMAND_ACTIVATE 0x50 // only on RFM73 ?
|
||||||
|
#define RH_NRF24_COMMAND_R_RX_PAYLOAD 0x61
|
||||||
|
#define RH_NRF24_COMMAND_W_TX_PAYLOAD 0xa0
|
||||||
|
#define RH_NRF24_COMMAND_FLUSH_TX 0xe1
|
||||||
|
#define RH_NRF24_COMMAND_FLUSH_RX 0xe2
|
||||||
|
#define RH_NRF24_COMMAND_REUSE_TX_PL 0xe3
|
||||||
|
#define RH_NRF24_COMMAND_R_RX_PL_WID 0x60
|
||||||
|
#define RH_NRF24_COMMAND_W_ACK_PAYLOAD(pipe) (0xa8|(pipe&0x7))
|
||||||
|
#define RH_NRF24_COMMAND_W_TX_PAYLOAD_NOACK 0xb0
|
||||||
|
#define RH_NRF24_COMMAND_NOP 0xff
|
||||||
|
|
||||||
|
// Register names
|
||||||
|
#define RH_NRF24_REGISTER_MASK 0x1f
|
||||||
|
#define RH_NRF24_REG_00_CONFIG 0x00
|
||||||
|
#define RH_NRF24_REG_01_EN_AA 0x01
|
||||||
|
#define RH_NRF24_REG_02_EN_RXADDR 0x02
|
||||||
|
#define RH_NRF24_REG_03_SETUP_AW 0x03
|
||||||
|
#define RH_NRF24_REG_04_SETUP_RETR 0x04
|
||||||
|
#define RH_NRF24_REG_05_RF_CH 0x05
|
||||||
|
#define RH_NRF24_REG_06_RF_SETUP 0x06
|
||||||
|
#define RH_NRF24_REG_07_STATUS 0x07
|
||||||
|
#define RH_NRF24_REG_08_OBSERVE_TX 0x08
|
||||||
|
#define RH_NRF24_REG_09_RPD 0x09
|
||||||
|
#define RH_NRF24_REG_0A_RX_ADDR_P0 0x0a
|
||||||
|
#define RH_NRF24_REG_0B_RX_ADDR_P1 0x0b
|
||||||
|
#define RH_NRF24_REG_0C_RX_ADDR_P2 0x0c
|
||||||
|
#define RH_NRF24_REG_0D_RX_ADDR_P3 0x0d
|
||||||
|
#define RH_NRF24_REG_0E_RX_ADDR_P4 0x0e
|
||||||
|
#define RH_NRF24_REG_0F_RX_ADDR_P5 0x0f
|
||||||
|
#define RH_NRF24_REG_10_TX_ADDR 0x10
|
||||||
|
#define RH_NRF24_REG_11_RX_PW_P0 0x11
|
||||||
|
#define RH_NRF24_REG_12_RX_PW_P1 0x12
|
||||||
|
#define RH_NRF24_REG_13_RX_PW_P2 0x13
|
||||||
|
#define RH_NRF24_REG_14_RX_PW_P3 0x14
|
||||||
|
#define RH_NRF24_REG_15_RX_PW_P4 0x15
|
||||||
|
#define RH_NRF24_REG_16_RX_PW_P5 0x16
|
||||||
|
#define RH_NRF24_REG_17_FIFO_STATUS 0x17
|
||||||
|
#define RH_NRF24_REG_1C_DYNPD 0x1c
|
||||||
|
#define RH_NRF24_REG_1D_FEATURE 0x1d
|
||||||
|
|
||||||
|
// These register masks etc are named wherever possible
|
||||||
|
// corresponding to the bit and field names in the nRF24L01 Product Specification
|
||||||
|
// #define RH_NRF24_REG_00_CONFIG 0x00
|
||||||
|
#define RH_NRF24_MASK_RX_DR 0x40
|
||||||
|
#define RH_NRF24_MASK_TX_DS 0x20
|
||||||
|
#define RH_NRF24_MASK_MAX_RT 0x10
|
||||||
|
#define RH_NRF24_EN_CRC 0x08
|
||||||
|
#define RH_NRF24_CRCO 0x04
|
||||||
|
#define RH_NRF24_PWR_UP 0x02
|
||||||
|
#define RH_NRF24_PRIM_RX 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_01_EN_AA 0x01
|
||||||
|
#define RH_NRF24_ENAA_P5 0x20
|
||||||
|
#define RH_NRF24_ENAA_P4 0x10
|
||||||
|
#define RH_NRF24_ENAA_P3 0x08
|
||||||
|
#define RH_NRF24_ENAA_P2 0x04
|
||||||
|
#define RH_NRF24_ENAA_P1 0x02
|
||||||
|
#define RH_NRF24_ENAA_P0 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_02_EN_RXADDR 0x02
|
||||||
|
#define RH_NRF24_ERX_P5 0x20
|
||||||
|
#define RH_NRF24_ERX_P4 0x10
|
||||||
|
#define RH_NRF24_ERX_P3 0x08
|
||||||
|
#define RH_NRF24_ERX_P2 0x04
|
||||||
|
#define RH_NRF24_ERX_P1 0x02
|
||||||
|
#define RH_NRF24_ERX_P0 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_03_SETUP_AW 0x03
|
||||||
|
#define RH_NRF24_AW_3_BYTES 0x01
|
||||||
|
#define RH_NRF24_AW_4_BYTES 0x02
|
||||||
|
#define RH_NRF24_AW_5_BYTES 0x03
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_04_SETUP_RETR 0x04
|
||||||
|
#define RH_NRF24_ARD 0xf0
|
||||||
|
#define RH_NRF24_ARC 0x0f
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_05_RF_CH 0x05
|
||||||
|
#define RH_NRF24_RF_CH 0x7f
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_06_RF_SETUP 0x06
|
||||||
|
#define RH_NRF24_CONT_WAVE 0x80
|
||||||
|
#define RH_NRF24_RF_DR_LOW 0x20
|
||||||
|
#define RH_NRF24_PLL_LOCK 0x10
|
||||||
|
#define RH_NRF24_RF_DR_HIGH 0x08
|
||||||
|
#define RH_NRF24_PWR 0x06
|
||||||
|
#define RH_NRF24_PWR_m18dBm 0x00
|
||||||
|
#define RH_NRF24_PWR_m12dBm 0x02
|
||||||
|
#define RH_NRF24_PWR_m6dBm 0x04
|
||||||
|
#define RH_NRF24_PWR_0dBm 0x06
|
||||||
|
#define RH_NRF24_LNA_HCURR 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_07_STATUS 0x07
|
||||||
|
#define RH_NRF24_RX_DR 0x40
|
||||||
|
#define RH_NRF24_TX_DS 0x20
|
||||||
|
#define RH_NRF24_MAX_RT 0x10
|
||||||
|
#define RH_NRF24_RX_P_NO 0x0e
|
||||||
|
#define RH_NRF24_STATUS_TX_FULL 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_08_OBSERVE_TX 0x08
|
||||||
|
#define RH_NRF24_PLOS_CNT 0xf0
|
||||||
|
#define RH_NRF24_ARC_CNT 0x0f
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_09_RPD 0x09
|
||||||
|
#define RH_NRF24_RPD 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_17_FIFO_STATUS 0x17
|
||||||
|
#define RH_NRF24_TX_REUSE 0x40
|
||||||
|
#define RH_NRF24_TX_FULL 0x20
|
||||||
|
#define RH_NRF24_TX_EMPTY 0x10
|
||||||
|
#define RH_NRF24_RX_FULL 0x02
|
||||||
|
#define RH_NRF24_RX_EMPTY 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_1C_DYNPD 0x1c
|
||||||
|
#define RH_NRF24_DPL_ALL 0x3f
|
||||||
|
#define RH_NRF24_DPL_P5 0x20
|
||||||
|
#define RH_NRF24_DPL_P4 0x10
|
||||||
|
#define RH_NRF24_DPL_P3 0x08
|
||||||
|
#define RH_NRF24_DPL_P2 0x04
|
||||||
|
#define RH_NRF24_DPL_P1 0x02
|
||||||
|
#define RH_NRF24_DPL_P0 0x01
|
||||||
|
|
||||||
|
// #define RH_NRF24_REG_1D_FEATURE 0x1d
|
||||||
|
#define RH_NRF24_EN_DPL 0x04
|
||||||
|
#define RH_NRF24_EN_ACK_PAY 0x02
|
||||||
|
#define RH_NRF24_EN_DYN_ACK 0x01
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
/// \class RH_NRF24 RH_NRF24.h <RH_NRF24.h>
|
||||||
|
/// \brief Send and receive addressed, reliable, acknowledged datagrams by nRF24L01 and compatible transceivers.
|
||||||
|
///
|
||||||
|
/// Supported transceivers include:
|
||||||
|
/// - Nordic nRF24 based 2.4GHz radio modules, such as nRF24L01 http://www.nordicsemi.com/eng/Products/2.4GHz-RF/nRF24L01
|
||||||
|
/// and other compatible transceivers.
|
||||||
|
/// - nRF24L01p with PA and LNA modules that produce a higher power output similar to this one:
|
||||||
|
/// http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA
|
||||||
|
/// - Sparkfun WRL-00691 module with nRF24L01 https://www.sparkfun.com/products/691
|
||||||
|
/// or WRL-00705 https://www.sparkfun.com/products/705 etc.
|
||||||
|
/// - Hope-RF RFM73 http://www.hoperf.com/rf/2.4g_module/RFM73.htm and
|
||||||
|
/// http://www.anarduino.com/details.jsp?pid=121
|
||||||
|
/// and compatible devices (such as BK2423). nRF24L01 and RFM73 can interoperate
|
||||||
|
/// with each other.
|
||||||
|
///
|
||||||
|
/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams
|
||||||
|
/// of arbitrary length to 28 octets per packet. Use one of the Manager classes to get addressing and
|
||||||
|
/// acknowledgement reliability, routing, meshes etc.
|
||||||
|
///
|
||||||
|
/// The nRF24L01 (http://www.sparkfun.com/datasheets/Wireless/Nordic/nRF24L01P_Product_Specification_1_0.pdf)
|
||||||
|
/// is a low-cost 2.4GHz ISM transceiver module. It supports a number of channel frequencies in the 2.4GHz band
|
||||||
|
/// and a range of data rates.
|
||||||
|
///
|
||||||
|
/// This library provides functions for sending and receiving messages of up to 28 octets on any
|
||||||
|
/// frequency supported by the nRF24L01, at a selected data rate.
|
||||||
|
///
|
||||||
|
/// Several nRF24L01 modules can be connected to an Arduino, permitting the construction of translators
|
||||||
|
/// and frequency changers, etc.
|
||||||
|
///
|
||||||
|
/// The nRF24 transceiver is configured to use Enhanced Shockburst with no acknowledgement and no retransmits.
|
||||||
|
/// TX_ADDR and RX_ADDR_P0 are set to the network address. If you need the low level auto-acknowledgement
|
||||||
|
/// feature supported by this chip, you can use our original NRF24 library
|
||||||
|
/// at http://www.airspayce.com/mikem/arduino/NRF24
|
||||||
|
///
|
||||||
|
/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and
|
||||||
|
/// data rate, and with identical network addresses.
|
||||||
|
///
|
||||||
|
/// Example Arduino programs are included to show the main modes of use.
|
||||||
|
///
|
||||||
|
/// \par Packet Format
|
||||||
|
///
|
||||||
|
/// All messages sent and received by this class conform to this packet format, as specified by
|
||||||
|
/// the nRF24L01 product specification:
|
||||||
|
///
|
||||||
|
/// - 1 octets PREAMBLE
|
||||||
|
/// - 3 to 5 octets NETWORK ADDRESS
|
||||||
|
/// - 9 bits packet control field
|
||||||
|
/// - 0 to 32 octets PAYLOAD, consisting of:
|
||||||
|
/// - 1 octet TO header
|
||||||
|
/// - 1 octet FROM header
|
||||||
|
/// - 1 octet ID header
|
||||||
|
/// - 1 octet FLAGS header
|
||||||
|
/// - 0 to 28 octets of user message
|
||||||
|
/// - 2 octets CRC
|
||||||
|
///
|
||||||
|
/// \par Connecting nRF24L01 to Arduino
|
||||||
|
///
|
||||||
|
/// The electrical connection between the nRF24L01 and the Arduino require 3.3V, the 3 x SPI pins (SCK, SDI, SDO),
|
||||||
|
/// a Chip Enable pin and a Slave Select pin.
|
||||||
|
/// If you are using the Sparkfun WRL-00691 module, it has a voltage regulator on board and
|
||||||
|
/// can be should with 5V VCC if possible.
|
||||||
|
/// The examples below assume the Sparkfun WRL-00691 module
|
||||||
|
///
|
||||||
|
/// Connect the nRF24L01 to most Arduino's like this (Caution, Arduino Mega has different pins for SPI,
|
||||||
|
/// see below). Use these same connections for Teensy 3.1 (use 3.3V not 5V Vcc).
|
||||||
|
/// \code
|
||||||
|
/// Arduino Sparkfun WRL-00691
|
||||||
|
/// 5V-----------VCC (3.3V to 7V in)
|
||||||
|
/// pin D8-----------CE (chip enable in)
|
||||||
|
/// SS pin D10----------CSN (chip select in)
|
||||||
|
/// SCK pin D13----------SCK (SPI clock in)
|
||||||
|
/// MOSI pin D11----------SDI (SPI Data in)
|
||||||
|
/// MISO pin D12----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// For an Arduino Leonardo (the SPI pins do not come out on the Digital pins as for normal Arduino, but only
|
||||||
|
/// appear on the ICSP header)
|
||||||
|
/// \code
|
||||||
|
/// Leonardo Sparkfun WRL-00691
|
||||||
|
/// 5V-----------VCC (3.3V to 7V in)
|
||||||
|
/// pin D8-----------CE (chip enable in)
|
||||||
|
/// SS pin D10----------CSN (chip select in)
|
||||||
|
/// SCK ICSP pin 3----------SCK (SPI clock in)
|
||||||
|
/// MOSI ICSP pin 4----------SDI (SPI Data in)
|
||||||
|
/// MISO ICSP pin 1----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
/// and initialise the NRF24 object like this to explicitly set the SS pin
|
||||||
|
/// NRF24 nrf24(8, 10);
|
||||||
|
///
|
||||||
|
/// For an Arduino Due (the SPI pins do not come out on the Digital pins as for normal Arduino, but only
|
||||||
|
/// appear on the SPI header). Use the same connections for Yun with 5V or 3.3V.
|
||||||
|
/// \code
|
||||||
|
/// Due Sparkfun WRL-00691
|
||||||
|
/// 3.3V-----------VCC (3.3V to 7V in)
|
||||||
|
/// pin D8-----------CE (chip enable in)
|
||||||
|
/// SS pin D10----------CSN (chip select in)
|
||||||
|
/// SCK SPI pin 3----------SCK (SPI clock in)
|
||||||
|
/// MOSI SPI pin 4----------SDI (SPI Data in)
|
||||||
|
/// MISO SPI pin 1----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
/// and initialise the NRF24 object with the default constructor
|
||||||
|
/// NRF24 nrf24;
|
||||||
|
///
|
||||||
|
/// For an Arduino Mega:
|
||||||
|
/// \code
|
||||||
|
/// Mega Sparkfun WRL-00691
|
||||||
|
/// 5V-----------VCC (3.3V to 7V in)
|
||||||
|
/// pin D8-----------CE (chip enable in)
|
||||||
|
/// SS pin D53----------CSN (chip select in)
|
||||||
|
/// SCK pin D52----------SCK (SPI clock in)
|
||||||
|
/// MOSI pin D51----------SDI (SPI Data in)
|
||||||
|
/// MISO pin D50----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
/// and you can then use the constructor RH_NRF24(8, 53).
|
||||||
|
///
|
||||||
|
/// For an Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html, connected by hardware SPI to the
|
||||||
|
/// ITDB02 Parallel LCD Module Interface pins:
|
||||||
|
/// \code
|
||||||
|
/// IBoard Signal=ITDB02 pin Sparkfun WRL-00691
|
||||||
|
/// 3.3V 37-----------VCC (3.3V to 7V in)
|
||||||
|
/// D2 28-----------CE (chip enable in)
|
||||||
|
/// D29 27----------CSN (chip select in)
|
||||||
|
/// SCK D52 32----------SCK (SPI clock in)
|
||||||
|
/// MOSI D51 34----------SDI (SPI Data in)
|
||||||
|
/// MISO D50 30----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND 39----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
/// And initialise like this:
|
||||||
|
/// \code
|
||||||
|
/// RH_NRF24 nrf24(2, 29);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// For an Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html, connected by software SPI to the
|
||||||
|
/// nRF24L01+ Module Interface pins. CAUTION: performance of software SPI is very slow and is not
|
||||||
|
/// compatible with other modules running hardware SPI.
|
||||||
|
/// \code
|
||||||
|
/// IBoard Signal=Module pin Sparkfun WRL-00691
|
||||||
|
/// 3.3V 2----------VCC (3.3V to 7V in)
|
||||||
|
/// D12 3-----------CE (chip enable in)
|
||||||
|
/// D29 4----------CSN (chip select in)
|
||||||
|
/// D9 5----------SCK (SPI clock in)
|
||||||
|
/// D8 6----------SDI (SPI Data in)
|
||||||
|
/// D7 7----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND 1----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
/// And initialise like this:
|
||||||
|
/// \code
|
||||||
|
/// #include <SPI.h>
|
||||||
|
/// #include <RH_NRF24.h>
|
||||||
|
/// #include <RHSoftwareSPI.h>
|
||||||
|
/// Singleton instance of the radio driver
|
||||||
|
/// RHSoftwareSPI spi;
|
||||||
|
/// RH_NRF24 nrf24(12, 11, spi);
|
||||||
|
/// void setup() {
|
||||||
|
/// spi.setPins(7, 8, 9);
|
||||||
|
/// ....
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// For Raspberry Pi with Sparkfun WRL-00691
|
||||||
|
/// \code
|
||||||
|
/// Raspberry Pi P1 pin Sparkfun WRL-00691
|
||||||
|
/// 5V 2-----------VCC (3.3V to 7V in)
|
||||||
|
/// GPIO25 22-----------CE (chip enable in)
|
||||||
|
/// GPIO8 24----------CSN (chip select in)
|
||||||
|
/// GPIO11 23----------SCK (SPI clock in)
|
||||||
|
/// GPIO10 19----------SDI (SPI Data in)
|
||||||
|
/// GPIO9 21----------SDO (SPI data out)
|
||||||
|
/// IRQ (Interrupt output, not connected)
|
||||||
|
/// GND 6----------GND (ground in)
|
||||||
|
/// \endcode
|
||||||
|
/// and initialise like this:
|
||||||
|
/// \code
|
||||||
|
/// RH_NRF24 nrf24(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24);
|
||||||
|
/// \endcode
|
||||||
|
/// See the example program and Makefile in examples/raspi. Requires bcm2835 library to be previously installed.
|
||||||
|
/// \code
|
||||||
|
/// cd examples/raspi
|
||||||
|
/// make
|
||||||
|
/// sudo ./RasPiRH
|
||||||
|
/// \endcode
|
||||||
|
/// \code
|
||||||
|
///
|
||||||
|
/// You can override the default settings for the CSN and CE pins
|
||||||
|
/// in the NRF24() constructor if you wish to connect the slave select CSN to other than the normal one for your
|
||||||
|
/// Arduino (D10 for Diecimila, Uno etc and D53 for Mega)
|
||||||
|
///
|
||||||
|
/// Caution: on some Arduinos such as the Mega 2560, if you set the slave select pin to be other than the usual SS
|
||||||
|
/// pin (D53 on Mega 2560), you may need to set the usual SS pin to be an output to force the Arduino into SPI
|
||||||
|
/// master mode.
|
||||||
|
///
|
||||||
|
/// Caution: this module has not been proved to work with Leonardo, at least without level
|
||||||
|
/// shifters between the nRF24 and the Leonardo. Tests seem to indicate that such level shifters would be required
|
||||||
|
/// with Leonardo to make it work.
|
||||||
|
///
|
||||||
|
/// It is possible to have 2 radios conected to one arduino, provided each radio has its own
|
||||||
|
/// CSN and CE line (SCK, SDI and SDO are common to both radios)
|
||||||
|
///
|
||||||
|
/// \par SPI Interface
|
||||||
|
///
|
||||||
|
/// You can interface to nRF24L01 with with hardware or software SPI. Use of software SPI with the RHSoftwareSPI
|
||||||
|
/// class depends on a fast enough processor and digitalOut() functions to achieve a high enough SPI bus frequency.
|
||||||
|
/// If you observe reliable behaviour with the default hardware SPI RHHardwareSPI, but unreliable behaviour
|
||||||
|
/// with Software SPI RHSoftwareSPI, it may be due to slow CPU performance.
|
||||||
|
///
|
||||||
|
/// Initialisation example with hardware SPI
|
||||||
|
/// \code
|
||||||
|
/// #include <RH_NRF24.h>
|
||||||
|
/// RH_NRF24 driver;
|
||||||
|
/// RHReliableDatagram manager(driver, CLIENT_ADDRESS);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// Initialisation example with software SPI
|
||||||
|
/// \code
|
||||||
|
/// #include <RH_NRF24.h>
|
||||||
|
/// #include <RHSoftwareSPI.h>
|
||||||
|
/// RHSoftwareSPI spi;
|
||||||
|
/// RH_NRF24 driver(8, 10, spi);
|
||||||
|
/// RHReliableDatagram manager(driver, CLIENT_ADDRESS);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// \par Example programs
|
||||||
|
///
|
||||||
|
/// Several example programs are provided.
|
||||||
|
///
|
||||||
|
/// \par Radio Performance
|
||||||
|
///
|
||||||
|
/// Frequency accuracy may be debatable. For nominal frequency of 2401.000 MHz (ie channel 1),
|
||||||
|
/// my Yaesu VR-5000 receiver indicated the center frequency for my test radios
|
||||||
|
/// was 2401.121 MHz. Its not clear to me if the Yaesu
|
||||||
|
/// is the source of the error, but I tend to believe it, which would make the nRF24l01 frequency out by 121kHz.
|
||||||
|
///
|
||||||
|
/// The measured power output for a nRF24L01p with PA and LNA set to 0dBm output is about 18dBm.
|
||||||
|
///
|
||||||
|
/// \par Radio operating strategy and defaults
|
||||||
|
///
|
||||||
|
/// The radio is enabled all the time, and switched between TX and RX modes depending on
|
||||||
|
/// whether there is any data to send. Sending data sets the radio to TX mode.
|
||||||
|
/// After data is sent, the radio automatically returns to Standby II mode. Calling waitAvailable() or
|
||||||
|
/// waitAvailableTimeout() starts the radio in RX mode.
|
||||||
|
///
|
||||||
|
/// The radio is configured by default to Channel 2, 2Mbps, 0dBm power, 5 bytes address, payload width 1, CRC enabled
|
||||||
|
/// 2 byte CRC, No Auto-Ack mode. Enhanced shockburst is used.
|
||||||
|
/// TX and P0 are set to the Network address. Node addresses and decoding are handled with the RH_NRF24 module.
|
||||||
|
///
|
||||||
|
/// \par Memory
|
||||||
|
///
|
||||||
|
/// Memory usage of this class is minimal. The compiled client and server sketches are about 6000 bytes on Arduino.
|
||||||
|
/// The reliable client and server sketches compile to about 8500 bytes on Arduino.
|
||||||
|
/// RAM requirements are minimal.
|
||||||
|
///
|
||||||
|
class RH_NRF24 : public RHNRFSPIDriver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \brief Defines convenient values for setting data rates in setRF()
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
DataRate1Mbps = 0, ///< 1 Mbps
|
||||||
|
DataRate2Mbps, ///< 2 Mbps
|
||||||
|
DataRate250kbps ///< 250 kbps
|
||||||
|
} DataRate;
|
||||||
|
|
||||||
|
/// \brief Convenient values for setting transmitter power in setRF()
|
||||||
|
/// These are designed to agree with the values for RF_PWR in RH_NRF24_REG_06_RF_SETUP
|
||||||
|
/// To be passed to setRF();
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
// Add 20dBm for nRF24L01p with PA and LNA modules
|
||||||
|
TransmitPowerm18dBm = 0, ///< On nRF24, -18 dBm
|
||||||
|
TransmitPowerm12dBm, ///< On nRF24, -12 dBm
|
||||||
|
TransmitPowerm6dBm, ///< On nRF24, -6 dBm
|
||||||
|
TransmitPower0dBm, ///< On nRF24, 0 dBm
|
||||||
|
// Sigh, different power levels for the same bit patterns on RFM73:
|
||||||
|
// On RFM73P-S, there is a Tx power amp, so expect higher power levels, up to 20dBm. Alas
|
||||||
|
// there is no clear documentation on the power for different settings :-(
|
||||||
|
RFM73TransmitPowerm10dBm = 0, ///< On RFM73, -10 dBm
|
||||||
|
RFM73TransmitPowerm5dBm, ///< On RFM73, -5 dBm
|
||||||
|
RFM73TransmitPowerm0dBm, ///< On RFM73, 0 dBm
|
||||||
|
RFM73TransmitPower5dBm ///< On RFM73, 5 dBm. 20dBm on RFM73P-S2 ?
|
||||||
|
|
||||||
|
} TransmitPower;
|
||||||
|
|
||||||
|
/// Constructor. You can have multiple instances, but each instance must have its own
|
||||||
|
/// chip enable and slave select pin.
|
||||||
|
/// After constructing, you must call init() to initialise the interface
|
||||||
|
/// and the radio module
|
||||||
|
/// \param[in] chipEnablePin the Arduino pin to use to enable the chip for transmit/receive
|
||||||
|
/// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the NRF24 before
|
||||||
|
/// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega,
|
||||||
|
/// D10 for Maple)
|
||||||
|
/// \param[in] spi Pointer to the SPI interface object to use.
|
||||||
|
/// Defaults to the standard Arduino hardware SPI interface
|
||||||
|
RH_NRF24(uint8_t chipEnablePin = 8, uint8_t slaveSelectPin = SS, RHGenericSPI& spi = hardware_spi);
|
||||||
|
|
||||||
|
/// Initialises this instance and the radio module connected to it.
|
||||||
|
/// The following steps are taken:g
|
||||||
|
/// - Set the chip enable and chip select pins to output LOW, HIGH respectively.
|
||||||
|
/// - Initialise the SPI output pins
|
||||||
|
/// - Initialise the SPI interface library to 8MHz (Hint, if you want to lower
|
||||||
|
/// the SPI frequency (perhaps where you have other SPI shields, low voltages etc),
|
||||||
|
/// call SPI.setClockDivider() after init()).
|
||||||
|
/// -Flush the receiver and transmitter buffers
|
||||||
|
/// - Set the radio to receive with powerUpRx();
|
||||||
|
/// \return true if everything was successful
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
/// Reads a single register from the NRF24
|
||||||
|
/// \param[in] reg Register number, one of RH_NRF24_REG_*
|
||||||
|
/// \return The value of the register
|
||||||
|
uint8_t spiReadRegister(uint8_t reg);
|
||||||
|
|
||||||
|
/// Writes a single byte to the NRF24, and at the same time reads the current STATUS register
|
||||||
|
/// \param[in] reg Register number, one of RH_NRF24_REG_*
|
||||||
|
/// \param[in] val The value to write
|
||||||
|
/// \return the current STATUS (read while the command is sent)
|
||||||
|
uint8_t spiWriteRegister(uint8_t reg, uint8_t val);
|
||||||
|
|
||||||
|
/// Reads a number of consecutive registers from the NRF24 using burst read mode
|
||||||
|
/// \param[in] reg Register number of the first register, one of RH_NRF24_REG_*
|
||||||
|
/// \param[in] dest Array to write the register values to. Must be at least len bytes
|
||||||
|
/// \param[in] len Number of bytes to read
|
||||||
|
/// \return the current STATUS (read while the command is sent)
|
||||||
|
uint8_t spiBurstReadRegister(uint8_t reg, uint8_t* dest, uint8_t len);
|
||||||
|
|
||||||
|
/// Write a number of consecutive registers using burst write mode
|
||||||
|
/// \param[in] reg Register number of the first register, one of RH_NRF24_REG_*
|
||||||
|
/// \param[in] src Array of new register values to write. Must be at least len bytes
|
||||||
|
/// \param[in] len Number of bytes to write
|
||||||
|
/// \return the current STATUS (read while the command is sent)
|
||||||
|
uint8_t spiBurstWriteRegister(uint8_t reg, uint8_t* src, uint8_t len);
|
||||||
|
|
||||||
|
/// Reads and returns the device status register NRF24_REG_02_DEVICE_STATUS
|
||||||
|
/// \return The value of the device status register
|
||||||
|
uint8_t statusRead();
|
||||||
|
|
||||||
|
/// Sets the transmit and receive channel number.
|
||||||
|
/// The frequency used is (2400 + channel) MHz
|
||||||
|
/// \return true on success
|
||||||
|
bool setChannel(uint8_t channel);
|
||||||
|
|
||||||
|
/// Sets the chip configuration that will be used to set
|
||||||
|
/// the NRF24 NRF24_REG_00_CONFIG register when in Idle mode. This allows you to change some
|
||||||
|
/// chip configuration for compatibility with libraries other than this one.
|
||||||
|
/// You should not normally need to call this.
|
||||||
|
/// Defaults to NRF24_EN_CRC| RH_NRF24_CRCO, which is the standard configuration for this library
|
||||||
|
/// (2 byte CRC enabled).
|
||||||
|
/// \param[in] mode The chip configuration to be used whe in Idle mode.
|
||||||
|
/// \return true on success
|
||||||
|
bool setOpMode(uint8_t mode);
|
||||||
|
|
||||||
|
/// Sets the Network address.
|
||||||
|
/// Only nodes with the same network address can communicate with each other. You
|
||||||
|
/// can set different network addresses in different sets of nodes to isolate them from each other.
|
||||||
|
/// Internally, this sets the nRF24 TX_ADDR and RX_ADDR_P0 to be the given network address.
|
||||||
|
/// The default network address is 0xE7E7E7E7E7
|
||||||
|
/// \param[in] address The new network address. Must match the network address of any receiving node(s).
|
||||||
|
/// \param[in] len Number of bytes of address to set (3 to 5).
|
||||||
|
/// \return true on success, false if len is not in the range 3-5 inclusive.
|
||||||
|
bool setNetworkAddress(uint8_t* address, uint8_t len);
|
||||||
|
|
||||||
|
/// Sets the data rate and transmitter power to use. Note that the nRF24 and the RFM73 have different
|
||||||
|
/// available power levels, and for convenience, 2 different sets of values are available in the
|
||||||
|
/// RH_NRF24::TransmitPower enum. The ones with the RFM73 only have meaning on the RFM73 and compatible
|
||||||
|
/// devces. The others are for the nRF24.
|
||||||
|
/// \param [in] data_rate The data rate to use for all packets transmitted and received. One of RH_NRF24::DataRate.
|
||||||
|
/// \param [in] power Transmitter power. One of RH_NRF24::TransmitPower.
|
||||||
|
/// \return true on success
|
||||||
|
bool setRF(DataRate data_rate, TransmitPower power);
|
||||||
|
|
||||||
|
/// Sets the radio in power down mode, with the configuration set to the
|
||||||
|
/// last value from setOpMode().
|
||||||
|
/// Sets chip enable to LOW.
|
||||||
|
void setModeIdle();
|
||||||
|
|
||||||
|
/// Sets the radio in RX mode.
|
||||||
|
/// Sets chip enable to HIGH to enable the chip in RX mode.
|
||||||
|
void setModeRx();
|
||||||
|
|
||||||
|
/// Sets the radio in TX mode.
|
||||||
|
/// Pulses the chip enable LOW then HIGH to enable the chip in TX mode.
|
||||||
|
void setModeTx();
|
||||||
|
|
||||||
|
/// Sends data to the address set by setTransmitAddress()
|
||||||
|
/// Sets the radio to TX mode
|
||||||
|
/// \param [in] data Data bytes to send.
|
||||||
|
/// \param [in] len Number of data bytes to send
|
||||||
|
/// \return true on success (which does not necessarily mean the receiver got the message, only that the message was
|
||||||
|
/// successfully transmitted).
|
||||||
|
bool send(const uint8_t* data, uint8_t len);
|
||||||
|
|
||||||
|
/// Blocks until the current message (if any)
|
||||||
|
/// has been transmitted
|
||||||
|
/// \return true on success, false if the chip is not in transmit mode or other transmit failure
|
||||||
|
virtual bool waitPacketSent();
|
||||||
|
|
||||||
|
/// Indicates if the chip is in transmit mode and
|
||||||
|
/// there is a packet currently being transmitted
|
||||||
|
/// \return true if the chip is in transmit mode and there is a transmission in progress
|
||||||
|
bool isSending();
|
||||||
|
|
||||||
|
/// Prints the value of all chip registers
|
||||||
|
/// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
|
||||||
|
/// For debugging purposes only.
|
||||||
|
/// \return true on success
|
||||||
|
bool printRegisters();
|
||||||
|
|
||||||
|
/// Checks whether a received message is available.
|
||||||
|
/// This can be called multiple times in a timeout loop
|
||||||
|
/// \return true if a complete, valid message has been received and is able to be retrieved by
|
||||||
|
/// recv()
|
||||||
|
bool available();
|
||||||
|
|
||||||
|
/// Turns the receiver on if it not already on.
|
||||||
|
/// If there is a valid message available, copy it to buf and return true
|
||||||
|
/// else return false.
|
||||||
|
/// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted).
|
||||||
|
/// You should be sure to call this function frequently enough to not miss any messages
|
||||||
|
/// It is recommended that you call it in your main loop.
|
||||||
|
/// \param[in] buf Location to copy the received message
|
||||||
|
/// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied.
|
||||||
|
/// \return true if a valid message was copied to buf
|
||||||
|
bool recv(uint8_t* buf, uint8_t* len);
|
||||||
|
|
||||||
|
/// The maximum message length supported by this driver
|
||||||
|
/// \return The maximum message length supported by this driver
|
||||||
|
uint8_t maxMessageLength();
|
||||||
|
|
||||||
|
/// Sets the radio into Power Down mode.
|
||||||
|
/// If successful, the radio will stay in Power Down mode until woken by
|
||||||
|
/// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc)
|
||||||
|
/// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode.
|
||||||
|
/// \return true if sleep mode was successfully entered.
|
||||||
|
virtual bool sleep();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Flush the TX FIFOs
|
||||||
|
/// \return the value of the device status register
|
||||||
|
uint8_t flushTx();
|
||||||
|
|
||||||
|
/// Flush the RX FIFOs
|
||||||
|
/// \return the value of the device status register
|
||||||
|
uint8_t flushRx();
|
||||||
|
|
||||||
|
/// Examine the receive buffer to determine whether the message is for this node
|
||||||
|
void validateRxBuf();
|
||||||
|
|
||||||
|
/// Clear our local receive buffer
|
||||||
|
void clearRxBuf();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// This idle mode chip configuration
|
||||||
|
uint8_t _configuration;
|
||||||
|
|
||||||
|
/// the number of the chip enable pin
|
||||||
|
uint8_t _chipEnablePin;
|
||||||
|
|
||||||
|
/// Number of octets in the buffer
|
||||||
|
uint8_t _bufLen;
|
||||||
|
|
||||||
|
/// The receiver/transmitter buffer
|
||||||
|
uint8_t _buf[RH_NRF24_MAX_PAYLOAD_LEN];
|
||||||
|
|
||||||
|
/// True when there is a valid message in the buffer
|
||||||
|
bool _rxBufValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @example nrf24_client.pde
|
||||||
|
/// @example nrf24_server.pde
|
||||||
|
/// @example nrf24_reliable_datagram_client.pde
|
||||||
|
/// @example nrf24_reliable_datagram_server.pde
|
||||||
|
/// @example RasPiRH.cpp
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,975 @@
|
||||||
|
// RadioHead.h
|
||||||
|
// Author: Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY
|
||||||
|
// Copyright (C) 2014 Mike McCauley
|
||||||
|
// $Id: RadioHead.h,v 1.56 2016/07/07 00:02:53 mikem Exp mikem $
|
||||||
|
|
||||||
|
/// \mainpage RadioHead Packet Radio library for embedded microprocessors
|
||||||
|
///
|
||||||
|
/// This is the RadioHead Packet Radio library for embedded microprocessors.
|
||||||
|
/// It provides a complete object-oriented library for sending and receiving packetized messages
|
||||||
|
/// via a variety of common data radios and other transports on a range of embedded microprocessors.
|
||||||
|
///
|
||||||
|
/// The version of the package that this documentation refers to can be downloaded
|
||||||
|
/// from http://www.airspayce.com/mikem/arduino/RadioHead/RadioHead-1.61.zip
|
||||||
|
/// You can find the latest version at http://www.airspayce.com/mikem/arduino/RadioHead
|
||||||
|
///
|
||||||
|
/// You can also find online help and discussion at
|
||||||
|
/// http://groups.google.com/group/radiohead-arduino
|
||||||
|
/// Please use that group for all questions and discussions on this topic.
|
||||||
|
/// Do not contact the author directly, unless it is to discuss commercial licensing.
|
||||||
|
/// Before asking a question or reporting a bug, please read
|
||||||
|
/// - http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question
|
||||||
|
/// - http://www.catb.org/esr/faqs/smart-questions.html
|
||||||
|
/// - http://www.chiark.greenend.org.uk/~shgtatham/bugs.html
|
||||||
|
///
|
||||||
|
/// \par Overview
|
||||||
|
///
|
||||||
|
/// RadioHead consists of 2 main sets of classes: Drivers and Managers.
|
||||||
|
///
|
||||||
|
/// - Drivers provide low level access to a range of different packet radios and other packetized message transports.
|
||||||
|
/// - Managers provide high level message sending and receiving facilities for a range of different requirements.
|
||||||
|
///
|
||||||
|
/// Every RadioHead program will have an instance of a Driver to provide access to the data radio or transport,
|
||||||
|
/// and a Manager that uses that driver to send and receive messages for the application. The programmer is required
|
||||||
|
/// to instantiate a Driver and a Manager, and to initialise the Manager. Thereafter the facilities of the Manager
|
||||||
|
/// can be used to send and receive messages.
|
||||||
|
///
|
||||||
|
/// It is also possible to use a Driver on its own, without a Manager, although this only allows unaddressed,
|
||||||
|
/// unreliable transport via the Driver's facilities.
|
||||||
|
///
|
||||||
|
/// In some specialised use cases, it is possible to instantiate more than one Driver and more than one Manager.
|
||||||
|
///
|
||||||
|
/// A range of different common embedded microprocessor platforms are supported, allowing your project to run
|
||||||
|
/// on your choice of processor.
|
||||||
|
///
|
||||||
|
/// Example programs are included to show the main modes of use.
|
||||||
|
///
|
||||||
|
/// \par Drivers
|
||||||
|
///
|
||||||
|
/// The following Drivers are provided:
|
||||||
|
///
|
||||||
|
/// - RH_RF22
|
||||||
|
/// Works with Hope-RF
|
||||||
|
/// RF22B and RF23B based transceivers, and compatible chips and modules,
|
||||||
|
/// including the RFM22B transceiver module such as
|
||||||
|
/// this bare module: http://www.sparkfun.com/products/10153
|
||||||
|
/// and this shield: http://www.sparkfun.com/products/11018
|
||||||
|
/// and this board: http://www.anarduino.com/miniwireless
|
||||||
|
/// and RF23BP modules such as: http://www.anarduino.com/details.jsp?pid=130
|
||||||
|
/// Supports GFSK, FSK and OOK. Access to other chip
|
||||||
|
/// features such as on-chip temperature measurement, analog-digital
|
||||||
|
/// converter, transmitter power control etc is also provided.
|
||||||
|
///
|
||||||
|
/// - RH_RF24
|
||||||
|
/// Works with Silicon Labs Si4460/4461/4463/4464 family of transceivers chip, and the equivalent
|
||||||
|
/// HopeRF RF24/26/27 family of chips and the HopeRF RFM24W/26W/27W modules.
|
||||||
|
/// Supports GFSK, FSK and OOK. Access to other chip
|
||||||
|
/// features such as on-chip temperature measurement, analog-digital
|
||||||
|
/// converter, transmitter power control etc is also provided.
|
||||||
|
///
|
||||||
|
/// - RH_RF69
|
||||||
|
/// Works with Hope-RF
|
||||||
|
/// RF69B based radio modules, such as the RFM69 module, (as used on the excellent Moteino and Moteino-USB
|
||||||
|
/// boards from LowPowerLab http://lowpowerlab.com/moteino/ )
|
||||||
|
/// and compatible chips and modules such as RFM69W, RFM69HW, RFM69CW, RFM69HCW (Semtech SX1231, SX1231H).
|
||||||
|
/// Also works with Anarduino MiniWireless -CW and -HW boards http://www.anarduino.com/miniwireless/ including
|
||||||
|
/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range).
|
||||||
|
/// Supports GFSK, FSK.
|
||||||
|
///
|
||||||
|
/// - RH_NRF24
|
||||||
|
/// Works with Nordic nRF24 based 2.4GHz radio modules, such as nRF24L01 and others.
|
||||||
|
/// Also works with Hope-RF RFM73
|
||||||
|
/// and compatible devices (such as BK2423). nRF24L01 and RFM73 can interoperate
|
||||||
|
/// with each other.
|
||||||
|
///
|
||||||
|
/// - RH_NRF905
|
||||||
|
/// Works with Nordic nRF905 based 433/868/915 MHz radio modules.
|
||||||
|
///
|
||||||
|
/// - RH_NRF51
|
||||||
|
/// Works with Nordic nRF51 compatible 2.4 GHz SoC/devices such as the nRF51822.
|
||||||
|
///
|
||||||
|
/// - RH_RF95
|
||||||
|
/// Works with Semtech SX1276/77/78/79, Modtronix inAir4 and inAir9,
|
||||||
|
/// and HopeRF RFM95/96/97/98 and other similar LoRa capable radios.
|
||||||
|
/// Supports Long Range (LoRa) with spread spectrum frequency hopping, large payloads etc.
|
||||||
|
/// FSK/GFSK/OOK modes are not (yet) supported.
|
||||||
|
///
|
||||||
|
/// - RH_MRF89
|
||||||
|
/// Works with Microchip MRF89XA and compatible transceivers.
|
||||||
|
/// and modules such as MRF89XAM9A.
|
||||||
|
///
|
||||||
|
/// - RH_CC110
|
||||||
|
/// Works with Texas Instruments CC110L transceivers and compatible modules such as Anaren AIR BoosterPack 430BOOST-CC110L
|
||||||
|
///
|
||||||
|
/// - RH_ASK
|
||||||
|
/// Works with a range of inexpensive ASK (amplitude shift keying) RF transceivers such as RX-B1
|
||||||
|
/// (also known as ST-RX04-ASK) receiver; TX-C1 transmitter and DR3100 transceiver; FS1000A/XY-MK-5V transceiver;
|
||||||
|
/// HopeRF RFM83C / RFM85. Supports ASK (OOK).
|
||||||
|
///
|
||||||
|
/// - RH_Serial
|
||||||
|
/// Works with RS232, RS422, RS485, RS488 and other point-to-point and multidropped serial connections,
|
||||||
|
/// or with TTL serial UARTs such as those on Arduino and many other processors,
|
||||||
|
/// or with data radios with a
|
||||||
|
/// serial port interface. RH_Serial provides packetization and error detection over any hardware or
|
||||||
|
/// virtual serial connection. Also builds and runs on Linux and OSX.
|
||||||
|
///
|
||||||
|
/// - RH_TCP
|
||||||
|
/// For use with simulated sketches compiled and running on Linux.
|
||||||
|
/// Works with tools/etherSimulator.pl to pass messages between simulated sketches, allowing
|
||||||
|
/// testing of Manager classes on Linux and without need for real radios or other transport hardware.
|
||||||
|
///
|
||||||
|
/// Drivers can be used on their own to provide unaddressed, unreliable datagrams.
|
||||||
|
/// All drivers have the same identical API.
|
||||||
|
/// Or you can use any Driver with any of the Managers described below.
|
||||||
|
///
|
||||||
|
/// We welcome contributions of well tested and well documented code to support other transports.
|
||||||
|
///
|
||||||
|
/// \par Managers
|
||||||
|
///
|
||||||
|
/// The following Mangers are provided:
|
||||||
|
///
|
||||||
|
/// - RHDatagram
|
||||||
|
/// Addressed, unreliable variable length messages, with optional broadcast facilities.
|
||||||
|
///
|
||||||
|
/// - RHReliableDatagram
|
||||||
|
/// Addressed, reliable, retransmitted, acknowledged variable length messages.
|
||||||
|
///
|
||||||
|
/// - RHRouter
|
||||||
|
/// Multi-hop delivery from source node to destination node via 0 or more intermediate nodes, with manual routing.
|
||||||
|
///
|
||||||
|
/// - RHMesh
|
||||||
|
/// Multi-hop delivery with automatic route discovery and rediscovery.
|
||||||
|
///
|
||||||
|
/// Any Manager may be used with any Driver.
|
||||||
|
///
|
||||||
|
/// \par Platforms
|
||||||
|
///
|
||||||
|
/// A range of platforms is supported:
|
||||||
|
///
|
||||||
|
/// - Arduino and the Arduino IDE (version 1.0 to 1.6.5 and later)
|
||||||
|
/// Including Diecimila, Uno, Mega, Leonardo, Yun, Due, Zero etc. http://arduino.cc/, Also similar boards such as
|
||||||
|
/// - Moteino http://lowpowerlab.com/moteino/
|
||||||
|
/// - Anarduino Mini http://www.anarduino.com/mini/
|
||||||
|
/// - RedBearLab Blend V1.0 http://redbearlab.com/blend/ (with Arduino 1.0.5 and RedBearLab Blend Add-On version 20140701)
|
||||||
|
/// - MoteinoMEGA https://lowpowerlab.com/shop/moteinomega
|
||||||
|
/// (with Arduino 1.0.5 and the MoteinoMEGA Arduino Core
|
||||||
|
/// https://github.com/LowPowerLab/Moteino/tree/master/MEGA/Core)
|
||||||
|
/// - ESP8266 on Arduino IDE and Boards Manager per https://github.com/esp8266/Arduino
|
||||||
|
/// Tested using Arduino 1.6.8 with esp8266 by ESP8266 Community version 2.1.0
|
||||||
|
/// Examples serial_reliable_datagram_* and ask_* are shown to work.
|
||||||
|
/// CAUTION: The GHz radio included in the ESP8266 is
|
||||||
|
/// not yet supported.
|
||||||
|
/// - etc.
|
||||||
|
///
|
||||||
|
/// - ChipKIT Core with Arduino IDE on any ChipKIT Core supported Digilent processor (tested on Uno32)
|
||||||
|
/// http://chipkit.net/wiki/index.php?title=ChipKIT_core
|
||||||
|
///
|
||||||
|
/// - Maple and Flymaple boards with libmaple and the Maple-IDE development environment
|
||||||
|
/// http://leaflabs.com/devices/maple/ and http://www.open-drone.org/flymaple
|
||||||
|
///
|
||||||
|
/// - Teensy including Teensy 3.1 and earlier built using Arduino IDE 1.0.5 to 1.6.4 and later with
|
||||||
|
/// teensyduino addon 1.18 to 1.23 and later.
|
||||||
|
/// http://www.pjrc.com/teensy
|
||||||
|
///
|
||||||
|
/// - Particle Photon https://store.particle.io/collections/photon and ARM3 based CPU with built-in
|
||||||
|
/// Wi-Fi transceiver and extensive IoT software suport. RadioHead does not support the built-in transceiver
|
||||||
|
/// bt can be used to control other SPI based radios, Serial ports etc.
|
||||||
|
/// See below for details on how to build RadioHead for Photon
|
||||||
|
///
|
||||||
|
/// - ATtiny built using Arduino IDE 1.0.5 with the arduino-tiny support from https://code.google.com/p/arduino-tiny/
|
||||||
|
/// and Digispark built with Arduino 1.6.5.
|
||||||
|
/// (Caution: these are very small processors and not all RadioHead features may be available, depending on memory requirements)
|
||||||
|
///
|
||||||
|
/// - nRF51 compatible Arm chips such as nRF51822 with Arduino 1.6.4 and later using the procedures
|
||||||
|
/// in http://redbearlab.com/getting-started-nrf51822/
|
||||||
|
///
|
||||||
|
/// - Raspberry Pi
|
||||||
|
/// Uses BCM2835 library for GPIO http://www.airspayce.com/mikem/bcm2835/
|
||||||
|
/// Currently works only with RH_NRF24 driver or other drivers that do not require interrupt support.
|
||||||
|
/// Contributed by Mike Poublon.
|
||||||
|
///
|
||||||
|
/// - Linux and OSX
|
||||||
|
/// Using the RHutil/HardwareSerial class, the RH_Serial driver and any manager will
|
||||||
|
/// build and run on Linux and OSX. These can be used to build programs that talk securely and reliably to
|
||||||
|
/// Arduino and other processors or to other Linux or OSX hosts on a reliable, error detected datagram
|
||||||
|
/// protocol over a serial line.
|
||||||
|
///
|
||||||
|
/// Other platforms are partially supported, such as Generic AVR 8 bit processors, MSP430.
|
||||||
|
/// We welcome contributions that will expand the range of supported platforms.
|
||||||
|
///
|
||||||
|
/// RadioHead is available (through the efforts of others)
|
||||||
|
/// for PlatformIO. PlatformIO is a cross-platform code builder and the missing library manager.
|
||||||
|
/// http://platformio.org/#!/lib/show/124/RadioHead
|
||||||
|
///
|
||||||
|
/// \par History
|
||||||
|
///
|
||||||
|
/// RadioHead was created in April 2014, substantially based on code from some of our other earlier Radio libraries:
|
||||||
|
///
|
||||||
|
/// - RHMesh, RHRouter, RHReliableDatagram and RHDatagram are derived from the RF22 library version 1.39.
|
||||||
|
/// - RH_RF22 is derived from the RF22 library version 1.39.
|
||||||
|
/// - RH_RF69 is derived from the RF69 library version 1.2.
|
||||||
|
/// - RH_ASK is based on the VirtualWire library version 1.26, after significant conversion to C++.
|
||||||
|
/// - RH_Serial was new.
|
||||||
|
/// - RH_NRF24 is based on the NRF24 library version 1.12, with some significant changes.
|
||||||
|
///
|
||||||
|
/// During this combination and redevelopment, we have tried to retain all the processor dependencies and support from
|
||||||
|
/// the libraries that were contributed by other people. However not all platforms can be tested by us, so if you
|
||||||
|
/// find that support from some platform has not been successfully migrated, please feel free to fix it and send us a
|
||||||
|
/// patch.
|
||||||
|
///
|
||||||
|
/// Users of RHMesh, RHRouter, RHReliableDatagram and RHDatagram in the previous RF22 library will find that their
|
||||||
|
/// existing code will run mostly without modification. See the RH_RF22 documentation for more details.
|
||||||
|
///
|
||||||
|
/// \par Installation
|
||||||
|
///
|
||||||
|
/// Install in the usual way: unzip the distribution zip file to the libraries
|
||||||
|
/// sub-folder of your sketchbook.
|
||||||
|
/// The example sketches will be visible in in your Arduino, mpide, maple-ide or whatever.
|
||||||
|
/// http://arduino.cc/en/Guide/Libraries
|
||||||
|
///
|
||||||
|
/// \par Building for Particle Photon
|
||||||
|
///
|
||||||
|
/// The Photon is not supported by the Arduino IDE, so it takes a little effort to set up a build environment.
|
||||||
|
/// Heres what we did to enable building of RadioHead example sketches on Linux,
|
||||||
|
/// but there are other ways to skin this cat.
|
||||||
|
/// Basic reference for getting stated is: http://particle-firmware.readthedocs.org/en/develop/build/
|
||||||
|
/// - Download the ARM gcc cross compiler binaries and unpack it in a suitable place:
|
||||||
|
/// \code
|
||||||
|
/// cd /tmp
|
||||||
|
/// wget https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2
|
||||||
|
/// tar xvf gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2
|
||||||
|
/// \endcode
|
||||||
|
/// - If dfu-util and friends not installed on your platform, download dfu-util and friends to somewhere in your path
|
||||||
|
/// \code
|
||||||
|
/// cd ~/bin
|
||||||
|
/// wget http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/linux-i386/dfu-util
|
||||||
|
/// wget http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/linux-i386/dfu-suffix
|
||||||
|
/// wget http://dfu-util.sourceforge.net/releases/dfu-util-0.8-binaries/linux-i386/dfu-prefix
|
||||||
|
/// \endcode
|
||||||
|
/// - Download the Particle firmware (contains headers and libraries require to compile Photon sketches)
|
||||||
|
/// to a suitable place:
|
||||||
|
/// \code
|
||||||
|
/// cd /tmp
|
||||||
|
/// wget https://github.com/spark/firmware/archive/develop.zip
|
||||||
|
/// unzip develop.zip
|
||||||
|
/// \endcode
|
||||||
|
/// - Make a working area containing the RadioHead library source code and your RadioHead sketch. You must
|
||||||
|
/// rename the sketch from .pde or .ino to application.cpp
|
||||||
|
/// \code
|
||||||
|
/// cd /tmp
|
||||||
|
/// mkdir RadioHead
|
||||||
|
/// cd RadioHead
|
||||||
|
/// cp /usr/local/projects/arduino/libraries/RadioHead/*.h .
|
||||||
|
/// cp /usr/local/projects/arduino/libraries/RadioHead/*.cpp .
|
||||||
|
/// cp /usr/local/projects/arduino/libraries/RadioHead/examples/cc110/cc110_client/cc110_client.pde application.cpp
|
||||||
|
/// \endcode
|
||||||
|
/// - Edit application.cpp and comment out any #include <SPI.h> so it looks like:
|
||||||
|
/// \code
|
||||||
|
/// // #include <SPI.h>
|
||||||
|
/// \endcode
|
||||||
|
/// - Connect your Photon by USB. Put it in DFU mode as descibed in Photon documentation. Light should be flashing yellow
|
||||||
|
/// - Compile the RadioHead sketch and install it as the user program (this does not update the rest of the
|
||||||
|
/// Photon firmware, just the user part:
|
||||||
|
/// \code
|
||||||
|
/// cd /tmp/firmware-develop/main
|
||||||
|
/// PATH=$PATH:/tmp/gcc-arm-none-eabi-5_2-2015q4/bin make APPDIR=/tmp/RadioHead all PLATFORM=photon program-dfu
|
||||||
|
/// \endcode
|
||||||
|
/// - You should see RadioHead compile without errors and download the finished sketch into the Photon.
|
||||||
|
///
|
||||||
|
/// \par Compatible Hardware Suppliers
|
||||||
|
///
|
||||||
|
/// We have had good experiences with the following suppliers of RadioHead compatible hardware:
|
||||||
|
///
|
||||||
|
/// - LittleBird http://littlebirdelectronics.com.au in Australia for all manner of Arduinos and radios.
|
||||||
|
/// - LowPowerLab http://lowpowerlab.com/moteino in USA for the excellent Moteino and Moteino-USB
|
||||||
|
/// boards which include Hope-RF RF69B radios on-board.
|
||||||
|
/// - Anarduino and HopeRF USA (http://www.hoperfusa.com and http://www.anarduino.com) who have a wide range
|
||||||
|
/// of HopeRF radios and Arduino integrated modules.
|
||||||
|
/// - SparkFun https://www.sparkfun.com/ in USA who design and sell a wide range of Arduinos and radio modules.
|
||||||
|
///
|
||||||
|
/// \par Donations
|
||||||
|
///
|
||||||
|
/// This library is offered under a free GPL license for those who want to use it that way.
|
||||||
|
/// We try hard to keep it up to date, fix bugs
|
||||||
|
/// and to provide free support. If this library has helped you save time or money, please consider donating at
|
||||||
|
/// http://www.airspayce.com or here:
|
||||||
|
///
|
||||||
|
/// \htmlonly <form action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="business" value="mikem@airspayce.com" /> <input type="hidden" name="lc" value="AU" /> <input type="hidden" name="item_name" value="Airspayce" /> <input type="hidden" name="item_number" value="RadioHead" /> <input type="hidden" name="currency_code" value="USD" /> <input type="hidden" name="bn" value="PP-DonationsBF:btn_donateCC_LG.gif:NonHosted" /> <input type="image" alt="PayPal — The safer, easier way to pay online." name="submit" src="https://www.paypalobjects.com/en_AU/i/btn/btn_donateCC_LG.gif" /> <img alt="" src="https://www.paypalobjects.com/en_AU/i/scr/pixel.gif" width="1" height="1" border="0" /></form> \endhtmlonly
|
||||||
|
///
|
||||||
|
/// \par Trademarks
|
||||||
|
///
|
||||||
|
/// RadioHead is a trademark of AirSpayce Pty Ltd. The RadioHead mark was first used on April 12 2014 for
|
||||||
|
/// international trade, and is used only in relation to data communications hardware and software and related services.
|
||||||
|
/// It is not to be confused with any other similar marks covering other goods and services.
|
||||||
|
///
|
||||||
|
/// \par Copyright
|
||||||
|
///
|
||||||
|
/// This software is Copyright (C) 2011-2016 Mike McCauley. Use is subject to license
|
||||||
|
/// conditions. The main licensing options available are GPL V2 or Commercial:
|
||||||
|
///
|
||||||
|
/// \par 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
|
||||||
|
///
|
||||||
|
/// \par Commercial Licensing
|
||||||
|
///
|
||||||
|
/// This is the appropriate option if you are creating proprietary applications
|
||||||
|
/// and you are not prepared to distribute and share the source code of your
|
||||||
|
/// application. Contact info@airspayce.com for details (do not use this address for anything other than
|
||||||
|
/// commercial license enquiries. For all other queries, using the RadioHead mailing list).
|
||||||
|
///
|
||||||
|
/// \par Revision History
|
||||||
|
/// \version 1.1 2014-04-14<br>
|
||||||
|
/// Initial public release
|
||||||
|
/// \version 1.2 2014-04-23<br>
|
||||||
|
/// Fixed various typos. <br>
|
||||||
|
/// Added links to compatible Anarduino products.<br>
|
||||||
|
/// Added RHNRFSPIDriver, RH_NRF24 classes to support Nordic NRF24 based radios.
|
||||||
|
/// \version 1.3 2014-04-28<br>
|
||||||
|
/// Various documentation fixups.<br>
|
||||||
|
/// RHDatagram::setThisAddress() did not set the local copy of thisAddress. Reported by Steve Childress.<br>
|
||||||
|
/// Fixed a problem on Teensy with RF22 and RF69, where the interrupt pin needs to be set for input, <br>
|
||||||
|
/// else pin interrupt doesn't work properly. Reported by Steve Childress and patched by
|
||||||
|
/// Adrien van den Bossche. Thanks.<br>
|
||||||
|
/// Fixed a problem that prevented RF22 honouring setPromiscuous(true). Reported by Steve Childress.<br>
|
||||||
|
/// Updated documentation to clarify some issues to do with maximum message lengths
|
||||||
|
/// reported by Steve Childress.<br>
|
||||||
|
/// Added support for yield() on systems that support it (currently Arduino 1.5.5 and later)
|
||||||
|
/// so that spin-loops can suport multitasking. Suggested by Steve Childress.<br>
|
||||||
|
/// Added RH_RF22::setGpioReversed() so the reversal it can be configured at run-time after
|
||||||
|
/// radio initialisation. It must now be called _after_ init(). Suggested by Steve Childress.<br>
|
||||||
|
/// \version 1.4 2014-04-29<br>
|
||||||
|
/// Fixed further problems with Teensy compatibility for RH_RF22. Tested on Teensy 3.1.
|
||||||
|
/// The example/rf22_* examples now run out of the box with the wiring connections as documented for Teensy
|
||||||
|
/// in RH_RF22.<br>
|
||||||
|
/// Added YIELDs to spin-loops in RHRouter, RHMesh and RHReliableDatagram, RH_NRF24.<br>
|
||||||
|
/// Tested RH_Serial examples with Teensy 3.1: they now run out of the box.<br>
|
||||||
|
/// Tested RH_ASK examples with Teensy 3.1: they now run out of the box.<br>
|
||||||
|
/// Reduced default SPI speed for NRF24 from 8MHz to 1MHz on Teensy, to improve reliability when
|
||||||
|
/// poor wiring is in use.<br>
|
||||||
|
/// on some devices such as Teensy.<br>
|
||||||
|
/// Tested RH_NRF24 examples with Teensy 3.1: they now run out of the box.<br>
|
||||||
|
/// \version 1.5 2014-04-29<br>
|
||||||
|
/// Added support for Nordic Semiconductor nRF905 transceiver with RH_NRF905 driver. Also
|
||||||
|
/// added examples for nRF905 and tested on Teensy 3.1
|
||||||
|
/// \version 1.6 2014-04-30<br>
|
||||||
|
/// NRF905 examples were missing
|
||||||
|
/// \version 1.7 2014-05-03<br>
|
||||||
|
/// Added support for Arduino Due. Tested with RH_NRF905, RH_Serial, RH_ASK.
|
||||||
|
/// IMPORTANT CHANGE to interrupt pins on Arduino with RH_RF22 and RH_RF69 constructors:
|
||||||
|
/// previously, you had to specify the interrupt _number_ not the interrupt _pin_. Arduinos and Uno32
|
||||||
|
/// are now consistent with all other platforms: you must specify the interrupt pin number. Default
|
||||||
|
/// changed to pin 2 (a common choice with RF22 shields).
|
||||||
|
/// Removed examples/maple/maple_rf22_reliable_datagram_client and
|
||||||
|
/// examples/maple/maple_rf22_reliable_datagram_client since the rf22 examples now work out
|
||||||
|
/// of the box with Flymaple.
|
||||||
|
/// Removed examples/uno32/uno32_rf22_reliable_datagram_client and
|
||||||
|
/// examples/uno32/uno32_rf22_reliable_datagram_client since the rf22 examples now work out
|
||||||
|
/// of the box with ChipKit Uno32.
|
||||||
|
/// \version 1.8 2014-05-08 <br>
|
||||||
|
/// Added support for YIELD in Teensy 2 and 3, suggested by Steve Childress.<br>
|
||||||
|
/// Documentation updates. Clarify use of headers and Flags<br>
|
||||||
|
/// Fixed misalignment in RH_RF69 between ModemConfigChoice definitions and the implemented choices
|
||||||
|
/// which meant you didnt get the choice you thought and GFSK_Rb55555Fd50 hung the transmitter.<br>
|
||||||
|
/// Preliminary work on Linux simulator.
|
||||||
|
/// \version 1.9 2014-05-14 <br>
|
||||||
|
/// Added support for using Timer 2 instead of Timer 1 on Arduino in RH_ASK when
|
||||||
|
/// RH_ASK_ARDUINO_USE_TIMER2 is defined. With the kind assistance of
|
||||||
|
/// Luc Small. Thanks!<br>
|
||||||
|
/// Updated comments in RHReliableDatagram concerning servers, retries, timeouts and delays.
|
||||||
|
/// Fixed an error in RHReliableDatagram where recvfrom return value was not checked.
|
||||||
|
/// Reported by Steve Childress.<br>
|
||||||
|
/// Added Linux simulator support so simple RadioHead sketches can be compiled and run on Linux.<br>
|
||||||
|
/// Added RH_TCP driver to permit message passing between simulated sketches on Linux.<br>
|
||||||
|
/// Added example simulator sketches.<br>
|
||||||
|
/// Added tools/etherSimulator.pl, a simulator of the 'Luminiferous Ether' that passes
|
||||||
|
/// messages between simulated sketches and can simulate random message loss etc.<br>
|
||||||
|
/// Fixed a number of typos and improved some documentation.<br>
|
||||||
|
/// \version 1.10 2014-05-15 <br>
|
||||||
|
/// Added support for RFM73 modules to RH_NRF24. These 2 radios are very similar, and can interoperate
|
||||||
|
/// with each other. Added new RH_NRF24::TransmitPower enums for the RFM73, which has a different
|
||||||
|
/// range of available powers<br>
|
||||||
|
/// reduced the default SPI bus speed for RH_NRF24 to 1MHz, since so many modules and CPU have problems
|
||||||
|
/// with 8MHz.<br>
|
||||||
|
/// \version 1.11 2014-05-18<br>
|
||||||
|
/// Testing RH_RF22 with RFM23BP and 3.3V Teensy 3.1 and 5V Arduinos.
|
||||||
|
/// Updated documentation with respect to GPIO and antenna
|
||||||
|
/// control pins for RFM23. Updated documentation with respect to transmitter power control for RFM23<br>
|
||||||
|
/// Fixed a problem with RH_RF22 driver, where GPIO TX and RX pins were not configured during
|
||||||
|
/// initialisation, causing poor transmit power and sensitivity on those RF22/RF23 devices where GPIO controls
|
||||||
|
/// the antenna selection pins.
|
||||||
|
/// \version 1.12 2014-05-20<br>
|
||||||
|
/// Testing with RF69HW and the RH_RF69 driver. Works well with the Anarduino MiniWireless -CW and -HW
|
||||||
|
/// boards http://www.anarduino.com/miniwireless/ including
|
||||||
|
/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range).<br>
|
||||||
|
/// Clarified documentation of RH_RF69::setTxPower values for different models of RF69.<br>
|
||||||
|
/// Added RHReliableDatagram::resetRetransmissions().<br>
|
||||||
|
/// Retransmission count precision increased to uin32_t.<br>
|
||||||
|
/// Added data about actual power measurements from RFM22 module.<br>
|
||||||
|
/// \version 1.13 2014-05-23<br>
|
||||||
|
/// setHeaderFlags(flags) changed to setHeaderFlags(set, clear), enabling any flags to be
|
||||||
|
/// individually set and cleared by either RadioHead or application code. Requested by Steve Childress.<br>
|
||||||
|
/// Fixed power output setting for boost power on RF69HW for 18, 19 and 20dBm.<br>
|
||||||
|
/// Added data about actual power measurements from RFM69W and RFM69HW modules.<br>
|
||||||
|
/// \version 1.14 2014-05-26<br>
|
||||||
|
/// RH_RF69::init() now always sets the PA boost back to the default settings, else can get invalid
|
||||||
|
/// PA power modes after uploading new sketches without a power cycle. Reported by Bryan.<br>
|
||||||
|
/// Added new macros RH_VERSION_MAJOR RH_VERSION_MINOR, with automatic maintenance in Makefile.<br>
|
||||||
|
/// Improvements to RH_TCP: constructor now honours the server argument in the form "servername:port".<br>
|
||||||
|
/// Added YIELD to RHReliableDatagram::recvfromAckTimeout. Requested by Steve Childress.<br>
|
||||||
|
/// Fixed a problem with RH_RF22 reliable datagram acknowledgements that was introduced in version 1.13.
|
||||||
|
/// Reported by Steve Childress.<br>
|
||||||
|
/// \version 1.15 2014-05-27<br>
|
||||||
|
/// Fixed a problem with the RadioHead .zip link.
|
||||||
|
/// \version 1.16 2014-05-30 <br>
|
||||||
|
/// Fixed RH_RF22 so that lastRssi() returns the signal strength in dBm. Suggested by Steve Childress.<br>
|
||||||
|
/// Added support for getLastPreambleTime() to RH_RF69. Requested by Steve Childress.<br>
|
||||||
|
/// RH_NRF24::init() now checks if there is a device connected and responding, else init() will fail.
|
||||||
|
/// Suggested by Steve Brown.<br>
|
||||||
|
/// RHSoftwareSPI now initialises default values for SPI pins MOSI = 12, MISO = 11 and SCK = 13.<br>
|
||||||
|
/// Fixed some problems that prevented RH_NRF24 working with mixed software and hardware SPI
|
||||||
|
/// on different devices: a race condition
|
||||||
|
/// due to slow SPI transfers and fast acknowledgement.<br>
|
||||||
|
/// \version 1.17 2014-06-02 <br>
|
||||||
|
/// Fixed a debug typo in RHReliableDatagram that was introduced in 1.16.<br>
|
||||||
|
/// RH_NRF24 now sets default power, data rate and channel in init(), in case another
|
||||||
|
/// app has previously set different values without powerdown.<br>
|
||||||
|
/// Caution: there are still problems with RH_NRF24 and Software SPI. Do not use.<br>
|
||||||
|
/// \version 1.18 2014-06-02<br>
|
||||||
|
/// Improvements to performance of RH_NRF24 statusRead, allowing RH_NRF24 and Software SPI
|
||||||
|
/// to operate on slow devices like Arduino Uno.<br>
|
||||||
|
/// \version 1.19 2014-06-19<br>
|
||||||
|
/// Added examples ask_transmitter.pde and ask_receiver.pde.<br>
|
||||||
|
/// Fixed an error in the RH_RF22 doc for connection of Teensy to RF22.<br>
|
||||||
|
/// Improved documentation of start symbol bit patterns in RH_ASK.cpp
|
||||||
|
/// \version 1.20 2014-06-24<br>
|
||||||
|
/// Fixed a problem with compiling on platforms such as ATTiny where SS is not defined.<br>
|
||||||
|
/// Added YIELD to RHMesh::recvfromAckTimeout().<br>
|
||||||
|
/// \version 1.21 2014-06-24<br>
|
||||||
|
/// Fixed an issue in RH_Serial where characters might be lost with back-to-back frames.
|
||||||
|
/// Suggested by Steve Childress.<br>
|
||||||
|
/// Brought previous RHutil/crc16.h code into mainline RHCRC.cpp to prevent name collisions
|
||||||
|
/// with other similarly named code in other libraries. Suggested by Steve Childress.<br>
|
||||||
|
/// Fix SPI bus speed errors on 8MHz Arduinos.
|
||||||
|
/// \version 1.22 2014-07-01<br>
|
||||||
|
/// Update RH_ASK documentation for common wiring connections.<br>
|
||||||
|
/// Testing RH_ASK with HopeRF RFM83C/RFM85 courtesy Anarduino http://www.anarduino.com/<br>
|
||||||
|
/// Testing RH_NRF24 with Itead Studio IBoard Pro http://imall.iteadstudio.com/iboard-pro.html
|
||||||
|
/// using both hardware SPI on the ITDB02 Parallel LCD Module Interface pins and software SPI
|
||||||
|
/// on the nRF24L01+ Module Interface pins. Documented wiring required.<br>
|
||||||
|
/// Added support for AVR 1284 and 1284p, contributed by Peter Scargill.
|
||||||
|
/// Added support for Semtech SX1276/77/78 and HopeRF RFM95/96/97/98 and other similar LoRa capable radios
|
||||||
|
/// in LoRa mode only. Tested with the excellent MiniWirelessLoRa from
|
||||||
|
/// Anarduino http://www.anarduino.com/miniwireless<br>
|
||||||
|
/// \version 1.23 2014-07-03<br>
|
||||||
|
/// Changed the default modulation for RH_RF69 to GFSK_Rb250Fd250, since the previous default
|
||||||
|
/// was not very reliable.<br>
|
||||||
|
/// Documented RH_RF95 range tests.<br>
|
||||||
|
/// Improvements to RH_RF22 RSSI readings so that lastRssi correctly returns the last message in dBm.<br>
|
||||||
|
/// \version 1.24 2014-07-18
|
||||||
|
/// Added support for building RadioHead for STM32F4 Discovery boards, using the native STM Firmware libraries,
|
||||||
|
/// in order to support Codec2WalkieTalkie (http://www.airspayce.com/mikem/Codec2WalkieTalkie)
|
||||||
|
/// and other projects. See STM32ArduinoCompat.<br>
|
||||||
|
/// Default modulation for RH_RF95 was incorrectly set to a very slow Bw125Cr48Sf4096
|
||||||
|
/// \version 1.25 2014-07-25
|
||||||
|
/// The available() function will longer terminate any current transmission, and force receive mode.
|
||||||
|
/// Now, if there is no unprocessed incoming message and an outgoing message is currently being transmitted,
|
||||||
|
/// available() will return false.<br>
|
||||||
|
/// RHRouter::sendtoWait(uint8_t*, uint8_t, uint8_t, uint8_t) renamed to sendtoFromSourceWait due to conflicts
|
||||||
|
/// with new sendtoWait() with optional flags.<br>
|
||||||
|
/// RHMEsh and RHRouter already supported end-to-end application layer flags, but RHMesh::sendtoWait()
|
||||||
|
/// and RHRouter::sendToWait have now been extended to expose a way to send optional application layer flags.
|
||||||
|
/// \version 1.26 2014-08-12
|
||||||
|
/// Fixed a Teensy 2.0 compile problem due yield() not available on Teensy < 3.0. <br>
|
||||||
|
/// Adjusted the algorithm of RH_RF69::temperatureRead() to more closely reflect reality.<br>
|
||||||
|
/// Added functions to RHGenericDriver to get driver packet statistics: rxBad(), rxGood(), txGood().<br>
|
||||||
|
/// Added RH_RF69::printRegisters().<br>
|
||||||
|
/// RH_RF95::printRegisters() was incorrectly printing the register index instead of the address.
|
||||||
|
/// Reported by Phang Moh Lim.<br>
|
||||||
|
/// RH_RF95, added definitions for some more registers that are usable in LoRa mode.<br>
|
||||||
|
/// RH_RF95::setTxPower now uses RH_RF95_PA_DAC_ENABLE to achieve 21, 22 and 23dBm.<br>
|
||||||
|
/// RH_RF95, updated power output measurements.<br>
|
||||||
|
/// Testing RH_RF69 on Teensy 3.1 with RF69 on PJRC breakout board. OK.<br>
|
||||||
|
/// Improvements so RadioHead will build under Arduino where SPI is not supported, such as
|
||||||
|
/// ATTiny.<br>
|
||||||
|
/// Improvements so RadioHead will build for ATTiny using Arduino IDE and tinycore arduino-tiny-0100-0018.zip.<br>
|
||||||
|
/// Testing RH_ASK on ATTiny85. Reduced RAM footprint.
|
||||||
|
/// Added helpful documentation. Caution: RAM memory is *very* tight on this platform.<br>
|
||||||
|
/// RH_RF22 and RH_RF69, added setIdleMode() function to allow the idle mode radio operating state
|
||||||
|
/// to be controlled for lower idle power consumption at the expense of slower transitions to TX and RX.<br>
|
||||||
|
/// \version 1.27 2014-08-13
|
||||||
|
/// All RH_RF69 modulation schemes now have data whitening enabled by default.<br>
|
||||||
|
/// Tested and added a number of OOK modulation schemes to RH_RF69 Modem config table.<br>
|
||||||
|
/// Minor improvements to a number of the faster RH_RF69 modulation schemes, but some slower ones
|
||||||
|
/// are still not working correctly.<br>
|
||||||
|
/// \version 1.28 2014-08-20
|
||||||
|
/// Added new RH_RF24 driver to support Si446x, RF24/26/26, RFM24/26/27 family of transceivers.
|
||||||
|
/// Tested with the excellent
|
||||||
|
/// Anarduino Mini and RFM24W and RFM26W with the generous assistance of the good people at
|
||||||
|
/// Anarduino http://www.anarduino.com.
|
||||||
|
/// \version 1.29 2014-08-21
|
||||||
|
/// Fixed a compile error in RH_RF24 introduced at the last minute in hte previous release.<br>
|
||||||
|
/// Improvements to RH_RF69 modulation schemes: now include the AFCBW in teh ModemConfig.<br>
|
||||||
|
/// ModemConfig RH_RF69::FSK_Rb2Fd5 and RH_RF69::GFSK_Rb2Fd5 are now working.<br>
|
||||||
|
/// \version 1.30 2014-08-25
|
||||||
|
/// Fixed some compile problems with ATtiny84 on Arduino 1.5.5 reported by Glen Cook.<br>
|
||||||
|
/// \version 1.31 2014-08-27
|
||||||
|
/// Changed RH_RF69 FSK and GFSK modulations from Rb2_4Fd2_4 to Rb2_4Fd4_8 and FSK_Rb4_8Fd4_8 to FSK_Rb4_8Fd9_6
|
||||||
|
/// since the previous ones were unreliable (they had modulation indexes of 1).<br>
|
||||||
|
/// \version 1.32 2014-08-28
|
||||||
|
/// Testing with RedBearLab Blend board http://redbearlab.com/blend/. OK.<br>
|
||||||
|
/// Changed more RH_RF69 FSK and GFSK slowish modulations to have modulation index of 2 instead of 1.
|
||||||
|
/// This required chnaging the symbolic names.<br>
|
||||||
|
/// \version 1.33 2014-09-01
|
||||||
|
/// Added support for sleep mode in RHGeneric driver, with new mode
|
||||||
|
/// RHModeSleep and new virtual function sleep().<br>
|
||||||
|
/// Added support for sleep to RH_RF69, RH_RF22, RH_NRF24, RH_RF24, RH_RF95 drivers.<br>
|
||||||
|
/// \version 1.34 2014-09-19
|
||||||
|
/// Fixed compile errors in example rf22_router_test.<br>
|
||||||
|
/// Fixed a problem with RH_NRF24::setNetworkAddress, also improvements to RH_NRF24 register printing.
|
||||||
|
/// Patched by Yveaux.<br>
|
||||||
|
/// Improvements to RH_NRF24 initialisation for version 2.0 silicon.<br>
|
||||||
|
/// Fixed problem with ambigiguous print call in RH_RFM69 when compiling for Codec2.<br>
|
||||||
|
/// Fixed a problem with RH_NRF24 on RFM73 where the LNA gain was not set properly, reducing the sensitivity
|
||||||
|
/// of the receiver.
|
||||||
|
/// \version 1.35 2014-09-19
|
||||||
|
/// Fixed a problem with interrupt setup on RH_RF95 with Teensy3.1. Reported by AD.<br>
|
||||||
|
/// \version 1.36 2014-09-22
|
||||||
|
/// Improvements to interrupt pin assignments for __AVR_ATmega1284__ and__AVR_ATmega1284P__, provided by
|
||||||
|
/// Peter Scargill.<br>
|
||||||
|
/// Work around a bug in Arduino 1.0.6 where digitalPinToInterrupt is defined but NOT_AN_INTERRUPT is not.<br>
|
||||||
|
/// \version 1.37 2014-10-19
|
||||||
|
/// Updated doc for connecting RH_NRF24 to Arduino Mega.<br>
|
||||||
|
/// Changes to RHGenericDriver::setHeaderFlags(), so that the default for the clear argument
|
||||||
|
/// is now RH_FLAGS_APPLICATION_SPECIFIC, which is less surprising to users.
|
||||||
|
/// Testing with the excellent MoteinoMEGA from LowPowerLab
|
||||||
|
/// https://lowpowerlab.com/shop/moteinomega with on-board RFM69W.
|
||||||
|
/// \version 1.38 2014-12-29
|
||||||
|
/// Fixed compile warning on some platforms where RH_RF24::send and RH_RF24::writeTxFifo
|
||||||
|
/// did not return a value.<br>
|
||||||
|
/// Fixed some more compiler warnings in RH_RF24 on some platforms.<br>
|
||||||
|
/// Refactored printRegisters for some radios. Printing to Serial
|
||||||
|
/// is now controlled by the definition of RH_HAVE_SERIAL.<br>
|
||||||
|
/// Added partial support for ARM M4 w/CMSIS with STM's Hardware Abstraction lib for
|
||||||
|
/// Steve Childress.<br>
|
||||||
|
/// \version 1.39 2014-12-30
|
||||||
|
/// Fix some compiler warnings under IAR.<br>
|
||||||
|
/// RH_HAVE_SERIAL and Serial.print calls removed for ATTiny platforms.<br>
|
||||||
|
/// \version 1.40 2015-03-09
|
||||||
|
/// Added notice about availability on PlatformIO, thanks to Ivan Kravets.<br>
|
||||||
|
/// Fixed a problem with RH_NRF24 where short packet lengths would occasionally not be trasmitted
|
||||||
|
/// due to a race condition with RH_NRF24_TX_DS. Reported by Mark Fox.<br>
|
||||||
|
/// \version 1.41 2015-03-29
|
||||||
|
/// RH_RF22, RH_RF24, RH_RF69 and RH_RF95 improved to allow driver.init() to be called multiple
|
||||||
|
/// times without reallocating a new interrupt, allowing the driver to be reinitialised
|
||||||
|
/// after sleeping or powering down.
|
||||||
|
/// \version 1.42 2015-05-17
|
||||||
|
/// Added support for RH_NRF24 driver on Raspberry Pi, using BCM2835
|
||||||
|
/// library for GPIO pin IO. Contributed by Mike Poublon.<br>
|
||||||
|
/// Tested RH_NRF24 module with NRF24L01+PA+LNA SMA Antenna Wireless Transceiver modules
|
||||||
|
/// similar to: http://www.elecfreaks.com/wiki/index.php?title=2.4G_Wireless_nRF24L01p_with_PA_and_LNA
|
||||||
|
/// works with no software changes. Measured max power output 18dBm.<br>
|
||||||
|
/// \version 1.43 2015-08-02
|
||||||
|
/// Added RH_NRF51 driver to support Nordic nRF51 family processor with 2.4GHz radio such
|
||||||
|
/// as nRF51822, to be built on Arduino 1.6.4 and later. Tested with RedBearLabs nRF51822 board
|
||||||
|
/// and BLE Nano kit<br>
|
||||||
|
/// \version 1.44 2015-08-08
|
||||||
|
/// Fixed errors with compiling on some platforms without serial, such as ATTiny.
|
||||||
|
/// Reported by Friedrich Müller.<br>
|
||||||
|
/// \version 1.45 2015-08-13
|
||||||
|
/// Added support for using RH_Serial on Linux and OSX (new class RHutil/HardwareSerial
|
||||||
|
/// encapsulates serial ports on those platforms). Example examples/serial upgraded
|
||||||
|
/// to build and run on Linux and OSX using the tools/simBuild builder.
|
||||||
|
/// RHMesh, RHRouter and RHReliableDatagram updated so they can use RH_Serial without
|
||||||
|
/// polling loops on Linux and OSX for CPU efficiency.<br>
|
||||||
|
/// \version 1.46 2015-08-14
|
||||||
|
/// Amplified some doc concerning Linux and OSX RH_Serial. Added support for 230400
|
||||||
|
/// baud rate in HardwareSerial.<br>
|
||||||
|
/// Added sample sketches nrf51_audio_tx and nrf51_audio_rx which show how to
|
||||||
|
/// build an audio TX/RX pair with RedBear nRF51822 boards and a SparkFun MCP4725 DAC board.
|
||||||
|
/// Uses the built-in ADC of the nRF51822 to sample audio at 5kHz and transmit packets
|
||||||
|
/// to the receiver which plays them via the DAC.<br>
|
||||||
|
/// \version 1.47 2015-09-18
|
||||||
|
/// Removed top level Makefile from distribution: its only used by the developer and
|
||||||
|
/// its presence confuses some people.<br>
|
||||||
|
/// Fixed a problem with RHReliableDatagram with some versions of Raspberry Pi random() that causes
|
||||||
|
/// problems: random(min, max) sometimes exceeds its max limit.
|
||||||
|
/// \version 1.48 2015-09-30
|
||||||
|
/// Added support for Arduino Zero. Tested on Arduino Zero Pro.
|
||||||
|
/// \version 1.49 2015-10-01
|
||||||
|
/// Fixed problems that prevented interrupts working correctly on Arduino Zero and Due.
|
||||||
|
/// Builds and runs with 1.6.5 (with 'Arduino SAMD Boards' for Zero version 1.6.1) from arduino.cc.
|
||||||
|
/// Arduino version 1.7.7 from arduino.org is not currently supported.
|
||||||
|
/// \version 1.50 2015-10-25
|
||||||
|
/// Verified correct building and operation with Arduino 1.7.7 from arduino.org.
|
||||||
|
/// Caution: You must burn the bootloader from 1.7.7 to the Arduino Zero before it will
|
||||||
|
/// work with Arduino 1.7.7 from arduino.org. Conversely, you must burn the bootloader from 1.6.5
|
||||||
|
/// to the Arduino Zero before it will
|
||||||
|
/// work with Arduino 1.6.5 from arduino.cc. Sigh.
|
||||||
|
/// Fixed a problem with RH_NRF905 that prevented the power and frequency ranges being set
|
||||||
|
/// properly. Reported by Alan Webber.
|
||||||
|
/// \version 1.51 2015-12-11
|
||||||
|
/// Changes to RH_RF6::setTxPower() to be compatible with SX1276/77/78/79 modules that
|
||||||
|
/// use RFO transmitter pins instead of PA_BOOST, such as the excellent
|
||||||
|
/// Modtronix inAir4 http://modtronix.com/inair4.html
|
||||||
|
/// and inAir9 modules http://modtronix.com/inair9.html. With the kind assistance of
|
||||||
|
/// David from Modtronix.
|
||||||
|
/// \version 1.52 2015-12-17
|
||||||
|
/// Added RH_MRF89 module to suport Microchip MRF89XA and compatible transceivers.
|
||||||
|
/// and modules.<br>
|
||||||
|
/// \version 1.53 2016-01-02
|
||||||
|
/// Added RH_CC110 module to support Texas Instruments CC110L and compatible transceivers and modules.<br>
|
||||||
|
/// \version 1.54 2016-01-29
|
||||||
|
/// Added support for ESP8266 processor on Arduino IDE. Examples serial_reliable_datagram_* are shown to work.
|
||||||
|
/// CAUTION: SPI not supported yet. Timers used by RH_ASK are not tested.
|
||||||
|
/// The GHz radio included in the ESP8266 is not yet supported.
|
||||||
|
/// \version 1.55 2016-02-12
|
||||||
|
/// Added macros for htons() and friends to RadioHead.h.
|
||||||
|
/// Added example sketch serial_gateway.pde. Acts as a transparent gateway between RH_RF22 and RH_Serial,
|
||||||
|
/// and with minor mods acts as a universal gateway between any 2 RadioHead driver networks.
|
||||||
|
/// Initial work on supporting STM32 F2 on Particle Photon: new platform type defined.
|
||||||
|
/// Fixed many warnings exposed by test building for Photon.
|
||||||
|
/// Particle Photon tested support for RH_Serial, RH_ASK, SPI, RH_CC110 etc.
|
||||||
|
/// Added notes on how to build RadioHead sketches for Photon.
|
||||||
|
/// \version 1.56 2016-02-18
|
||||||
|
/// Implemented timers for RH_ASK on ESP8266, added some doc on IO pin selection.
|
||||||
|
/// \version 1.57 2016-02-23
|
||||||
|
/// Fixed an issue reported by S3B, where RH_RF22 would sometimes not clear the rxbufvalid flag.
|
||||||
|
/// \version 1.58 2-16-04-04
|
||||||
|
/// Tested RH_RF69 with Arduino Due. OK. Updated doc.<br>
|
||||||
|
/// Added support for all ChipKIT Core supported boards
|
||||||
|
/// http://chipkit.net/wiki/index.php?title=ChipKIT_core
|
||||||
|
/// Tested on ChipKIT Uno32.<br>
|
||||||
|
/// Digilent Uno32 under the old MPIDE is no longer formally
|
||||||
|
/// supported but may continue to work for some time.<br>
|
||||||
|
/// \version 1.59 2016-04-12
|
||||||
|
/// Testing with the excellent Rocket Scream Mini Ultra Pro with the RFM95W and RFM69HCW modules from
|
||||||
|
/// http://www.rocketscream.com/blog/product/mini-ultra-pro-with-radio/ (915MHz versions). Updated
|
||||||
|
/// documentation with hints to suit. Caution: requires Arduino 1.6.8 and Arduino SAMD Boards 1.6.5.
|
||||||
|
/// See also http://www.rocketscream.com/blog/2016/03/10/radio-range-test-with-rfm69hcw/
|
||||||
|
/// for the vendors tests and range with the RFM69HCW version.
|
||||||
|
/// These boards are highly recommended. They also include battery charging support.
|
||||||
|
/// \version 1.60 2016-06-25
|
||||||
|
/// Tested with the excellent talk2 Whisper Node boards
|
||||||
|
/// (https://talk2.wisen.com.au/ and https://bitbucket.org/talk2/),
|
||||||
|
/// an Arduino Nano compatible board, which include an on-board RF69 radio, external antenna,
|
||||||
|
/// run on 2xAA batteries and support low power operations. RF69 examples work without modification.
|
||||||
|
/// Added support for ESP8266 SPI, provided by David Skinner.
|
||||||
|
/// \version 1.61 2016-07-07
|
||||||
|
/// Patch to RH_ASK.cpp for ESP8266, to prevent crashes in interrupt handlers. Patch from Alexander Mamchits.
|
||||||
|
///
|
||||||
|
/// \author Mike McCauley. DO NOT CONTACT THE AUTHOR DIRECTLY. USE THE MAILING LIST GIVEN ABOVE
|
||||||
|
|
||||||
|
#ifndef RadioHead_h
|
||||||
|
#define RadioHead_h
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
// Official version numbers are maintained automatically by Makefile:
|
||||||
|
#define RH_VERSION_MAJOR 1
|
||||||
|
#define RH_VERSION_MINOR 61
|
||||||
|
|
||||||
|
// Symbolic names for currently supported platform types
|
||||||
|
#define RH_PLATFORM_ARDUINO 1
|
||||||
|
#define RH_PLATFORM_MSP430 2
|
||||||
|
#define RH_PLATFORM_STM32 3
|
||||||
|
#define RH_PLATFORM_GENERIC_AVR8 4
|
||||||
|
#define RH_PLATFORM_UNO32 5
|
||||||
|
#define RH_PLATFORM_UNIX 6
|
||||||
|
#define RH_PLATFORM_STM32STD 7
|
||||||
|
#define RH_PLATFORM_STM32F4_HAL 8
|
||||||
|
#define RH_PLATFORM_RASPI 9
|
||||||
|
#define RH_PLATFORM_NRF51 10
|
||||||
|
#define RH_PLATFORM_ESP8266 11
|
||||||
|
#define RH_PLATFORM_STM32F2 12
|
||||||
|
#define RH_PLATFORM_CHIPKIT_CORE 13
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// Select platform automatically, if possible
|
||||||
|
#ifndef RH_PLATFORM
|
||||||
|
#if (MPIDE>=150 && defined(ARDUINO))
|
||||||
|
// Using ChipKIT Core on Arduino IDE
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_CHIPKIT_CORE
|
||||||
|
#elif defined(MPIDE)
|
||||||
|
// Uno32 under old MPIDE, which has been discontinued:
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_UNO32
|
||||||
|
#elif defined(NRF51)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_NRF51
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_ESP8266
|
||||||
|
#elif defined(ARDUINO)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_ARDUINO
|
||||||
|
#elif defined(__MSP430G2452__) || defined(__MSP430G2553__)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_MSP430
|
||||||
|
#elif defined(MCU_STM32F103RE)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_STM32
|
||||||
|
#elif defined(STM32F2XX)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_STM32F2
|
||||||
|
#elif defined(USE_STDPERIPH_DRIVER)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_STM32STD
|
||||||
|
#elif defined(RASPBERRY_PI)
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_RASPI
|
||||||
|
#elif defined(__unix__) // Linux
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_UNIX
|
||||||
|
#elif defined(__APPLE__) // OSX
|
||||||
|
#define RH_PLATFORM RH_PLATFORM_UNIX
|
||||||
|
#else
|
||||||
|
#error Platform not defined!
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtinyX4__) || defined(__AVR_ATtinyX5__) || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) || defined(__AVR_ATtinyX313__)
|
||||||
|
#define RH_PLATFORM_ATTINY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// Platform specific headers:
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
|
||||||
|
#if (ARDUINO >= 100)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#else
|
||||||
|
#include <wiring.h>
|
||||||
|
#endif
|
||||||
|
#ifdef RH_PLATFORM_ATTINY
|
||||||
|
#warning Arduino TinyCore does not support hardware SPI. Use software SPI instead.
|
||||||
|
#else
|
||||||
|
#include <SPI.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_ESP8266) // ESP8266 processor on Arduino IDE
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_MSP430) // LaunchPad specific
|
||||||
|
#include "legacymsp430.h"
|
||||||
|
#include "Energia.h"
|
||||||
|
#include <SPI.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_UNO32 || RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE)
|
||||||
|
#include <WProgram.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#define memcpy_P memcpy
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_STM32) // Maple, Flymaple etc
|
||||||
|
#include <wirish.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <HardwareSPI.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
// Defines which timer to use on Maple
|
||||||
|
#define MAPLE_TIMER 1
|
||||||
|
#define PROGMEM
|
||||||
|
#define memcpy_P memcpy
|
||||||
|
#define Serial SerialUSB
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Particle Photon with firmware-develop
|
||||||
|
#include <stm32f2xx.h>
|
||||||
|
#include <application.h>
|
||||||
|
#include <math.h> // floor
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_STM32STD) // STM32 with STM32F4xx_StdPeriph_Driver
|
||||||
|
#include <stm32f4xx.h>
|
||||||
|
#include <wirish.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <HardwareSPI.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#define Serial SerialUSB
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_GENERIC_AVR8)
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
// For Steve Childress port to ARM M4 w/CMSIS with STM's Hardware Abstraction lib.
|
||||||
|
// See ArduinoWorkarounds.h (not supplied)
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_STM32F4_HAL)
|
||||||
|
#include <ArduinoWorkarounds.h>
|
||||||
|
#include <stm32f4xx.h> // Also using ST's CubeMX to generate I/O and CPU setup source code for IAR/EWARM, not GCC ARM.
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#define RH_HAVE_HARDWARE_SPI // using HAL (Hardware Abstraction Libraries from ST along with CMSIS, not arduino libs or pins concept.
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_RASPI)
|
||||||
|
#define RH_HAVE_HARDWARE_SPI
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
#define PROGMEM
|
||||||
|
#include <RHutil/RasPi.h>
|
||||||
|
#include <string.h>
|
||||||
|
//Define SS for CS0 or pin 24
|
||||||
|
#define SS 8
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_NRF51)
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
#define PROGMEM
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_UNIX)
|
||||||
|
// Simulate the sketch on Linux and OSX
|
||||||
|
#include <RHutil/simulator.h>
|
||||||
|
#define RH_HAVE_SERIAL
|
||||||
|
#include <netinet/in.h> // For htons and friends
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error Platform unknown!
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// This is an attempt to make a portable atomic block
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO)
|
||||||
|
#if defined(__arm__)
|
||||||
|
#include <RHutil/atomic.h>
|
||||||
|
#else
|
||||||
|
#include <util/atomic.h>
|
||||||
|
#endif
|
||||||
|
#define ATOMIC_BLOCK_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||||
|
#define ATOMIC_BLOCK_END }
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE)
|
||||||
|
// UsingChipKIT Core on Arduino IDE
|
||||||
|
#define ATOMIC_BLOCK_START unsigned int __status = disableInterrupts(); {
|
||||||
|
#define ATOMIC_BLOCK_END } restoreInterrupts(__status);
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_UNO32)
|
||||||
|
// Under old MPIDE, which has been discontinued:
|
||||||
|
#include <peripheral/int.h>
|
||||||
|
#define ATOMIC_BLOCK_START unsigned int __status = INTDisableInterrupts(); {
|
||||||
|
#define ATOMIC_BLOCK_END } INTRestoreInterrupts(__status);
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_STM32F2) // Particle Photon with firmware-develop
|
||||||
|
#define ATOMIC_BLOCK_START { int __prev = HAL_disable_irq();
|
||||||
|
#define ATOMIC_BLOCK_END HAL_enable_irq(__prev); }
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_ESP8266)
|
||||||
|
// See hardware/esp8266/2.0.0/cores/esp8266/Arduino.h
|
||||||
|
#define ATOMIC_BLOCK_START { uint32_t __savedPS = xt_rsil(15);
|
||||||
|
#define ATOMIC_BLOCK_END xt_wsr_ps(__savedPS);}
|
||||||
|
#else
|
||||||
|
// TO BE DONE:
|
||||||
|
#define ATOMIC_BLOCK_START
|
||||||
|
#define ATOMIC_BLOCK_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// Try to be compatible with systems that support yield() and multitasking
|
||||||
|
// instead of spin-loops
|
||||||
|
// Recent Arduino IDE or Teensy 3 has yield()
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO && ARDUINO >= 155 && !defined(RH_PLATFORM_ATTINY)) || (TEENSYDUINO && defined(__MK20DX128__))
|
||||||
|
#define YIELD yield();
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_ESP8266)
|
||||||
|
// ESP8266 also hash it
|
||||||
|
#if defined(MONGOOSE_OS)
|
||||||
|
extern void yield(void);
|
||||||
|
#endif
|
||||||
|
#define YIELD yield();
|
||||||
|
#else
|
||||||
|
#define YIELD
|
||||||
|
#endif
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// digitalPinToInterrupt is not available prior to Arduino 1.5.6 and 1.0.6
|
||||||
|
// See http://arduino.cc/en/Reference/attachInterrupt
|
||||||
|
#ifndef NOT_AN_INTERRUPT
|
||||||
|
#define NOT_AN_INTERRUPT -1
|
||||||
|
#endif
|
||||||
|
#ifndef digitalPinToInterrupt
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && !defined(__arm__)
|
||||||
|
|
||||||
|
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||||
|
// Arduino Mega, Mega ADK, Mega Pro
|
||||||
|
// 2->0, 3->1, 21->2, 20->3, 19->4, 18->5
|
||||||
|
#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : ((p) >= 18 && (p) <= 21 ? 23 - (p) : NOT_AN_INTERRUPT)))
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
|
||||||
|
// Arduino 1284 and 1284P - See Manicbug and Optiboot
|
||||||
|
// 10->0, 11->1, 2->2
|
||||||
|
#define digitalPinToInterrupt(p) ((p) == 10 ? 0 : ((p) == 11 ? 1 : ((p) == 2 ? 2 : NOT_AN_INTERRUPT)))
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega32U4__)
|
||||||
|
// Leonardo, Yun, Micro, Pro Micro, Flora, Esplora
|
||||||
|
// 3->0, 2->1, 0->2, 1->3, 7->4
|
||||||
|
#define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT)))))
|
||||||
|
|
||||||
|
#else
|
||||||
|
// All other arduino except Due:
|
||||||
|
// Serial Arduino, Extreme, NG, BT, Uno, Diecimila, Duemilanove, Nano, Menta, Pro, Mini 04, Fio, LilyPad, Ethernet etc
|
||||||
|
// 2->0, 3->1
|
||||||
|
#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : NOT_AN_INTERRUPT))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif (RH_PLATFORM == RH_PLATFORM_UNO32) || (RH_PLATFORM == RH_PLATFORM_CHIPKIT_CORE)
|
||||||
|
// Hmmm, this is correct for Uno32, but what about other boards on ChipKIT Core?
|
||||||
|
#define digitalPinToInterrupt(p) ((p) == 38 ? 0 : ((p) == 2 ? 1 : ((p) == 7 ? 2 : ((p) == 8 ? 3 : ((p) == 735 ? 4 : NOT_AN_INTERRUPT)))))
|
||||||
|
|
||||||
|
#else
|
||||||
|
// Everything else (including Due and Teensy) interrupt number the same as the interrupt pin number
|
||||||
|
#define digitalPinToInterrupt(p) (p)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// On some platforms, attachInterrupt() takes a pin number, not an interrupt number
|
||||||
|
#if (RH_PLATFORM == RH_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_SAM_DUE))
|
||||||
|
#define RH_ATTACHINTERRUPT_TAKES_PIN_NUMBER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Slave select pin, some platforms such as ATTiny do not define it.
|
||||||
|
#ifndef SS
|
||||||
|
#define SS 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// These defs cause trouble on some versions of Arduino
|
||||||
|
#undef abs
|
||||||
|
#undef round
|
||||||
|
#undef double
|
||||||
|
|
||||||
|
// Sigh: there is no widespread adoption of htons and friends in the base code, only in some WiFi headers etc
|
||||||
|
// that have a lot of excess baggage
|
||||||
|
#if RH_PLATFORM != RH_PLATFORM_UNIX && !defined(htons)
|
||||||
|
// #ifndef htons
|
||||||
|
// These predefined macros availble on modern GCC compilers
|
||||||
|
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||||
|
// Atmel processors
|
||||||
|
#define htons(x) ( ((x)<<8) | (((x)>>8)&0xFF) )
|
||||||
|
#define ntohs(x) htons(x)
|
||||||
|
#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
|
||||||
|
((x)<< 8 & 0x00FF0000UL) | \
|
||||||
|
((x)>> 8 & 0x0000FF00UL) | \
|
||||||
|
((x)>>24 & 0x000000FFUL) )
|
||||||
|
#define ntohl(x) htonl(x)
|
||||||
|
|
||||||
|
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
// Others
|
||||||
|
#define htons(x) (x)
|
||||||
|
#define ntohs(x) (x)
|
||||||
|
#define htonl(x) (x)
|
||||||
|
#define ntohl(x) (x)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Dont know how to define htons and friends for this processor"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This is the address that indicates a broadcast
|
||||||
|
#define RH_BROADCAST_ADDRESS 0xff
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef clockMsg_h_included
|
||||||
|
#define clockMsg_h_included
|
||||||
|
|
||||||
|
struct clockMsg_s {
|
||||||
|
uint8_t msgType;
|
||||||
|
uint8_t day;
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minute;
|
||||||
|
uint8_t second;
|
||||||
|
uint16_t millisecond;
|
||||||
|
};
|
||||||
|
#define msgType_Clock 'c'
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef config_h_included
|
||||||
|
#define config_h_included
|
||||||
|
|
||||||
|
#define PIN_NRF24_CSN 2
|
||||||
|
#define PIN_NRF24_CE 15
|
||||||
|
|
||||||
|
// communication protocol definitions
|
||||||
|
#define nRF_Channel 1
|
||||||
|
#define THIS_ADRESS 0 // uint8_t address of this node
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,295 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2014-2017 Cesanta Software Limited
|
||||||
|
* All rights reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "RadioHead/RH_NRF24.h"
|
||||||
|
#include "RadioHead/RHDatagram.h"
|
||||||
|
// #include <Fonts/Picopixel.h>
|
||||||
|
#include <Fonts/Org_01.h>
|
||||||
|
#include <Fonts/TomThumb.h>
|
||||||
|
#include <Fonts/FreeMonoBold9pt7b.h>
|
||||||
|
|
||||||
|
#include "common/cs_dbg.h"
|
||||||
|
#include "mgos.h"
|
||||||
|
#include "mgos_app.h"
|
||||||
|
#include "mgos_timers.h"
|
||||||
|
#include "mgos_shadow.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "clockMsg.h"
|
||||||
|
|
||||||
|
static Adafruit_SSD1306 *display = nullptr;
|
||||||
|
|
||||||
|
#define LOGO16_GLCD_HEIGHT 16
|
||||||
|
#define LOGO16_GLCD_WIDTH 16
|
||||||
|
static const unsigned char PROGMEM logo16_glcd_bmp[] =
|
||||||
|
{ 0x00, 0xc0, // B00000000, B11000000,
|
||||||
|
0x01, 0xc0, // B00000001, B11000000,
|
||||||
|
0x01, 0xc0, // B00000001, B11000000,
|
||||||
|
0x03, 0xe0, // B00000011, B11100000,
|
||||||
|
0xf3, 0xe0, // B11110011, B11100000,
|
||||||
|
0xfe, 0xf8, // B11111110, B11111000,
|
||||||
|
0x7e, 0xff, // B01111110, B11111111,
|
||||||
|
0x33, 0x9f, // B00110011, B10011111,
|
||||||
|
0x1f, 0xfc, // B00011111, B11111100,
|
||||||
|
0x0d, 0x70, // B00001101, B01110000,
|
||||||
|
0x1b, 0xa0, // B00011011, B10100000,
|
||||||
|
0x3f, 0xe0, // B00111111, B11100000,
|
||||||
|
0x3f, 0xf0, // B00111111, B11110000,
|
||||||
|
0x7c, 0xf0, // B01111100, B11110000,
|
||||||
|
0x70, 0x70, // B01110000, B01110000,
|
||||||
|
0x00, 0x30 // B00000000, B00110000
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(CHECK_DISPLAY_RESOLUTION)
|
||||||
|
#if (SSD1306_LCDHEIGHT != 32)
|
||||||
|
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Singleton instance of the radio driver
|
||||||
|
RH_NRF24 nrf24(PIN_NRF24_CSN, PIN_NRF24_CE);
|
||||||
|
|
||||||
|
// Address RH_BROADCAST_ADDRESS can be used for broadcasts as destination
|
||||||
|
RHDatagram Datagram(nrf24, THIS_ADRESS);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void setSmallTextSize(void) { display->setFont(&TomThumb); }
|
||||||
|
static uint8_t getSmallTextHeight() { return TomThumb.yAdvance; }
|
||||||
|
//static uint8_t getSmallTextCharsPerLine() { return 39; }
|
||||||
|
|
||||||
|
static void setNormalTextSize(void) { display->setFont(&Org_01); }
|
||||||
|
static uint8_t getNormalTextHeight() { return Org_01.yAdvance; }
|
||||||
|
//static uint8_t getNormalTextCharsPerLine() { return 24; }
|
||||||
|
|
||||||
|
static void setLargeTextSize(void) { display->setFont(&FreeMonoBold9pt7b); }
|
||||||
|
//static uint8_t getLargeTextHeight() { return FreeMonoBold9pt7b.yAdvance; }
|
||||||
|
//static uint8_t getLargeTextCharsPerLine() { return 12; }
|
||||||
|
static void timer_cb(void *arg);
|
||||||
|
static void fastclockRF_receive_cb(void *arg);
|
||||||
|
static void fastclockRF_send_cb(void *arg);
|
||||||
|
static void initFastclockRF_cb(void *arg);
|
||||||
|
|
||||||
|
static void displayBegin(boolean reset) {
|
||||||
|
//display->begin(SSD1306_SWITCHCAPVCC, 0x3C, reset /* reset */);
|
||||||
|
display->begin(); (void) reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initDisplay_cb(void *arg) {
|
||||||
|
static int step=0;
|
||||||
|
(void) arg;
|
||||||
|
|
||||||
|
display = new Adafruit_SSD1306(16 /* RST GPIO */, Adafruit_SSD1306::RES_128_32);
|
||||||
|
|
||||||
|
switch (step) {
|
||||||
|
case 0:
|
||||||
|
displayBegin(true);
|
||||||
|
display->display();
|
||||||
|
LOG(LL_INFO, ("*** Display initialized, height=%d, width=%d", display->height(), display->width()));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
displayBegin(false);
|
||||||
|
display->clearDisplay();
|
||||||
|
display->drawPixel(10, 10, WHITE);
|
||||||
|
display->drawPixel(12, 12, WHITE);
|
||||||
|
display->drawPixel(14, 14, WHITE);
|
||||||
|
display->drawPixel(16, 16, WHITE);
|
||||||
|
display->display();
|
||||||
|
LOG(LL_INFO, ("*** Pixel drawn, height=%d, width=%d", display->height(), display->width()));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
displayBegin(false);
|
||||||
|
display->drawLine(0, display->height()-1, display->width(), display->height()/2, WHITE);
|
||||||
|
display->drawCircle(display->width()-20, display->height()-10, 10, WHITE);
|
||||||
|
display->display();
|
||||||
|
LOG(LL_INFO, ("*** Line & Circle drawn"));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
displayBegin(false);
|
||||||
|
display->drawBitmap(60, 0, logo16_glcd_bmp, 16, 16, 1);
|
||||||
|
display->display();
|
||||||
|
LOG(LL_INFO, ("*** Icon/Bitmap drawn"));
|
||||||
|
break;
|
||||||
|
default: // do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++step;
|
||||||
|
if (step <= 3) mgos_set_timer(600 /* ms */, false /* repeat */, initDisplay_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup(void) {
|
||||||
|
LOG(LL_INFO, ("*** Setup started"));
|
||||||
|
LOG(LL_INFO, ("*** Setting timer"));
|
||||||
|
mgos_set_timer(2000 /* ms */, false /* repeat */, initDisplay_cb, NULL);
|
||||||
|
mgos_set_timer(5000 /* ms */, true /* repeat */, timer_cb, NULL);
|
||||||
|
mgos_set_timer(7000 /* ms */, false /* repeat */, initFastclockRF_cb, NULL);
|
||||||
|
LOG(LL_INFO, ("*** Setup done"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_dashboard(int hour, int minute) {
|
||||||
|
display->clearDisplay();
|
||||||
|
display->setTextColor(WHITE, BLACK);
|
||||||
|
|
||||||
|
// ***** clock name *****
|
||||||
|
setNormalTextSize();
|
||||||
|
display->setCursor(0, getNormalTextHeight()-1);
|
||||||
|
display->printf("N-RE");
|
||||||
|
|
||||||
|
// ****** speed *****
|
||||||
|
setNormalTextSize();
|
||||||
|
display->setCursor(55, 2*getNormalTextHeight()-1);
|
||||||
|
display->printf("1:3,5");
|
||||||
|
|
||||||
|
// ***** time *****
|
||||||
|
setLargeTextSize();
|
||||||
|
display->setCursor(0, display->height()-1);
|
||||||
|
display->printf("%02d:%02d", hour, minute);
|
||||||
|
|
||||||
|
// ***** halt/go *****
|
||||||
|
setSmallTextSize();
|
||||||
|
display->setTextColor(BLACK, WHITE);
|
||||||
|
display->fillRect(55, display->height() - 2*getSmallTextHeight()-3, 5*4+2, getSmallTextHeight(), WHITE);
|
||||||
|
display->setCursor(57, display->height() - getSmallTextHeight()-3); display->printf("HALT");
|
||||||
|
display->setTextColor(WHITE, BLACK);
|
||||||
|
|
||||||
|
// **** weekday *****
|
||||||
|
setSmallTextSize();
|
||||||
|
display->setCursor(60, display->height()); display->printf("Mo");
|
||||||
|
|
||||||
|
// ***** # of clients *****
|
||||||
|
setSmallTextSize();
|
||||||
|
display->setCursor(55, getNormalTextHeight()); display->printf("7 -->");
|
||||||
|
|
||||||
|
// ***** client list *****
|
||||||
|
display->writeFastVLine(79, 0, display->height(), WHITE);
|
||||||
|
for (int i=0; i<5; ++i) {
|
||||||
|
display->setTextColor(BLACK, WHITE);
|
||||||
|
display->fillRect(81, i * getSmallTextHeight(), 3*3+1, getSmallTextHeight(), WHITE);
|
||||||
|
display->setCursor(82, (i+1) * getSmallTextHeight());
|
||||||
|
display->printf("%02d", i);
|
||||||
|
display->setTextColor(WHITE, BLACK);
|
||||||
|
display->setCursor(82+3*3+1+1, (i+1) * getSmallTextHeight());
|
||||||
|
display->printf("Client-%d", i);
|
||||||
|
}
|
||||||
|
display->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static long lastSentTimeTick = 0;
|
||||||
|
static long msPerModelSecond = 1000; // 500 = real time
|
||||||
|
static struct clock_s {
|
||||||
|
uint8_t day;
|
||||||
|
uint8_t hour;
|
||||||
|
uint8_t minute;
|
||||||
|
uint8_t second;
|
||||||
|
uint16_t millisecond;
|
||||||
|
} fastclock;
|
||||||
|
|
||||||
|
static void incrementClockByMilliseconds(int amount) {
|
||||||
|
fastclock.millisecond += amount;
|
||||||
|
if (fastclock.millisecond >= 1000) {
|
||||||
|
fastclock.millisecond -= 1000;
|
||||||
|
fastclock.second++;
|
||||||
|
if (fastclock.second >= 60) {
|
||||||
|
fastclock.second -= 60;
|
||||||
|
fastclock.minute++;
|
||||||
|
if (fastclock.minute >= 60) {
|
||||||
|
fastclock.minute -= 60;
|
||||||
|
fastclock.hour++;
|
||||||
|
if (fastclock.hour >= 24) {
|
||||||
|
fastclock.hour -=24;
|
||||||
|
fastclock.day++;
|
||||||
|
if (fastclock.day >= 7) {
|
||||||
|
fastclock.day -= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG(LL_INFO, ("*** new clock: %02d:%02d:%02d.%03d day %d", fastclock.hour, fastclock.minute, fastclock.second, fastclock.millisecond, fastclock.day));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fastclockRF_receive_cb(void *arg) {
|
||||||
|
(void) arg;
|
||||||
|
|
||||||
|
// check for incoming messages
|
||||||
|
if (Datagram.available())
|
||||||
|
{
|
||||||
|
// Should be a message for us now
|
||||||
|
uint8_t buf[RH_MAX_MESSAGE_LEN];
|
||||||
|
uint8_t len = sizeof(buf);
|
||||||
|
uint8_t from, to, id, flags;
|
||||||
|
|
||||||
|
if (Datagram.recvfrom(buf, &len, &from, &to, &id, &flags)) {
|
||||||
|
LOG(LL_INFO, ("got request: %s", (char*)buf));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(LL_INFO, ("*** Datagram.recvfrom failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct clockMsg_s clockMsg;
|
||||||
|
|
||||||
|
static void fastclockRF_send_cb(void *arg) {
|
||||||
|
(void) arg;
|
||||||
|
|
||||||
|
clockMsg.msgType = msgType_Clock;
|
||||||
|
clockMsg.hour = fastclock.hour;
|
||||||
|
clockMsg.minute = fastclock.minute;
|
||||||
|
clockMsg.second = fastclock.second;
|
||||||
|
|
||||||
|
// send clock info as a broadcast message
|
||||||
|
|
||||||
|
LOG(LL_INFO, ("*** Sending clock packet (broadcast)"));
|
||||||
|
if (Datagram.sendto((uint8_t *) &clockMsg, sizeof(clockMsg), RH_BROADCAST_ADDRESS)) {
|
||||||
|
LOG(LL_INFO, ("%02d:%02d:%02d - Sent new clock tick", fastclock.hour, fastclock.minute, fastclock.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timeTick_cb(void *arg) {
|
||||||
|
(void) arg;
|
||||||
|
long newTimeTick = millis();
|
||||||
|
int fastclockTimeAdvance = (newTimeTick - lastSentTimeTick) / msPerModelSecond;
|
||||||
|
|
||||||
|
incrementClockByMilliseconds(fastclockTimeAdvance);
|
||||||
|
lastSentTimeTick += fastclockTimeAdvance * msPerModelSecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initFastclockRF_cb(void *arg) {
|
||||||
|
(void) arg;
|
||||||
|
LOG(LL_INFO, ("*** Setting up RF"));
|
||||||
|
lastSentTimeTick = millis();
|
||||||
|
fastclock.day = 0;
|
||||||
|
fastclock.hour = 0;
|
||||||
|
fastclock.minute = 0;
|
||||||
|
fastclock.second = 0;
|
||||||
|
fastclock.millisecond = 0;
|
||||||
|
mgos_set_timer(200 /* ms */, true /* repeat */, fastclockRF_receive_cb, NULL);
|
||||||
|
mgos_set_timer(500 /* ms */, true /* repeat */, timeTick_cb, NULL);
|
||||||
|
mgos_set_timer(4000 /* ms */, true /* repeat */, fastclockRF_send_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timer_cb(void *arg) {
|
||||||
|
// static int hour = 0, minute = 0;
|
||||||
|
show_dashboard(fastclock.hour, fastclock.minute);
|
||||||
|
LOG(LL_INFO, ("%02d:%02d", fastclock.hour, fastclock.minute));
|
||||||
|
/*
|
||||||
|
minute++;
|
||||||
|
if (minute >= 60) { hour++; minute=0; }
|
||||||
|
if (hour >= 24) { hour=0; }
|
||||||
|
*/
|
||||||
|
(void) arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void loop(void) {
|
||||||
|
/* do not use loop(), use timers instead; otherwise the watchdog timer reboots your device */
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
defaultDEVICE_ID=fad7795eff2d760d27b7bc59
|
||||||
|
defaultTOKEN=90a408ad822cce2e194a7cdc
|
||||||
|
defaultWIFI_SSID=Pinguin
|
||||||
|
defaultWIFI_PWD=PaulchenAufmKlo34
|
||||||
|
|
||||||
|
read -p 'Device ID [default=$defaultDEVICE_ID]: ' DEVICE_ID
|
||||||
|
: ${DEVICE_ID:=$defaultDEVICE_ID}
|
||||||
|
echo $DEVICE_ID
|
||||||
|
read -p 'Mongoose Dash Token: [default=$defaultTOKEN]' TOKEN
|
||||||
|
: ${TOKEN:=$defaultTOKEN}
|
||||||
|
echo $TOKEN
|
||||||
|
read -p 'Wifi SSID [default=$defaultSSID]: ' WIFI_SSID
|
||||||
|
: ${WIFI_SSID:=$defaultWIFI_SSID}
|
||||||
|
echo $WIFI_SSID
|
||||||
|
read -sp 'Wifi Password: ' WIFI_PWD
|
||||||
|
: ${WIFI_PWD:=$defaultWIFI_PWD}
|
||||||
|
echo "*****"
|
||||||
|
echo ""
|
||||||
|
echo "Setting device id to $DEVICE_ID"
|
||||||
|
mos config-set --no-reboot device.id="$DEVICE_ID"
|
||||||
|
echo "Setting token to $TOKEN"
|
||||||
|
mos config-set --no-reboot dash.token="$TOKEN"
|
||||||
|
echo "Enabling DASH"
|
||||||
|
mos config-set --no-reboot dash.enable=true
|
||||||
|
echo "Setting ACLs"
|
||||||
|
mos config-set --no-reboot conf_acl=wifi.*,device.*,dash.enable
|
||||||
|
echo "Setting WIFI"
|
||||||
|
mos wifi "$WIFI_SSID" "$WIFI_PWD"
|
||||||
|
echo "Making changes permanent"
|
||||||
|
mos call FS.Rename '{"src": "conf9.json", "dst": "conf5.json"}'
|
||||||
|
echo "Rebooting device"
|
||||||
|
mos call Sys.Reboot
|
||||||
|
|
Loading…
Reference in New Issue