Add Automatic Transmission Control (ATC)
This commit is contained in:
parent
1925ac4f62
commit
bd50fa29df
|
|
@ -1,13 +1,17 @@
|
|||
// Sample RFM69 receiver/gateway sketch, with ACK and optional encryption
|
||||
// Sample RFM69 receiver/gateway sketch, with ACK and optional encryption, and Automatic Transmission Control
|
||||
// Passes through any wireless received messages to the serial port & responds to ACKs
|
||||
// It also looks for an onboard FLASH chip, if present
|
||||
// Library and code by Felix Rusu - felix@lowpowerlab.com
|
||||
// Get the RFM69 and SPIFlash library at: https://github.com/LowPowerLab/
|
||||
// RFM69 library and sample code by Felix Rusu - http://LowPowerLab.com/contact
|
||||
// Copyright Felix Rusu (2015)
|
||||
|
||||
#include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69
|
||||
#include <SPI.h>
|
||||
#include <RFM69_ATC.h>//get it here: https://www.github.com/lowpowerlab/rfm69
|
||||
#include <SPI.h> //comes with Arduino IDE (www.arduino.cc)
|
||||
#include <SPIFlash.h> //get it here: https://www.github.com/lowpowerlab/spiflash
|
||||
|
||||
//*********************************************************************************************
|
||||
//************ IMPORTANT SETTINGS - YOU MUST CHANGE/CONFIGURE TO FIT YOUR HARDWARE *************
|
||||
//*********************************************************************************************
|
||||
#define NODEID 1 //unique for each node on same network
|
||||
#define NETWORKID 100 //the same on all nodes that talk to each other
|
||||
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
|
||||
|
|
@ -16,6 +20,9 @@
|
|||
//#define FREQUENCY RF69_915MHZ
|
||||
#define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
|
||||
//#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
|
||||
#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
|
||||
//*********************************************************************************************
|
||||
|
||||
#define SERIAL_BAUD 115200
|
||||
|
||||
#ifdef __AVR_ATmega1284P__
|
||||
|
|
@ -26,7 +33,12 @@
|
|||
#define FLASH_SS 8 // and FLASH SS on D8
|
||||
#endif
|
||||
|
||||
RFM69 radio;
|
||||
#ifdef ENABLE_ATC
|
||||
RFM69_ATC radio;
|
||||
#else
|
||||
RFM69 radio;
|
||||
#endif
|
||||
|
||||
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for 4mbit Windbond chip (W25X40CL)
|
||||
bool promiscuousMode = false; //set to 'true' to sniff all packets on the same network
|
||||
|
||||
|
|
@ -39,7 +51,7 @@ void setup() {
|
|||
#endif
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
radio.promiscuous(promiscuousMode);
|
||||
//radio.setFrequency(919000000);
|
||||
//radio.setFrequency(919000000); //set frequency to some custom frequency
|
||||
char buff[50];
|
||||
sprintf(buff, "\nListening at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
|
||||
Serial.println(buff);
|
||||
|
|
@ -61,10 +73,13 @@ void setup() {
|
|||
// Serial.print(MAC[i], HEX);
|
||||
// Serial.print(' ');
|
||||
//}
|
||||
|
||||
}
|
||||
else
|
||||
Serial.println("SPI Flash Init FAIL! (is chip present?)");
|
||||
Serial.println("SPI Flash MEM not found (is chip soldered?)...");
|
||||
|
||||
#ifdef ENABLE_ATC
|
||||
Serial.println("RFM69_ATC Enabled (Auto Transmission Control)");
|
||||
#endif
|
||||
}
|
||||
|
||||
byte ackCount=0;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
// Make sure you adjust the settings in the configuration section below !!!
|
||||
|
||||
// **********************************************************************************
|
||||
// Copyright Felix Rusu, LowPowerLab.com
|
||||
// Library and code by Felix Rusu - felix@lowpowerlab.com
|
||||
// Copyright Felix Rusu of LowPowerLab.com, 2015-11-10
|
||||
// RFM69 library and sample code by Felix Rusu - lowpowerlab.com/contact
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
|
|
@ -34,14 +34,14 @@
|
|||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
// **********************************************************************************
|
||||
|
||||
#include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69
|
||||
#include <SPI.h>
|
||||
#include <RFM69_ATC.h>//get it here: https://www.github.com/lowpowerlab/rfm69
|
||||
#include <SPI.h> //comes with Arduino IDE (www.arduino.cc)
|
||||
#include <LowPower.h> //get library from: https://github.com/lowpowerlab/lowpower
|
||||
//writeup here: http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/
|
||||
|
||||
//*********************************************************************************************
|
||||
// *********** IMPORTANT SETTINGS - YOU MUST CHANGE/ONFIGURE TO FIT YOUR HARDWARE *************
|
||||
//************ IMPORTANT SETTINGS - YOU MUST CHANGE/CONFIGURE TO FIT YOUR HARDWARE *************
|
||||
//*********************************************************************************************
|
||||
#define NODEID 88 //unique for each node on same network
|
||||
#define NETWORKID 100 //the same on all nodes that talk to each other
|
||||
|
|
@ -50,17 +50,19 @@
|
|||
//#define FREQUENCY RF69_433MHZ
|
||||
//#define FREQUENCY RF69_868MHZ
|
||||
#define FREQUENCY RF69_915MHZ
|
||||
#define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
|
||||
//#define IS_RFM69HW //uncomment only for RFM69HW! Remove/comment if you have RFM69W!
|
||||
#define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
|
||||
#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
|
||||
//*********************************************************************************************
|
||||
|
||||
#define ACK_TIME 30 // max # of ms to wait for an ack
|
||||
#define ACK_TIME 30 // max # of ms to wait for an ack
|
||||
#define ONBOARDLED 9 // Moteinos have LEDs on D9
|
||||
#define EXTLED 5 // MotionOLEDMote has an external LED on D5
|
||||
#define BATT_MONITOR A7 // Sense VBAT_COND signal (when powered externally should read ~3.25v/3.3v (1000-1023), when external power is cutoff it should start reading around 2.85v/3.3v * 1023 ~= 880 (ratio given by 10k+4.7K divider from VBAT_COND = 1.47 multiplier)
|
||||
#define BATT_CYCLES 30 //read and report battery voltage every this many wakeup cycles (ex 30cycles * 8sec sleep = 240sec/4min)
|
||||
#define MOTIONPIN 1 //hardware interrupt 1 (D3)
|
||||
|
||||
#define LED 5 // MotionOLEDMote has an external LED on D5
|
||||
#define MOTION_PIN 3 // D3
|
||||
#define MOTION_IRQ 1 // hardware interrupt 1 (D3) - where motion sensors OUTput is connected, this will generate an interrupt every time there is MOTION
|
||||
#define BATT_MONITOR A7 // Sense VBAT_COND signal (when powered externally should read ~3.25v/3.3v (1000-1023), when external power is cutoff it should start reading around 2.85v/3.3v * 1023 ~= 883 (ratio given by 10k+4.7K divider from VBAT_COND = 1.47 multiplier)
|
||||
#define BATT_CYCLES 450 // read and report battery voltage every this many sleep cycles (ex 30cycles * 8sec sleep = 240sec/4min). For 450 cyclesyou would get ~1 hour intervals
|
||||
#define BATT_FORMULA(reading) reading * 0.00322 * 1.51 // >>> fine tune this parameter to match your voltage when fully charged
|
||||
// details on how this works: https://lowpowerlab.com/forum/index.php/topic,1206.0.html
|
||||
//#define SERIAL_EN //comment this out when deploying to an installed SM to save a few KB of sketch size
|
||||
#define SERIAL_BAUD 115200
|
||||
#ifdef SERIAL_EN
|
||||
|
|
@ -71,13 +73,20 @@
|
|||
#define DEBUGln(input);
|
||||
#endif
|
||||
|
||||
RFM69 radio;
|
||||
#ifdef ENABLE_ATC
|
||||
RFM69_ATC radio;
|
||||
#else
|
||||
RFM69 radio;
|
||||
#endif
|
||||
volatile boolean motionDetected=false;
|
||||
float batteryVolts = 5;
|
||||
char* BATstr="BAT:5.00v";
|
||||
char BATstr[20]; //longest battery voltage reading message = 9chars
|
||||
char sendBuf[32];
|
||||
byte sendLen;
|
||||
|
||||
void motionIRQ(void);
|
||||
void checkBattery(void);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
radio.initialize(FREQUENCY,NODEID,NETWORKID);
|
||||
|
|
@ -85,18 +94,33 @@ void setup() {
|
|||
radio.setHighPower(); //uncomment only for RFM69HW!
|
||||
#endif
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
pinMode(MOTIONPIN, INPUT);
|
||||
attachInterrupt(MOTIONPIN, motionIRQ, RISING);
|
||||
|
||||
//Auto Transmission Control - dials down transmit power to save battery (-100 is the noise floor, -90 is still pretty good)
|
||||
//For indoor nodes that are pretty static and at pretty stable temperatures (like a MotionMote) -90dBm is quite safe
|
||||
//For more variable nodes that can expect to move or experience larger temp drifts a lower margin like -70 to -80 would probably be better
|
||||
//Always test your ATC mote in the edge cases in your own environment to ensure ATC will perform as you expect
|
||||
#ifdef ENABLE_ATC
|
||||
radio.enableAutoPower(-90);
|
||||
#endif
|
||||
|
||||
pinMode(MOTION_PIN, INPUT);
|
||||
attachInterrupt(MOTION_IRQ, motionIRQ, RISING);
|
||||
char buff[50];
|
||||
sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
|
||||
DEBUGln(buff);
|
||||
pinMode(ONBOARDLED, OUTPUT);
|
||||
pinMode(EXTLED, OUTPUT);
|
||||
pinMode(LED, OUTPUT);
|
||||
radio.sendWithRetry(GATEWAYID, "START", 5);
|
||||
|
||||
#ifdef ENABLE_ATC
|
||||
DEBUGln("RFM69_ATC Enabled (Auto Transmission Control)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void motionIRQ()
|
||||
{
|
||||
motionDetected=true;
|
||||
DEBUGln("IRQ");
|
||||
}
|
||||
|
||||
byte batteryReportCycles=0;
|
||||
|
|
@ -104,7 +128,7 @@ void loop() {
|
|||
checkBattery();
|
||||
if (motionDetected)
|
||||
{
|
||||
digitalWrite(EXTLED, HIGH);
|
||||
digitalWrite(LED, HIGH);
|
||||
sprintf(sendBuf, "MOTION BAT:%sv", BATstr);
|
||||
sendLen = strlen(sendBuf);
|
||||
|
||||
|
|
@ -120,13 +144,13 @@ void loop() {
|
|||
DEBUGln(BATstr);
|
||||
|
||||
radio.sleep();
|
||||
digitalWrite(EXTLED, LOW);
|
||||
digitalWrite(LED, LOW);
|
||||
}
|
||||
else if (batteryReportCycles == BATT_CYCLES)
|
||||
{
|
||||
sprintf(sendBuf, "BAT:%sv", BATstr);
|
||||
sendLen = strlen(sendBuf);
|
||||
radio.send(GATEWAYID, sendBuf, sendLen);
|
||||
radio.sendWithRetry(GATEWAYID, sendBuf, sendLen);
|
||||
radio.sleep();
|
||||
batteryReportCycles=0;
|
||||
}
|
||||
|
|
@ -143,7 +167,7 @@ void checkBattery()
|
|||
unsigned int readings=0;
|
||||
for (byte i=0; i<10; i++) //take 10 samples, and average
|
||||
readings+=analogRead(BATT_MONITOR);
|
||||
batteryVolts = (readings / 10.0) * 0.00322 * 1.42;
|
||||
batteryVolts = BATT_FORMULA(readings / 10.0);
|
||||
dtostrf(batteryVolts, 3,2, BATstr); //update the BATStr which gets sent every BATT_CYCLES or along with the MOTION message
|
||||
cycleCount = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
// Sample RFM69 sender/node sketch, with ACK and optional encryption
|
||||
// Sample RFM69 sender/node sketch, with ACK and optional encryption, and Automatic Transmission Control
|
||||
// Sends periodic messages of increasing length to gateway (id=1)
|
||||
// It also looks for an onboard FLASH chip, if present
|
||||
// Library and code by Felix Rusu - felix@lowpowerlab.com
|
||||
// Get the RFM69 and SPIFlash library at: https://github.com/LowPowerLab/
|
||||
// RFM69 library and sample code by Felix Rusu - http://LowPowerLab.com/contact
|
||||
// Copyright Felix Rusu (2015)
|
||||
|
||||
#include <RFM69.h> //get it here: https://www.github.com/lowpowerlab/rfm69
|
||||
#include <RFM69_ATC.h>//get it here: https://www.github.com/lowpowerlab/rfm69
|
||||
#include <SPI.h>
|
||||
#include <SPIFlash.h> //get it here: https://www.github.com/lowpowerlab/spiflash
|
||||
|
||||
#define NODEID 2 //unique for each node on same network
|
||||
#define NETWORKID 100 //the same on all nodes that talk to each other
|
||||
//*********************************************************************************************
|
||||
//************ IMPORTANT SETTINGS - YOU MUST CHANGE/CONFIGURE TO FIT YOUR HARDWARE *************
|
||||
//*********************************************************************************************
|
||||
#define NODEID 2 //must be unique for each node on same network (range up to 254, 255 is used for broadcast)
|
||||
#define NETWORKID 100 //the same on all nodes that talk to each other (range up to 255)
|
||||
#define GATEWAYID 1
|
||||
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
|
||||
#define FREQUENCY RF69_433MHZ
|
||||
|
|
@ -17,6 +21,9 @@
|
|||
//#define FREQUENCY RF69_915MHZ
|
||||
#define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
|
||||
//#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
|
||||
#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
|
||||
//*********************************************************************************************
|
||||
|
||||
#ifdef __AVR_ATmega1284P__
|
||||
#define LED 15 // Moteino MEGAs have LEDs on D15
|
||||
#define FLASH_SS 23 // and FLASH SS on D23
|
||||
|
|
@ -33,7 +40,12 @@ char buff[20];
|
|||
byte sendSize=0;
|
||||
boolean requestACK = false;
|
||||
SPIFlash flash(FLASH_SS, 0xEF30); //EF30 for 4mbit Windbond chip (W25X40CL)
|
||||
RFM69 radio;
|
||||
|
||||
#ifdef ENABLE_ATC
|
||||
RFM69_ATC radio;
|
||||
#else
|
||||
RFM69 radio;
|
||||
#endif
|
||||
|
||||
void setup() {
|
||||
Serial.begin(SERIAL_BAUD);
|
||||
|
|
@ -43,6 +55,15 @@ void setup() {
|
|||
#endif
|
||||
radio.encrypt(ENCRYPTKEY);
|
||||
//radio.setFrequency(919000000); //set frequency to some custom frequency
|
||||
|
||||
//Auto Transmission Control - dials down transmit power to save battery (-100 is the noise floor, -90 is still pretty good)
|
||||
//For indoor nodes that are pretty static and at pretty stable temperatures (like a MotionMote) -90dBm is quite safe
|
||||
//For more variable nodes that can expect to move or experience larger temp drifts a lower margin like -70 to -80 would probably be better
|
||||
//Always test your ATC mote in the edge cases in your own environment to ensure ATC will perform as you expect
|
||||
#ifdef ENABLE_ATC
|
||||
radio.enableAutoPower(-70);
|
||||
#endif
|
||||
|
||||
char buff[50];
|
||||
sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
|
||||
Serial.println(buff);
|
||||
|
|
@ -59,7 +80,11 @@ void setup() {
|
|||
Serial.println();
|
||||
}
|
||||
else
|
||||
Serial.println("SPI Flash Init FAIL! (is chip present?)");
|
||||
Serial.println("SPI Flash MEM not found (is chip soldered?)...");
|
||||
|
||||
#ifdef ENABLE_ATC
|
||||
Serial.println("RFM69_ATC Enabled (Auto Transmission Control)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
long lastPeriod = 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,254 @@
|
|||
// **********************************************************************************
|
||||
// Automatic Transmit Power Control class derived from RFM69 library.
|
||||
// Discussion and details in this forum post: https://lowpowerlab.com/forum/index.php/topic,688.0.html
|
||||
// **********************************************************************************
|
||||
// Copyright Thomas Studwell (2014,2015)
|
||||
// Adjustments by Felix Rusu, LowPowerLab.com
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
// **********************************************************************************
|
||||
#include <RFM69_ATC.h>
|
||||
#include <RFM69.h> // include the RFM69 library files as well
|
||||
#include <RFM69registers.h>
|
||||
#include <SPI.h>
|
||||
|
||||
volatile uint8_t RFM69_ATC::ACK_RSSI_REQUESTED; // new type of flag on ACK_REQUEST
|
||||
|
||||
//=============================================================================
|
||||
// initialize() - some extra initialization before calling base class
|
||||
//=============================================================================
|
||||
bool RFM69_ATC::initialize(uint8_t freqBand, uint8_t nodeID, uint8_t networkID) {
|
||||
_targetRSSI = 0; // TomWS1: default to disabled
|
||||
_ackRSSI = 0; // TomWS1: no existing response at init time
|
||||
ACK_RSSI_REQUESTED = 0; // TomWS1: init to none
|
||||
//_powerBoost = false; // TomWS1: require someone to explicitly turn boost on!
|
||||
_transmitLevel = 31; // TomWS1: match default value in PA Level register
|
||||
return RFM69::initialize(freqBand, nodeID, networkID); // use base class to initialize most everything
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// setMode() - got to set updated transmit power level before switching to TX mode
|
||||
//=============================================================================
|
||||
void RFM69_ATC::setMode(uint8_t newMode) {
|
||||
if (newMode == _mode) return;
|
||||
//_powerBoost = (_transmitLevel >= 50); // this needs to be set before changing mode just in case setHighPowerRegs is called
|
||||
RFM69::setMode(newMode); // call base class first
|
||||
|
||||
if (newMode == RF69_MODE_TX) // special stuff if switching to TX mode
|
||||
{
|
||||
if (_targetRSSI) setPowerLevel(_transmitLevel); // TomWS1: apply most recent transmit level if auto power
|
||||
//if (_isRFM69HW) setHighPowerRegs(true);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// sendAck() - updated to call new sendFrame with additional parameters
|
||||
//=============================================================================
|
||||
// should be called immediately after reception in case sender wants ACK
|
||||
void RFM69_ATC::sendACK(const void* buffer, uint8_t bufferSize) {
|
||||
ACK_REQUESTED = 0; // TomWS1 added to make sure we don't end up in a timing race and infinite loop sending Acks
|
||||
uint8_t sender = SENDERID;
|
||||
int16_t _RSSI = RSSI; // save payload received RSSI value
|
||||
bool sendRSSI = ACK_RSSI_REQUESTED;
|
||||
writeReg(REG_PACKETCONFIG2, (readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART); // avoid RX deadlocks
|
||||
uint32_t now = millis();
|
||||
while (!canSend() && millis() - now < RF69_CSMA_LIMIT_MS) receiveDone();
|
||||
SENDERID = sender; // TomWS1: Restore SenderID after it gets wiped out by receiveDone()
|
||||
sendFrame(sender, buffer, bufferSize, false, true, sendRSSI, _RSSI); // TomWS1: Special override on sendFrame with extra params
|
||||
RSSI = _RSSI; // restore payload RSSI
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// sendFrame() - the basic version is used to match the RFM69 prototype so we can extend it
|
||||
//=============================================================================
|
||||
// this sendFrame is generally called by the internal RFM69 functions. Simply transfer to our modified version.
|
||||
void RFM69_ATC::sendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize, bool requestACK, bool sendACK) {
|
||||
sendFrame(toAddress, buffer, bufferSize, requestACK, sendACK, false, 0); // default sendFrame
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// sendFrame() - the new one with additional parameters. This packages recv'd RSSI with the packet, if required.
|
||||
//=============================================================================
|
||||
void RFM69_ATC::sendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize, bool requestACK, bool sendACK, bool sendRSSI, int16_t lastRSSI) {
|
||||
setMode(RF69_MODE_STANDBY); // turn off receiver to prevent reception while filling fifo
|
||||
while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady
|
||||
writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_00); // DIO0 is "Packet Sent"
|
||||
|
||||
bufferSize += (sendACK && sendRSSI)?1:0; // if sending ACK_RSSI then increase data size by 1
|
||||
if (bufferSize > RF69_MAX_DATA_LEN) bufferSize = RF69_MAX_DATA_LEN;
|
||||
|
||||
// write to FIFO
|
||||
select();
|
||||
SPI.transfer(REG_FIFO | 0x80);
|
||||
SPI.transfer(bufferSize + 3);
|
||||
SPI.transfer(toAddress);
|
||||
SPI.transfer(_address);
|
||||
|
||||
// control byte
|
||||
if (sendACK) { // TomWS1: adding logic to return ACK_RSSI if requested
|
||||
SPI.transfer(RFM69_CTL_SENDACK | (sendRSSI?RFM69_CTL_RESERVE1:0)); // TomWS1 TODO: Replace with EXT1
|
||||
if (sendRSSI) {
|
||||
SPI.transfer(abs(lastRSSI)); //RSSI dBm is negative expected between [-100 .. -20], convert to positive and pass along as single extra header byte
|
||||
bufferSize -=1; // account for the extra ACK-RSSI 'data' byte
|
||||
}
|
||||
}
|
||||
else if (requestACK) { // TODO: add logic to request ackRSSI with ACK - this is when both ends of a transmission would dial power down. May not work well for gateways in multi node networks
|
||||
SPI.transfer(_targetRSSI ? RFM69_CTL_REQACK | RFM69_CTL_RESERVE1 : RFM69_CTL_REQACK);
|
||||
}
|
||||
else SPI.transfer(0x00);
|
||||
|
||||
for (uint8_t i = 0; i < bufferSize; i++)
|
||||
SPI.transfer(((uint8_t*) buffer)[i]);
|
||||
unselect();
|
||||
|
||||
// no need to wait for transmit mode to be ready since its handled by the radio
|
||||
setMode(RF69_MODE_TX);
|
||||
uint32_t txStart = millis();
|
||||
while (digitalRead(_interruptPin) == 0 && millis() - txStart < RF69_TX_LIMIT_MS); // wait for DIO0 to turn HIGH signalling transmission finish
|
||||
//while (readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PACKETSENT == 0x00); // wait for ModeReady
|
||||
setMode(RF69_MODE_STANDBY);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// interruptHook() - gets called by the base class interrupt handler right after the header is fetched.
|
||||
//=============================================================================
|
||||
void RFM69_ATC::interruptHook(uint8_t CTLbyte) {
|
||||
ACK_RSSI_REQUESTED = CTLbyte & RFM69_CTL_RESERVE1; // TomWS1: extract the ACK RSSI request bit (could potentially merge with ACK_REQUESTED)
|
||||
// TomWS1: now see if this was an ACK with an ACK_RSSI response
|
||||
if (ACK_RECEIVED && ACK_RSSI_REQUESTED) {
|
||||
// the next two bytes contain the ACK_RSSI (assuming the datalength is valid)
|
||||
if (DATALEN >= 1) {
|
||||
_ackRSSI = -1 * SPI.transfer(0); //rssi was sent as single byte positive value, get the real value by * -1
|
||||
DATALEN -= 1; // and compensate data length accordingly
|
||||
// TomWS1: Now dither transmitLevel value (register update occurs later when transmitting);
|
||||
if (_targetRSSI != 0) {
|
||||
// if (_isRFM69HW) {
|
||||
// if (_ackRSSI < _targetRSSI && _transmitLevel < 51) _transmitLevel++;
|
||||
// else if (_ackRSSI > _targetRSSI && _transmitLevel > 32) _transmitLevel--;
|
||||
// } else {
|
||||
if (_ackRSSI < _targetRSSI && _transmitLevel < 31) { _transmitLevel++; /*Serial.println("\n ======= _transmitLevel ++ ======");*/ }
|
||||
else if (_ackRSSI > _targetRSSI && _transmitLevel > 0) { _transmitLevel--; /*Serial.println("\n ======= _transmitLevel -- ======");*/ }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// receiveBegin() - need to clear out our flag before calling base class.
|
||||
//=============================================================================
|
||||
void RFM69_ATC::receiveBegin() {
|
||||
ACK_RSSI_REQUESTED = 0;
|
||||
RFM69::receiveBegin();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// setPowerLevel() - outright replacement for base class. Provides finer granularity for RFM69HW.
|
||||
//=============================================================================
|
||||
// set output power: 0=min, 31=max (for RFM69W or RFM69CW), 0-31 or 32->51 for RFM69HW (see below)
|
||||
// this results in a "weaker" transmitted signal, and directly results in a lower RSSI at the receiver
|
||||
// allows power level selections above 31 (as in original base RFM69 lib) & selects appropriate PA based on the value
|
||||
// more discussion and details in this forum post: https://lowpowerlab.com/forum/index.php/topic,688.0.html
|
||||
// void RFM69_ATC::setPowerLevel(uint8_t powerLevel) {
|
||||
// _transmitLevel = powerLevel; // save this for later in case we do auto power control.
|
||||
// _powerBoost = (powerLevel >= 50);
|
||||
// if (!_isRFM69HW || powerLevel < 32) { // use original code without change
|
||||
// _powerLevel = powerLevel;
|
||||
// writeReg(REG_PALEVEL, (readReg(REG_PALEVEL) & 0xE0) | (_powerLevel > 31 ? 31 : _powerLevel));
|
||||
// } else {
|
||||
// // the allowable range of power level value, if >31 is: 32 -> 51, where...
|
||||
// // 32->47 use PA2 only and sets powerLevel register 0-15,
|
||||
// // 48->49 uses both PAs, and sets powerLevel register 14-15,
|
||||
// // 50->51 uses both PAs, sets powerBoost, and sets powerLevel register 14-15.
|
||||
// if (powerLevel < 48) {
|
||||
// _powerLevel = powerLevel & 0x0f; // just use 4 lower bits when in high power mode
|
||||
// _PA_Reg = 0x20;
|
||||
// } else {
|
||||
// _PA_Reg = 0x60;
|
||||
// if (powerLevel < 50) {
|
||||
// _powerLevel = powerLevel - 34; // leaves 14-15
|
||||
// } else {
|
||||
// if (powerLevel > 51)
|
||||
// powerLevel = 51; // saturate
|
||||
// _powerLevel = powerLevel - 36; // leaves 14-15
|
||||
// }
|
||||
// }
|
||||
// writeReg(REG_OCP, (_PA_Reg==0x60) ? RF_OCP_OFF : RF_OCP_ON);
|
||||
// writeReg(REG_PALEVEL, _powerLevel | _PA_Reg);
|
||||
// }
|
||||
// }
|
||||
|
||||
//=============================================================================
|
||||
// setHighPower() - only set High power bits on RFM69HW IFF the power level is set to MAX. Otherwise it is kept off.
|
||||
//=============================================================================
|
||||
// void RFM69_ATC::setHighPower(bool onOff, byte PA_ctl) {
|
||||
// _isRFM69HW = onOff;
|
||||
// writeReg(REG_OCP, (_isRFM69HW && PA_ctl==0x60) ? RF_OCP_OFF : RF_OCP_ON);
|
||||
// if (_isRFM69HW) { //turning ON based on module type
|
||||
// _powerLevel = readReg(REG_PALEVEL) & 0x1F; // make sure internal value matches reg
|
||||
// _powerBoost = (PA_ctl == 0x60);
|
||||
// _PA_Reg = PA_ctl;
|
||||
// writeReg(REG_PALEVEL, _powerLevel | PA_ctl ); //TomWS1: enable selected P1 & P2 amplifier stages
|
||||
// }
|
||||
// else {
|
||||
// _PA_Reg = RF_PALEVEL_PA0_ON; // TomWS1: save to reflect register value
|
||||
// writeReg(REG_PALEVEL, RF_PALEVEL_PA0_ON | RF_PALEVEL_PA1_OFF | RF_PALEVEL_PA2_OFF | _powerLevel); //enable P0 only
|
||||
// }
|
||||
// }
|
||||
|
||||
//=============================================================================
|
||||
// ditto from above.
|
||||
//=============================================================================
|
||||
// void RFM69_ATC::setHighPowerRegs(bool onOff) {
|
||||
// if ((0x60 != (readReg(REG_PALEVEL) & 0xe0)) || !_powerBoost) // TomWS1: only set to high power if we are using both PAs... and boost range is requested.
|
||||
// onOff = false;
|
||||
// writeReg(REG_TESTPA1, onOff ? 0x5D : 0x55);
|
||||
// writeReg(REG_TESTPA2, onOff ? 0x7C : 0x70);
|
||||
// }
|
||||
|
||||
//=============================================================================
|
||||
// enableAutoPower() - call with target RSSI, use 0 to disable (default), any other value with turn on autotransmit control.
|
||||
//=============================================================================
|
||||
// TomWS1: New methods to address autoPower control
|
||||
void RFM69_ATC::enableAutoPower(int targetRSSI){ // TomWS1: New method to enable/disable auto Power control
|
||||
_targetRSSI = targetRSSI; // no logic here, just set the value (if non-zero, then enabled), caller's responsibility to use a reasonable value
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// getAckRSSI() - returns the RSSI value ack'd by the far end.
|
||||
//=============================================================================
|
||||
int RFM69_ATC::getAckRSSI(void){ // TomWS1: New method to retrieve the ack'd RSSI (if any)
|
||||
return (_targetRSSI==0?0:_ackRSSI);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// setLNA() - used for power level testing.
|
||||
//=============================================================================
|
||||
byte RFM69_ATC::setLNA(byte newReg) { // TomWS1: New method used to disable LNA AGC for testing purposes
|
||||
byte oldReg;
|
||||
oldReg = readReg(REG_LNA);
|
||||
writeReg(REG_LNA, ((newReg & 7) | (oldReg & ~7))); // just control the LNA Gain bits for now
|
||||
return oldReg; // return the original value in case we need to restore it
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// **********************************************************************************
|
||||
// Automatic Transmit Power Control class derived from RFM69 library.
|
||||
// Discussion and details in this forum post: https://lowpowerlab.com/forum/index.php/topic,688.0.html
|
||||
// **********************************************************************************
|
||||
// Copyright Thomas Studwell (2014,2015)
|
||||
// Adjustments by Felix Rusu, LowPowerLab.com
|
||||
// **********************************************************************************
|
||||
// License
|
||||
// **********************************************************************************
|
||||
// This program 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.
|
||||
//
|
||||
// This program 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 this program.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// Licence can be viewed at
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
//
|
||||
// Please maintain this license information along with authorship
|
||||
// and copyright notices in any redistribution of this code
|
||||
// **********************************************************************************
|
||||
#ifndef RFM69_ATC_h
|
||||
#define RFM69_ATC_h
|
||||
#include <RFM69.h>
|
||||
|
||||
#define RFM69_CTL_RESERVE1 0x20
|
||||
|
||||
class RFM69_ATC: public RFM69 {
|
||||
public:
|
||||
static volatile uint8_t ACK_RSSI_REQUESTED; // new flag in CTL byte to request RSSI with ACK (could potentially be merged with ACK_REQUESTED)
|
||||
|
||||
RFM69_ATC(uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, uint8_t interruptNum=RF69_IRQ_NUM) :
|
||||
RFM69(slaveSelectPin, interruptPin, isRFM69HW, interruptNum) {
|
||||
}
|
||||
|
||||
bool initialize(uint8_t freqBand, uint8_t ID, uint8_t networkID=1);
|
||||
void sendACK(const void* buffer = "", uint8_t bufferSize=0);
|
||||
//void setHighPower(bool onOFF=true, uint8_t PA_ctl=0x60); //have to call it after initialize for RFM69HW
|
||||
//void setPowerLevel(uint8_t level); // reduce/increase transmit power level
|
||||
void enableAutoPower(int targetRSSI=-90); // TWS: New method to enable/disable auto Power control
|
||||
void setMode(uint8_t mode); // TWS: moved from protected to try to build block()/unblock() wrapper
|
||||
|
||||
int16_t getAckRSSI(void); // TWS: New method to retrieve the ack'd RSSI (if any)
|
||||
uint8_t setLNA(uint8_t newReg); // TWS: function to control LNA reg for power testing purposes
|
||||
int16_t _targetRSSI; // if non-zero then this is the desired end point RSSI for our transmission
|
||||
uint8_t _transmitLevel; // saved powerLevel in case we do auto power adjustment, this value gets dithered
|
||||
|
||||
protected:
|
||||
void interruptHook(uint8_t CTLbyte);
|
||||
void sendFrame(uint8_t toAddress, const void* buffer, uint8_t size, bool requestACK=false, bool sendACK=false); // Need this one to match the RFM69 prototype.
|
||||
void sendFrame(uint8_t toAddress, const void* buffer, uint8_t size, bool requestACK, bool sendACK, bool sendRSSI, int lastRSSI);
|
||||
void receiveBegin();
|
||||
//void setHighPowerRegs(bool onOff);
|
||||
|
||||
int16_t _ackRSSI; // this contains the RSSI our destination Ack'd back to us (if we enabledAutoPower)
|
||||
//bool _powerBoost; // this controls whether we need to turn on the highpower regs based on the setPowerLevel input
|
||||
uint8_t _PA_Reg; // saved and derived PA control bits so we don't have to spend time reading back from SPI port
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
RFM69 KEYWORD2
|
||||
RFM69_ATC KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
|
|
@ -38,6 +39,7 @@ setPowerLevel KEYWORD2
|
|||
readTemperature KEYWORD2
|
||||
rcCalibration KEYWORD2
|
||||
readAllRegs KEYWORD2
|
||||
enableAutoPower KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue