mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-30 11:36:13 +01:00
174 lines
6.4 KiB
C++
174 lines
6.4 KiB
C++
/*
|
|
* © 2021, Neil McKechnie. All rights reserved.
|
|
*
|
|
* This file is part of DCC++EX API
|
|
*
|
|
* This is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* It is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* nRF24 default mode of operation:
|
|
* Channel: 108
|
|
* Bit rate: 2MHz
|
|
* CRC: 16-bit
|
|
* Power Level: High
|
|
*
|
|
* The node number is used as the first byte of the nRF24's 5-byte address
|
|
* field. Number 255 is treated as a multicast address. All stations listen on
|
|
* their own address and on the multicast address.
|
|
*
|
|
* The nRF24 device receives and acknowledges data packets autonomously.
|
|
* Therefore, this driver just needs to detect when a packet is received and
|
|
* read and process its contents. The time to read the packet is under 200us
|
|
* typically.
|
|
*
|
|
* The nRF24 is also capable of autonomously sending packets, processing
|
|
* acknowledgements, and generating retries. The driver writes the packet to
|
|
* the device and then waits for notification of completion (success, or retries
|
|
* exceeded) through the device's registers. Similarly, the time to write a
|
|
* packet is under 200us and, if we don't wait for the completion, we can allow
|
|
* the processor to do other things while the transmission is in progress.
|
|
* A write with ack can complete in under 600us, plus the time of turning the
|
|
* receiver off and on.
|
|
*
|
|
* Usage:
|
|
* Net_RF24 *rf24Driver = new Net_RF24(48, 49);
|
|
* Network<Net_RF24>::create(4000, NUMREMOTEPINS(rpins), 1, rpins, rf24Driver);
|
|
*
|
|
* The nRF24 device has to be connected to the hardware MISO, MOSI, SCK and CS pins of the
|
|
* microcontroller; in addition, the CE and CSN pins on the nRF24 are connected to
|
|
* two pins (e.g. 48 and 49 above).
|
|
*
|
|
*/
|
|
|
|
#ifndef NET_RF24_H
|
|
#define NET_RF24_H
|
|
|
|
#include "IO_Network.h"
|
|
#include "DIAG.h"
|
|
|
|
class Net_RF24 : public RF24 {
|
|
|
|
private:
|
|
// pins must be arduino GPIO pins, not extender pins or HAL pins.
|
|
int _cePin;
|
|
int _csnPin;
|
|
// Number of the current node (1-254)
|
|
uint8_t _thisNode;
|
|
// 5-byte nRF24L01 address. First byte will contain the node number (0-254) or 255 for broadcast
|
|
byte _address[5];
|
|
bool _sendInProgress;
|
|
|
|
|
|
public:
|
|
// Constructor performs static initialisation of the device object
|
|
Net_RF24 (int cePin, int csnPin) {
|
|
_cePin = cePin;
|
|
_csnPin = csnPin;
|
|
// Initialise with an arbitrary address. The first byte will contain
|
|
// the node number.
|
|
_address[0] = 0x00;
|
|
_address[1] = 0xCC;
|
|
_address[2] = 0xEE;
|
|
_address[3] = 0xEE;
|
|
_address[4] = 0xCC;
|
|
}
|
|
|
|
// Perform dynamic initialisation of the device
|
|
bool begin(uint8_t thisNode) {
|
|
if (RF24::begin(_cePin, _csnPin)) {
|
|
// Device initialisation OK, set up parameters
|
|
RF24::setDataRate(RF24_2MBPS);
|
|
RF24::setPALevel(RF24_PA_HIGH);
|
|
RF24::setChannel(108);
|
|
RF24::enableDynamicPayloads(); // variable length packets
|
|
RF24::setAutoAck(true); // required for acks to work
|
|
RF24::enableDynamicAck(); // required for multicast to work
|
|
RF24::setRetries(1, 5); // Retry time=1*250+250us=500us, count=5.
|
|
|
|
_thisNode = thisNode;
|
|
// Set to listen on the address 255
|
|
_address[0] = 255;
|
|
RF24::openReadingPipe(1, _address);
|
|
// Also allow receives on own node address
|
|
_address[0] = _thisNode;
|
|
RF24::openReadingPipe(2, _address);
|
|
RF24::startListening();
|
|
_sendInProgress = false;
|
|
return true;
|
|
} else {
|
|
// Error in initialising
|
|
DIAG(F("nRF24L01 Failed to initialise"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check if there is a received packet ready for reading.
|
|
bool available() {
|
|
return RF24::available();
|
|
}
|
|
|
|
// Read next packet from the device, and return the number of bytes
|
|
// that were read.
|
|
uint8_t read(uint8_t buffer[], uint8_t size) {
|
|
uint8_t len = RF24::getDynamicPayloadSize();
|
|
RF24::read(buffer, size);
|
|
return len;
|
|
}
|
|
|
|
// Wrapper functions for RF24 send functions. If node=255, then
|
|
// the packet is to be sent as a multicast without acknowledgements.
|
|
// The multicast message takes ~400us. A further 260us is required to turn
|
|
// the receiver off and on for the transmission, totalling 660us.
|
|
// If the node is not 255, then the packet will be sent directly to the
|
|
// addressed node, with acknowledgement requested. If no acknowledgement is
|
|
// received, then the device will retry up to the defined maximum number of
|
|
// retries. This will take longer than a multicast. For example, with
|
|
// setRetries(1,3) the timeout is 500us and a maximum of 3 retries are
|
|
// carried out, so the operation will take as much as 2.26 milliseconds if
|
|
// the node in question is not responding, and as little as 890us if the
|
|
// ack is received immediately (including turning receiver on/off).
|
|
//
|
|
bool sendCommand(uint8_t node, const uint8_t buffer[], uint8_t len) {
|
|
_address[0] = node;
|
|
openWritingPipe(_address);
|
|
// We have to stop the receiver before we can transmit.
|
|
RF24::stopListening();
|
|
// Copy the message into the radio and start the transmitter.
|
|
// Multicast (no ack expected) if destination node is 255.
|
|
bool ok = RF24::writeFast(buffer, len, (node==255));
|
|
// We will poll the radio later on to see when the transmit queue
|
|
// has emptied. When that happens, we will go back to receive mode.
|
|
// This prevents txStandBy() from blocking while the transmission
|
|
// is in progress.
|
|
_sendInProgress = true;;
|
|
return ok;
|
|
}
|
|
|
|
// The following function should be called regularly to ensure that the
|
|
// device goes back into listening mode when a transmission is not
|
|
// in progress. (The nRF24 is a half-duplex device and cannot be in
|
|
// transmit mode and receive mode at the same time.)
|
|
void loop() {
|
|
bool completed = RF24::isWriteFinished();
|
|
if (_sendInProgress && completed) {
|
|
_sendInProgress = false;
|
|
RF24::txStandBy();
|
|
RF24::startListening();
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
#endif //NET_RF24_H
|