RFM95: Updates and optimizations (#1063)

This commit is contained in:
tekka
2018-02-18 21:05:25 +01:00
committed by Patrick Fallberg
parent 54fb250ab2
commit 0bc3ac9cb3
6 changed files with 314 additions and 284 deletions

View File

@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2017 Sensnology AB
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -18,7 +18,11 @@
*
* Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley <mikem@airspayce.com>
* Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html
* RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017 Olivier Mauti <olivier@mysensors.org>
*
* RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2018 Olivier Mauti <olivier@mysensors.org>
*
* Definitions for HopeRF LoRa radios:
* http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
*
*/
@@ -31,41 +35,33 @@
#define RFM95_DEBUG(x,...) //!< DEBUG null
#endif
#if defined (SREG) // To identify AVR vs EP8266
uint8_t _SREG; // Used to save and restore the SREG values in SPI transactions
#endif
#if defined (SPCR) && defined (SPSR)
uint8_t _SPCR; //!< _SPCR
uint8_t _SPSR; //!< _SPSR
#endif
#if defined(LINUX_SPI_BCM)
// SPI RX and TX buffers (max packet len + 1 byte for the command)
uint8_t spi_rxbuff[RFM95_MAX_PACKET_LEN + 1];
uint8_t spi_txbuff[RFM95_MAX_PACKET_LEN + 1];
#endif
volatile rfm95_internal_t RFM95; //!< internal variables
rfm95_internal_t RFM95; //!< internal variables
volatile uint8_t rfm95_irq; //<! rfm95 irq flag
LOCAL void RFM95_csn(const bool level)
{
hwDigitalWrite(MY_RFM95_CS_PIN, level);
}
LOCAL uint8_t RFM95_spiMultiByteTransfer(const uint8_t cmd, uint8_t* buf, uint8_t len,
LOCAL uint8_t RFM95_spiMultiByteTransfer(const uint8_t cmd, uint8_t *buf, uint8_t len,
const bool aReadMode)
{
uint8_t status;
uint8_t* current = buf;
uint8_t *current = buf;
#if !defined(MY_SOFTSPI) && defined(SPI_HAS_TRANSACTION)
RFM95_SPI.beginTransaction(SPISettings(MY_RFM95_SPI_SPEED, MY_RFM95_SPI_DATA_ORDER,
MY_RFM95_SPI_DATA_MODE));
#endif
RFM95_csn(LOW);
#if defined(LINUX_SPI_BCM)
uint8_t * prx = spi_rxbuff;
uint8_t * ptx = spi_txbuff;
uint8_t *prx = spi_rxbuff;
uint8_t *ptx = spi_txbuff;
uint8_t size = len + 1; // Add register value to transmit buffer
*ptx++ = cmd;
@@ -122,25 +118,51 @@ LOCAL uint8_t RFM95_RAW_writeByteRegister(const uint8_t address, uint8_t value)
return RFM95_spiMultiByteTransfer(address, &value, 1, false);
}
// macros, saves space
#define RFM95_readReg(__reg) RFM95_RAW_readByteRegister(__reg & RFM95_READ_REGISTER)
#define RFM95_writeReg(__reg, __value) RFM95_RAW_writeByteRegister((__reg | RFM95_WRITE_REGISTER), __value )
#define RFM95_burstReadReg(__reg, __buf, __len) RFM95_spiMultiByteTransfer( __reg & RFM95_READ_REGISTER, (uint8_t*)__buf, __len, true )
#define RFM95_burstWriteReg(__reg, __buf, __len) RFM95_spiMultiByteTransfer( __reg | RFM95_WRITE_REGISTER, (uint8_t*)__buf, __len, false )
// helper functions
LOCAL inline uint8_t RFM95_readReg(const uint8_t reg)
{
return RFM95_RAW_readByteRegister(reg & RFM95_READ_REGISTER);
}
LOCAL inline uint8_t RFM95_writeReg(const uint8_t reg, const uint8_t value)
{
return RFM95_RAW_writeByteRegister(reg | RFM95_WRITE_REGISTER, value);
}
LOCAL inline uint8_t RFM95_burstReadReg(const uint8_t reg, void *buf, uint8_t len)
{
return RFM95_spiMultiByteTransfer(reg & RFM95_READ_REGISTER, (uint8_t *)buf, len, true);
}
LOCAL inline uint8_t RFM95_burstWriteReg(const uint8_t reg, const void *buf, uint8_t len)
{
return RFM95_spiMultiByteTransfer(reg | RFM95_WRITE_REGISTER, (uint8_t *)buf, len, false);
}
LOCAL inline rfm95_RSSI_t RFM95_RSSItoInternal(const int16_t externalRSSI)
{
return static_cast<rfm95_RSSI_t>(externalRSSI + RFM95_RSSI_OFFSET);
}
LOCAL inline int16_t RFM95_internalToRSSI(const rfm95_RSSI_t internalRSSI)
{
return static_cast<int16_t>(internalRSSI - RFM95_RSSI_OFFSET);
}
LOCAL bool RFM95_initialise(const uint32_t frequencyHz)
{
RFM95_DEBUG(PSTR("RFM95:INIT\n"));
// reset radio module if rst pin defined
// power pin, if defined
#if defined(MY_RFM95_POWER_PIN)
hwPinMode(MY_RFM95_POWER_PIN, OUTPUT);
#endif
RFM95_powerUp();
// reset radio module if rst pin defined
#if defined(MY_RFM95_RST_PIN)
hwPinMode(MY_RFM95_RST_PIN, OUTPUT);
hwDigitalWrite(MY_RFM95_RST_PIN, LOW);
// 100uS
delayMicroseconds(100);
delayMicroseconds(RFM95_POWERUP_DELAY_MS);
hwDigitalWrite(MY_RFM95_RST_PIN, HIGH);
// wait until chip ready
delay(5);
@@ -168,16 +190,20 @@ LOCAL bool RFM95_initialise(const uint32_t frequencyHz)
RFM95_SPI.begin();
// Set LoRa mode (during sleep mode)
RFM95_writeReg(RFM95_REG_01_OP_MODE, RFM95_MODE_SLEEP | RFM95_LONG_RANGE_MODE);
(void)RFM95_writeReg(RFM95_REG_01_OP_MODE, RFM95_MODE_SLEEP | RFM95_LONG_RANGE_MODE);
delay(10); // Wait for sleep mode to take over
if (RFM95_readReg(RFM95_REG_01_OP_MODE) != (RFM95_MODE_SLEEP | RFM95_LONG_RANGE_MODE)) {
return false; // No device present?
}
// TCXO init, if present
#if defined(MY_RFM95_TCXO)
RFM95_enableTCXO();
#else
(void)RFM95_enableTCXO;
#endif
// Set up FIFO, 256 bytes: LoRa max message 64 bytes, set half RX half TX (default)
RFM95_writeReg(RFM95_REG_0F_FIFO_RX_BASE_ADDR, RFM95_RX_FIFO_ADDR);
RFM95_writeReg(RFM95_REG_0E_FIFO_TX_BASE_ADDR, RFM95_TX_FIFO_ADDR);
RFM95_writeReg(RFM95_REG_23_MAX_PAYLOAD_LENGTH, RFM95_MAX_PACKET_LEN);
(void)RFM95_writeReg(RFM95_REG_0F_FIFO_RX_BASE_ADDR, RFM95_RX_FIFO_ADDR);
(void)RFM95_writeReg(RFM95_REG_0E_FIFO_TX_BASE_ADDR, RFM95_TX_FIFO_ADDR);
(void)RFM95_writeReg(RFM95_REG_23_MAX_PAYLOAD_LENGTH, RFM95_MAX_PACKET_LEN);
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY);
const rfm95_modemConfig_t configuration = { MY_RFM95_MODEM_CONFIGRUATION };
@@ -185,64 +211,77 @@ LOCAL bool RFM95_initialise(const uint32_t frequencyHz)
RFM95_setPreambleLength(RFM95_PREAMBLE_LENGTH);
RFM95_setFrequency(frequencyHz);
(void)RFM95_setTxPowerLevel(MY_RFM95_TX_POWER_DBM);
// IRQ
hwPinMode(MY_RFM95_IRQ_PIN, INPUT);
#if defined(SPI_HAS_TRANSACTION) && !defined(ESP8266) && !defined(MY_SOFTSPI)
RFM95_SPI.usingInterrupt(MY_RFM95_IRQ_NUM);
#endif
if (!RFM95_sanityCheck()) {
RFM95_DEBUG(
PSTR("!RFM95:INIT:SANCHK FAIL\n")); // sanity check failed, check wiring or replace module
// sanity check failed, check wiring or replace module
RFM95_DEBUG(PSTR("!RFM95:INIT:SANCHK FAIL\n"));
return false;
}
attachInterrupt(digitalPinToInterrupt(MY_RFM95_IRQ_PIN), RFM95_interruptHandler, RISING);
// IRQ
rfm95_irq = false;
hwPinMode(MY_RFM95_IRQ_PIN, INPUT);
attachInterrupt(MY_RFM95_IRQ_NUM, RFM95_interruptHandler, RISING);
return true;
}
// RxDone, TxDone, CADDone is mapped to DI0
LOCAL void RFM95_interruptHandler(void)
{
// Read the interrupt register
const uint8_t irqFlags = RFM95_readReg(RFM95_REG_12_IRQ_FLAGS);
if (RFM95.radioMode == RFM95_RADIO_MODE_RX &&
(irqFlags & (RFM95_RX_TIMEOUT | RFM95_PAYLOAD_CRC_ERROR)) ) {
// CRC error or timeout
// RXcontinuous mode: radio stays in RX mode, clearing IRQ needed
} else if (RFM95.radioMode == RFM95_RADIO_MODE_RX && (irqFlags & RFM95_RX_DONE)) {
// set radio to STDBY (we are in RXcontinuous mode)
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY);
// Have received a packet
//In order to retrieve received data from FIFO the user must ensure that ValidHeader, PayloadCrcError, RxDone and RxTimeout interrupts in the status register RegIrqFlags are not asserted to ensure that packet reception has terminated successfully(i.e.no flags should be set).
const uint8_t bufLen = min(RFM95_readReg(RFM95_REG_13_RX_NB_BYTES), (uint8_t)RFM95_MAX_PACKET_LEN);
if (bufLen >= RFM95_HEADER_LEN) {
// Reset the fifo read ptr to the beginning of the packet
RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_readReg(RFM95_REG_10_FIFO_RX_CURRENT_ADDR));
RFM95_burstReadReg(RFM95_REG_00_FIFO, RFM95.currentPacket.data, bufLen);
RFM95.currentPacket.RSSI = static_cast<rfm95_RSSI_t>(RFM95_readReg(
RFM95_REG_1A_PKT_RSSI_VALUE)); // RSSI of latest packet received
RFM95.currentPacket.SNR = static_cast<rfm95_SNR_t>(RFM95_readReg(RFM95_REG_19_PKT_SNR_VALUE));
RFM95.currentPacket.payloadLen = bufLen - RFM95_HEADER_LEN;
// Message for us
if ((RFM95.currentPacket.header.version >= RFM95_MIN_PACKET_HEADER_VERSION) &&
(RFM95_PROMISCUOUS || RFM95.currentPacket.header.recipient == RFM95.address ||
RFM95.currentPacket.header.recipient == RFM95_BROADCAST_ADDRESS)) {
RFM95.ackReceived = RFM95_getACKReceived(RFM95.currentPacket.header.controlFlags) &&
!RFM95_getACKRequested(RFM95.currentPacket.header.controlFlags);
RFM95.dataReceived = !RFM95.ackReceived;
}
}
// set flag
rfm95_irq = true;
}
// RxDone, TxDone, CADDone is mapped to DI0
LOCAL void RFM95_interruptHandling(void)
{
// read interrupt register
const uint8_t irqFlags = RFM95_readReg(RFM95_REG_12_IRQ_FLAGS);
if (RFM95.radioMode == RFM95_RADIO_MODE_RX && (irqFlags & RFM95_RX_DONE)) {
// RXSingle mode: Radio goes automatically to STDBY after packet received
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY);
// Check CRC flag
if (!(irqFlags & RFM95_PAYLOAD_CRC_ERROR)) {
const uint8_t bufLen = min(RFM95_readReg(RFM95_REG_13_RX_NB_BYTES), (uint8_t)RFM95_MAX_PACKET_LEN);
if (bufLen >= RFM95_HEADER_LEN) {
// Reset the fifo read ptr to the beginning of the packet
(void)RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_readReg(RFM95_REG_10_FIFO_RX_CURRENT_ADDR));
(void)RFM95_burstReadReg(RFM95_REG_00_FIFO, RFM95.currentPacket.data, bufLen);
RFM95.currentPacket.RSSI = static_cast<rfm95_RSSI_t>(RFM95_readReg(
RFM95_REG_1A_PKT_RSSI_VALUE)); // RSSI of latest packet received
RFM95.currentPacket.SNR = static_cast<rfm95_SNR_t>(RFM95_readReg(RFM95_REG_19_PKT_SNR_VALUE));
RFM95.currentPacket.payloadLen = bufLen - RFM95_HEADER_LEN;
if ((RFM95.currentPacket.header.version >= RFM95_MIN_PACKET_HEADER_VERSION) &&
(RFM95_PROMISCUOUS || RFM95.currentPacket.header.recipient == RFM95.address ||
RFM95.currentPacket.header.recipient == RFM95_BROADCAST_ADDRESS)) {
// Message for us
RFM95.ackReceived = RFM95_getACKReceived(RFM95.currentPacket.header.controlFlags) &&
!RFM95_getACKRequested(RFM95.currentPacket.header.controlFlags);
RFM95.dataReceived = !RFM95.ackReceived;
}
}
} else {
// CRC error
RFM95_DEBUG(PSTR("!RFM95:IRH:CRC ERROR\n"));
// FIFO is cleared when switch from STDBY to RX or TX
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX);
}
} else if (RFM95.radioMode == RFM95_RADIO_MODE_TX && (irqFlags & RFM95_TX_DONE) ) {
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY); // change eventually to RX
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX);
} else if (RFM95.radioMode == RFM95_RADIO_MODE_CAD && (irqFlags & RFM95_CAD_DONE) ) {
RFM95.cad = irqFlags & RFM95_CAD_DETECTED;
RFM95.channelActive = irqFlags & RFM95_CAD_DETECTED;
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY);
}
// Clear IRQ flags
RFM95_writeReg(RFM95_REG_12_IRQ_FLAGS, RFM95_CLEAR_IRQ);
}
// Clear all IRQ flags
RFM95_writeReg(RFM95_REG_12_IRQ_FLAGS, 0xFF);
LOCAL void RFM95_handler(void)
{
if (rfm95_irq) {
rfm95_irq = false;
RFM95_interruptHandling();
}
}
LOCAL bool RFM95_available(void)
@@ -253,73 +292,68 @@ LOCAL bool RFM95_available(void)
} else if (RFM95.radioMode == RFM95_RADIO_MODE_TX) {
return false;
} else if (RFM95.radioMode != RFM95_RADIO_MODE_RX) {
// we are not in RX, and no data received
// we are not in RX, not CAD, and no data received
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX);
}
return false;
}
LOCAL uint8_t RFM95_recv(uint8_t* buf, const uint8_t maxBufSize)
LOCAL uint8_t RFM95_receive(uint8_t *buf, const uint8_t maxBufSize)
{
// atomic
#ifdef SREG
_SREG = SREG;
#endif
noInterrupts();
const uint8_t payloadLen = RFM95.currentPacket.payloadLen < maxBufSize?
RFM95.currentPacket.payloadLen : maxBufSize;
const uint8_t payloadLen = min(RFM95.currentPacket.payloadLen, maxBufSize);
const uint8_t sender = RFM95.currentPacket.header.sender;
const rfm95_sequenceNumber_t sequenceNumber = RFM95.currentPacket.header.sequenceNumber;
const rfm95_controlFlags_t controlFlags = RFM95.currentPacket.header.controlFlags;
const rfm95_RSSI_t RSSI = RFM95.currentPacket.RSSI;
const rfm95_SNR_t SNR = RFM95.currentPacket.SNR;
if (buf != NULL) {
(void)memcpy((void*)buf, (void*)RFM95.currentPacket.payload, payloadLen);
RFM95.dataReceived = false;
(void)memcpy((void *)buf, (void *)&RFM95.currentPacket.payload, payloadLen);
}
#ifdef SREG
SREG = _SREG; // restore interrupts
#endif
interrupts(); // explicitly re-enable interrupts
// clear data flag
RFM95.dataReceived = false;
// ACK handling
if (RFM95_getACKRequested(controlFlags) && !RFM95_getACKReceived(controlFlags)) {
#if defined(MY_GATEWAY_FEATURE) && (F_CPU>16*1000000ul)
// delay for fast GW and slow nodes
delay(50);
#endif
RFM95_sendACK(sender, sequenceNumber, RSSI, SNR);
}
return payloadLen;
}
LOCAL bool RFM95_sendFrame(rfm95_packet_t &packet, const bool increaseSequenceCounter)
LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet, const bool increaseSequenceCounter)
{
const uint8_t finalLen = packet.payloadLen + RFM95_HEADER_LEN;
// Make sure we dont interrupt an outgoing message
(void)RFM95_waitPacketSent();
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY); //==> not needed
// Check channel activity
if (!RFM95_waitCAD()) {
return false;
}
// radio is in STDBY
if (increaseSequenceCounter) {
// increase sequence counter, overflow is ok
RFM95.txSequenceNumber++;
}
packet.header.sequenceNumber = RFM95.txSequenceNumber;
packet->header.sequenceNumber = RFM95.txSequenceNumber;
// Position at the beginning of the TX FIFO
RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_TX_FIFO_ADDR);
(void)RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_TX_FIFO_ADDR);
// write packet
RFM95_burstWriteReg(RFM95_REG_00_FIFO, packet.data, finalLen);
const uint8_t finalLen = packet->payloadLen + RFM95_HEADER_LEN;
(void)RFM95_burstWriteReg(RFM95_REG_00_FIFO, packet->data, finalLen);
// total payload length
RFM95_writeReg(RFM95_REG_22_PAYLOAD_LENGTH, finalLen);
(void)RFM95_writeReg(RFM95_REG_22_PAYLOAD_LENGTH, finalLen);
// send message, if sent, irq fires and radio returns to standby
return RFM95_setRadioMode(RFM95_RADIO_MODE_TX);
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_TX);
// wait until IRQ fires or timeout
const uint32_t startTX_MS = hwMillis();
while (!rfm95_irq &&
(hwMillis() - startTX_MS <
MY_RFM95_TX_TIMEOUT_MS) ) { // make this payload length + bit rate dependend
doYield();
}
return rfm95_irq;
}
LOCAL bool RFM95_send(const uint8_t recipient, uint8_t* data, const uint8_t len,
LOCAL bool RFM95_send(const uint8_t recipient, uint8_t *data, const uint8_t len,
const rfm95_controlFlags_t flags, const bool increaseSequenceCounter)
{
rfm95_packet_t packet;
@@ -328,16 +362,16 @@ LOCAL bool RFM95_send(const uint8_t recipient, uint8_t* data, const uint8_t len,
packet.header.recipient = recipient;
packet.payloadLen = min(len, (uint8_t)RFM95_MAX_PAYLOAD_LEN);
packet.header.controlFlags = flags;
(void)memcpy(&packet.payload, data, packet.payloadLen);
return RFM95_sendFrame(packet, increaseSequenceCounter);
(void)memcpy((void *)&packet.payload, (void *)data, packet.payloadLen);
return RFM95_sendFrame(&packet, increaseSequenceCounter);
}
LOCAL void RFM95_setFrequency(const uint32_t frequencyHz)
{
const uint32_t freqReg = (uint32_t)(frequencyHz / RFM95_FSTEP);
RFM95_writeReg(RFM95_REG_06_FRF_MSB, (freqReg >> 16) & 0xff);
RFM95_writeReg(RFM95_REG_07_FRF_MID, (freqReg >> 8) & 0xff);
RFM95_writeReg(RFM95_REG_08_FRF_LSB, freqReg & 0xff);
(void)RFM95_writeReg(RFM95_REG_06_FRF_MSB, (freqReg >> 16) & 0xff);
(void)RFM95_writeReg(RFM95_REG_07_FRF_MID, (freqReg >> 8) & 0xff);
(void)RFM95_writeReg(RFM95_REG_08_FRF_LSB, freqReg & 0xff);
}
LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel)
@@ -352,13 +386,13 @@ LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel)
// enable DAC, adds 3dBm
// The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm,
// but OutputPower claims it would be 17dBm. Measurements show 20dBm is correct
RFM95_writeReg(RFM95_REG_4D_PA_DAC, RFM95_PA_DAC_ENABLE);
(void)RFM95_writeReg(RFM95_REG_4D_PA_DAC, RFM95_PA_DAC_ENABLE);
val = newPowerLevel - 8;
} else {
RFM95_writeReg(RFM95_REG_4D_PA_DAC, RFM95_PA_DAC_DISABLE);
(void)RFM95_writeReg(RFM95_REG_4D_PA_DAC, RFM95_PA_DAC_DISABLE);
val = newPowerLevel - 5;
}
RFM95_writeReg(RFM95_REG_09_PA_CONFIG, RFM95_PA_SELECT | val);
(void)RFM95_writeReg(RFM95_REG_09_PA_CONFIG, RFM95_PA_SELECT | val);
RFM95_DEBUG(PSTR("RFM95:PTX:LEVEL=%" PRIi8 "\n"), newPowerLevel);
return true;
}
@@ -366,28 +400,26 @@ LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel)
}
#if defined(MY_RFM95_TCXO)
LOCAL void RFM95_enableTCXO(void)
{
while ((RFM95_readReg(RFM95_REG_4B_TCXO) & RFM95_TCXO_TCXO_INPUT_ON) != RFM95_TCXO_TCXO_INPUT_ON) {
RFM95_sleep();
RFM95_writeReg(RFM95_REG_4B_TCXO, (RFM95_readReg(RFM95_REG_4B_TCXO) | RFM95_TCXO_TCXO_INPUT_ON));
(void)RFM95_writeReg(RFM95_REG_4B_TCXO,
(RFM95_readReg(RFM95_REG_4B_TCXO) | RFM95_TCXO_TCXO_INPUT_ON));
}
}
#endif
// Sets registers from a canned modem configuration structure
LOCAL void RFM95_setModemRegisters(const rfm95_modemConfig_t* config)
LOCAL void RFM95_setModemRegisters(const rfm95_modemConfig_t *config)
{
RFM95_writeReg(RFM95_REG_1D_MODEM_CONFIG1, config->reg_1d);
RFM95_writeReg(RFM95_REG_1E_MODEM_CONFIG2, config->reg_1e);
RFM95_writeReg(RFM95_REG_26_MODEM_CONFIG3, config->reg_26);
(void)RFM95_writeReg(RFM95_REG_1D_MODEM_CONFIG1, config->reg_1d);
(void)RFM95_writeReg(RFM95_REG_1E_MODEM_CONFIG2, config->reg_1e);
(void)RFM95_writeReg(RFM95_REG_26_MODEM_CONFIG3, config->reg_26);
}
LOCAL void RFM95_setPreambleLength(const uint16_t preambleLength)
{
RFM95_writeReg(RFM95_REG_20_PREAMBLE_MSB, preambleLength >> 8);
RFM95_writeReg(RFM95_REG_21_PREAMBLE_LSB, preambleLength & 0xff);
(void)RFM95_writeReg(RFM95_REG_20_PREAMBLE_MSB, (preambleLength >> 8) & 0xff);
(void)RFM95_writeReg(RFM95_REG_21_PREAMBLE_LSB, preambleLength & 0xff);
}
LOCAL void RFM95_setAddress(const uint8_t addr)
@@ -413,19 +445,21 @@ LOCAL bool RFM95_setRadioMode(const rfm95_radioMode_t newRadioMode)
regMode = RFM95_MODE_SLEEP;
} else if (newRadioMode == RFM95_RADIO_MODE_CAD) {
regMode = RFM95_MODE_CAD;
RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x80); // Interrupt on CadDone, DIO0
(void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x80); // Interrupt on CadDone, DIO0
} else if (newRadioMode == RFM95_RADIO_MODE_RX) {
RFM95.dataReceived = false;
RFM95.ackReceived = false;
regMode = RFM95_MODE_RXCONTINUOUS;
RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone, DIO0
(void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone, DIO0
(void)RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR,
RFM95_RX_FIFO_ADDR); // set FIFO ptr to beginning of RX FIFO address
} else if (newRadioMode == RFM95_RADIO_MODE_TX) {
regMode = RFM95_MODE_TX;
RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone, DIO0
(void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone, DIO0
} else {
return false;
}
RFM95_writeReg(RFM95_REG_01_OP_MODE, regMode);
(void)RFM95_writeReg(RFM95_REG_01_OP_MODE, regMode);
RFM95.radioMode = newRadioMode;
return true;
@@ -434,6 +468,7 @@ LOCAL bool RFM95_setRadioMode(const rfm95_radioMode_t newRadioMode)
LOCAL void RFM95_powerUp(void)
{
#if defined(MY_RFM95_POWER_PIN)
RFM69_DEBUG(PSTR("RFM95:PWU\n")); // power up radio
hwDigitalWrite(MY_RFM95_POWER_PIN, HIGH);
delay(RFM95_POWERUP_DELAY_MS);
#endif
@@ -441,6 +476,7 @@ LOCAL void RFM95_powerUp(void)
LOCAL void RFM95_powerDown(void)
{
#if defined(MY_RFM95_POWER_PIN)
RFM69_DEBUG(PSTR("RFM95:PWD\n")); // power down radio
hwDigitalWrite(MY_RFM95_POWER_PIN, LOW);
#endif
}
@@ -469,52 +505,53 @@ LOCAL void RFM95_sendACK(const uint8_t recipient, const rfm95_sequenceNumber_t s
ACK.sequenceNumber = sequenceNumber;
ACK.RSSI = RSSI;
ACK.SNR = SNR;
rfm95_controlFlags_t flags = 0x00;
rfm95_controlFlags_t flags = 0u;
RFM95_setACKReceived(flags, true);
RFM95_setACKRSSIReport(flags, true);
(void)RFM95_send(recipient, (uint8_t*)&ACK, sizeof(rfm95_ack_t), flags);
(void)RFM95_send(recipient, (uint8_t *)&ACK, sizeof(rfm95_ack_t), flags);
}
LOCAL bool RFM95_executeATC(const rfm95_RSSI_t currentRSSI, const rfm95_RSSI_t targetRSSI)
{
rfm95_powerLevel_t newPowerLevel = RFM95.powerLevel;
if (RFM95_internalToRSSI(currentRSSI) < RFM95_internalToRSSI(targetRSSI *
(1 + RFM95_ATC_TARGET_RANGE_PERCENT / 100)) &&
RFM95.powerLevel < RFM95_MAX_POWER_LEVEL_DBM) {
const int16_t ownRSSI = RFM95_internalToRSSI(currentRSSI);
const int16_t uRange = RFM95_internalToRSSI(targetRSSI) + RFM95_ATC_TARGET_RANGE_DBM;
const int16_t lRange = RFM95_internalToRSSI(targetRSSI) - RFM95_ATC_TARGET_RANGE_DBM;
if (ownRSSI < lRange && RFM95.powerLevel < RFM95_MAX_POWER_LEVEL_DBM) {
// increase transmitter power
newPowerLevel++;
} else if (RFM95_internalToRSSI(currentRSSI) > RFM95_internalToRSSI(targetRSSI *
(1 - RFM95_ATC_TARGET_RANGE_PERCENT / 100)) &&
RFM95.powerLevel > RFM95_MIN_POWER_LEVEL_DBM) {
} else if (ownRSSI > uRange && RFM95.powerLevel > RFM95_MIN_POWER_LEVEL_DBM) {
// decrease transmitter power
newPowerLevel--;
} else {
// nothing to adjust
return false;
}
RFM95_DEBUG(PSTR("RFM95:ATC:ADJ TXL,cR=%" PRIi16 ",tR=%" PRIi16 ",TXL=%" PRIi8 "\n"),
RFM95_internalToRSSI(currentRSSI),
RFM95_internalToRSSI(targetRSSI), RFM95.powerLevel);
RFM95_DEBUG(PSTR("RFM95:ATC:ADJ TXL,cR=%" PRIi16 ",tR=%" PRIi16 "..%" PRIi16 ",TXL=%" PRIi8 "\n"),
ownRSSI, lRange, uRange, RFM95.powerLevel);
return RFM95_setTxPowerLevel(newPowerLevel);
}
LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void* buffer,
LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer,
const uint8_t bufferSize, const uint8_t retries, const uint32_t retryWaitTime)
{
for (uint8_t retry = 0; retry <= retries; retry++) {
RFM95_DEBUG(PSTR("RFM95:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu16 ",RETRY=%" PRIu8 "\n"), recipient,
RFM95.txSequenceNumber,
retry);
rfm95_controlFlags_t flags = 0x00;
rfm95_controlFlags_t flags = 0u;
RFM95_setACKRequested(flags, (recipient != RFM95_BROADCAST_ADDRESS));
(void)RFM95_send(recipient, (uint8_t*)buffer, bufferSize, flags, !retry);
(void)RFM95_waitPacketSent();
// send packet
if (!RFM95_send(recipient, (uint8_t *)buffer, bufferSize, flags, !retry)) {
return false;
}
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX);
if (recipient == RFM95_BROADCAST_ADDRESS) {
return true;
}
const uint32_t enterMS = hwMillis();
while (hwMillis() - enterMS < retryWaitTime) {
RFM95_handler();
if (RFM95.ackReceived) {
const uint8_t sender = RFM95.currentPacket.header.sender;
const rfm95_sequenceNumber_t ACKsequenceNumber = RFM95.currentPacket.ACK.sequenceNumber;
@@ -533,7 +570,7 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void* buffer,
// ATC
if (RFM95.ATCenabled && RFM95_getACKRSSIReport(flag)) {
(void)RFM95_executeATC(RSSI, RFM95.ATCtargetRSSI);
} // ATC
}
return true;
} // seq check
}
@@ -541,7 +578,7 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void* buffer,
}
RFM95_DEBUG(PSTR("!RFM95:SWR:NACK\n"));
const uint32_t enterCSMAMS = hwMillis();
const uint16_t randDelayCSMA = enterMS & 100;
const uint16_t randDelayCSMA = enterMS % 100;
while (hwMillis() - enterCSMAMS < randDelayCSMA) {
doYield();
}
@@ -556,24 +593,15 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void* buffer,
// Wait until no channel activity detected or timeout
LOCAL bool RFM95_waitCAD(void)
{
// receiver needs to be in STDBY before entering CAD mode
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY);
(void)RFM95_setRadioMode(RFM95_RADIO_MODE_CAD);
const uint32_t enterMS = hwMillis();
while (RFM95.radioMode == RFM95_RADIO_MODE_CAD) {
if (hwMillis() - enterMS > RFM95_CAD_TIMEOUT_MS) {
return false;
}
while (RFM95.radioMode == RFM95_RADIO_MODE_CAD && (hwMillis() - enterMS < RFM95_CAD_TIMEOUT_MS) ) {
doYield();
RFM95_handler();
}
return !RFM95.cad;
}
// Wait for any previous transmission to finish
LOCAL bool RFM95_waitPacketSent(void)
{
while (RFM95.radioMode == RFM95_RADIO_MODE_TX) {
doYield();
}
return true;
return !RFM95.channelActive;
}
LOCAL void RFM95_ATCmode(const bool OnOff, const int16_t targetRSSI)
@@ -588,7 +616,6 @@ LOCAL bool RFM95_sanityCheck(void)
result &= RFM95_readReg(RFM95_REG_0F_FIFO_RX_BASE_ADDR) == RFM95_RX_FIFO_ADDR;
result &= RFM95_readReg(RFM95_REG_0E_FIFO_TX_BASE_ADDR) == RFM95_TX_FIFO_ADDR;
result &= RFM95_readReg(RFM95_REG_23_MAX_PAYLOAD_LENGTH) == RFM95_MAX_PACKET_LEN;
return result;
}

View File

@@ -6,7 +6,7 @@
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2017 Sensnology AB
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
@@ -18,7 +18,8 @@
*
* Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley <mikem@airspayce.com>
* Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html
* RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017 Olivier Mauti <olivier@mysensors.org>
*
* RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2018 Olivier Mauti <olivier@mysensors.org>
*
* Changelog:
* - ACK with sequenceNumber
@@ -26,7 +27,6 @@
*
* Definitions for HopeRF LoRa radios:
* http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
* http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf
*
*/
@@ -40,19 +40,22 @@
* RFM95 driver-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE
* - [!] Exclamation mark is prepended in case of error
*
* |E| SYS | SUB | Message | Comment
* |-|-------|------|------------------------------------|-----------------------------------------------------------------------------------
* | | RFM95 | INIT | | Initialise RFM95 radio
* | | RFM95 | INIT | PIN,CS=%d,IQP=%d,IQN=%d[,RST=%d] | Pin configuration: chip select (CS), IRQ pin (IQP), IRQ number (IQN), Reset (RST)
* |!| RFM95 | INIT | SANCHK FAIL | Sanity check failed, check wiring or replace module
* | | RFM95 | RCV | SEND ACK | ACK request received, sending ACK back
* | | RFM95 | PTC | LEVEL=%d | Set TX power level
* | | RFM95 | SAC | SEND ACK,TO=%d,RSSI=%d,SNR=%d | Send ACK to node (TO), RSSI of received message (RSSI), SNR of message (SNR)
* | | RFM95 | ATC | ADJ TXL,cR=%d,tR=%d,TXL=%d | Adjust TX level, current RSSI (cR), target RSSI (tR), TX level (TXL)
* | | RFM95 | SWR | SEND,TO=%d,RETRY=%d | Send message to (TO), NACK retry counter (RETRY)
* | | RFM95 | SWR | ACK FROM=%d,SEQ=%d,RSSI=%d,SNR=%d | ACK received from node (FROM), seq ID (SEQ), (RSSI), (SNR)
* |!| RFM95 | SWR | NACK | No ACK received
* | | RFM95 | SPP | PCT=%d,TX LEVEL=%d | Set TX level percent (PCT), TX level (LEVEL)
* |E| SYS | SUB | Message | Comment
* |-|-------|------|----------------------------------------|-----------------------------------------------------------------------------------
* | | RFM95 | INIT | | Initialise RFM95 radio
* | | RFM95 | INIT | PIN,CS=%%d,IQP=%%d,IQN=%%d[,RST=%%d] | Pin configuration: chip select (CS), IRQ pin (IQP), IRQ number (IQN), Reset (RST)
* |!| RFM95 | INIT | SANCHK FAIL | Sanity check failed, check wiring or replace module
* |!| RFM95 | IRH | CRC FAIL | Incoming packet has CRC error, skip
* | | RFM95 | RCV | SEND ACK | ACK request received, sending ACK back
* | | RFM95 | PTC | LEVEL=%%d | Set TX power level
* | | RFM95 | SAC | SEND ACK,TO=%%d,RSSI=%%d,SNR=%%d | Send ACK to node (TO), RSSI of received message (RSSI), SNR of message (SNR)
* | | RFM95 | ATC | ADJ TXL,cR=%%d,tR=%%d..%%d,TXL=%%d | Adjust TX level, current RSSI (cR), target RSSI range (tR), TX level (TXL)
* | | RFM95 | SWR | SEND,TO=%%d,RETRY=%%d | Send message to (TO), NACK retry counter (RETRY)
* | | RFM95 | SWR | ACK FROM=%%d,SEQ=%%d,RSSI=%%d,SNR=%%d | ACK received from node (FROM), seq ID (SEQ), (RSSI), (SNR)
* |!| RFM95 | SWR | NACK | No ACK received
* | | RFM95 | SPP | PCT=%%d,TX LEVEL=%%d | Set TX level percent (PCT), TX level (LEVEL)
* | | RFM95 | PWD | | Power down radio
* | | RFM95 | PWU | | Power up radio
*
* RFM95 modem configuration
*
@@ -60,12 +63,12 @@
* CR = Error correction code
* SF = Spreading factor, chips / symbol
*
* | CONFIG | REG_1D | REG_1E | REG_26 | BW | CR | SF | Comment | air-time (15 bytes)
* |------------------|--------|--------|--------|-------|-----|------|-----------------------|------------------------
* | BW125CR45SF128 | 0x72 | 0x74 | 0x04 | 125 | 4/5 | 128 | Default, medium range | 50ms
* | BW500CR45SF128 | 0x92 | 0x74 | 0x04 | 500 | 4/5 | 128 | Fast, short range | 15ms
* | BW31_25CR48SF512 | 0x48 | 0x94 | 0x04 | 31.25 | 4/8 | 512 | Slow, long range | 900ms
* | BW125CR48SF4096 | 0x78 | 0xC4 | 0x0C | 125 | 4/8 | 4096 | Slow, long range | 1500ms
* | CONFIG | BW | CR | SF | Comment | air-time (15 bytes)
* |------------------|-------|-----|------|-----------------------|------------------------
* | BW125CR45SF128 | 125 | 4/5 | 128 | Default, medium range | 50ms
* | BW500CR45SF128 | 500 | 4/5 | 128 | Fast, short range | 15ms
* | BW31_25CR48SF512 | 31.25 | 4/8 | 512 | Slow, long range | 900ms
* | BW125CR48SF4096 | 125 | 4/8 | 4096 | Slow, long range | 1500ms
*
* See here for air-time calculation: https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc
*
@@ -81,34 +84,27 @@
// default PIN assignments, can be overridden
#if defined(ARDUINO_ARCH_AVR)
#if defined(__AVR_ATmega32U4__)
#define DEFAULT_RFM95_IRQ_PIN (3) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_PIN (3) //!< DEFAULT_RFM95_IRQ_PIN
#else
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#endif
#define DEFAULT_RFM95_IRQ_NUM digitalPinToInterrupt(DEFAULT_RFM95_IRQ_PIN) //!< DEFAULT_RFM95_IRQ_NUM
#elif defined(ARDUINO_ARCH_ESP8266)
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_NUM digitalPinToInterrupt(DEFAULT_RFM95_IRQ_PIN) //!< DEFAULT_RFM95_IRQ_NUM
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#elif defined(ARDUINO_ARCH_ESP32)
#warning not implemented yet
#elif defined(ARDUINO_ARCH_SAMD)
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_NUM digitalPinToInterrupt(DEFAULT_RFM95_IRQ_PIN) //!< DEFAULT_RFM95_IRQ_NUM
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#elif defined(LINUX_ARCH_RASPBERRYPI)
#define DEFAULT_RFM95_IRQ_PIN (22) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_NUM DEFAULT_RFM95_IRQ_PIN //!< DEFAULT_RFM95_IRQ_NUM
#define DEFAULT_RFM95_IRQ_PIN (22) //!< DEFAULT_RFM95_IRQ_PIN
#elif defined(ARDUINO_ARCH_STM32F1)
#define DEFAULT_RFM95_IRQ_PIN (PA3) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_NUM DEFAULT_RFM95_IRQ_PIN //!< DEFAULT_RFM95_IRQ_NUM
#define DEFAULT_RFM95_IRQ_PIN (PA3) //!< DEFAULT_RFM95_IRQ_PIN
#elif defined(TEENSYDUINO)
#define DEFAULT_RFM95_IRQ_PIN (8) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_NUM digitalPinToInterrupt(DEFAULT_RFM95_IRQ_PIN) //!< DEFAULT_RFM95_IRQ_NUM
#define DEFAULT_RFM95_IRQ_PIN (8) //!< DEFAULT_RFM95_IRQ_PIN
#else
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#define DEFAULT_RFM95_IRQ_NUM DEFAULT_RFM95_IRQ_PIN //!< DEFAULT_RFM95_IRQ_NUM
#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN
#endif
#define DEFAULT_RFM95_CS_PIN (SS) //!< DEFAULT_RFM95_CS_PIN
#define DEFAULT_RFM95_CS_PIN (SS) //!< DEFAULT_RFM95_CS_PIN
// SPI settings
#define MY_RFM95_SPI_DATA_ORDER MSBFIRST //!< SPI data order
@@ -149,36 +145,40 @@ extern HardwareSPI SPI; //!< SPI
#define RFM95_RETRY_TIMEOUT_MS (500ul) //!< Timeout for ACK, adjustments needed if modem configuration changed (air time different)
#endif
#if !defined(MY_RFM95_TX_TIMEOUT_MS)
#define MY_RFM95_TX_TIMEOUT_MS (5*1000ul) //!< TX timeout
#endif
// Frequency definitions
#define RFM95_169MHZ (169000000ul) //!< 169 Mhz
#define RFM95_315MHZ (315000000ul) //!< 315 Mhz
#define RFM95_434MHZ (433920000ul) //!< 433.92 Mhz
#define RFM95_868MHZ (868100000ul) //!< 868.1 Mhz
#define RFM95_915MHZ (915000000ul) //!< 915 Mhz
#define RFM95_169MHZ (169000000ul) //!< 169 Mhz
#define RFM95_315MHZ (315000000ul) //!< 315 Mhz
#define RFM95_434MHZ (433920000ul) //!< 433.92 Mhz
#define RFM95_868MHZ (868100000ul) //!< 868.1 Mhz
#define RFM95_915MHZ (915000000ul) //!< 915 Mhz
#define RFM95_RETRIES (5u) //!< Retries in case of failed transmission
#define RFM95_FIFO_SIZE (0xFFu) //!< Max number of bytes the LORA Rx/Tx FIFO can hold
#define RFM95_RX_FIFO_ADDR (0x00u) //!< RX FIFO addr pointer
#define RFM95_TX_FIFO_ADDR (0x80u) //!< TX FIFO addr pointer
#define RFM95_MAX_PACKET_LEN (0x40u) //!< This is the maximum number of bytes that can be carried by the LORA
#define RFM95_PREAMBLE_LENGTH (8u) //!< Preamble length, default=8
#define RFM95_CAD_TIMEOUT_MS (2*1000ul) //!< channel activity detection timeout
#define RFM95_POWERUP_DELAY_MS (100u) //!< Power up delay, allow VCC to settle, transport to become fully operational
#define RFM95_RETRIES (5u) //!< Retries in case of failed transmission
#define RFM95_FIFO_SIZE (0xFFu) //!< Max number of bytes the LORA Rx/Tx FIFO can hold
#define RFM95_RX_FIFO_ADDR (0x00u) //!< RX FIFO addr pointer
#define RFM95_TX_FIFO_ADDR (0x80u) //!< TX FIFO addr pointer
#define RFM95_MAX_PACKET_LEN (0x40u) //!< This is the maximum number of bytes that can be carried by the LORA
#define RFM95_PREAMBLE_LENGTH (8u) //!< Preamble length, default=8
#define RFM95_CAD_TIMEOUT_MS (2*1000ul) //!< channel activity detection timeout
#define RFM95_POWERUP_DELAY_MS (100u) //!< Power up delay, allow VCC to settle, transport to become fully operational
#define RFM95_PACKET_HEADER_VERSION (1u) //!< RFM95 packet header version
#define RFM95_MIN_PACKET_HEADER_VERSION (1u) //!< Minimal RFM95 packet header version
#define RFM95_BIT_ACK_REQUESTED (7u) //!< RFM95 header, controlFlag, bit 7
#define RFM95_BIT_ACK_RECEIVED (6u) //!< RFM95 header, controlFlag, bit 6
#define RFM95_BIT_ACK_RSSI_REPORT (5u) //!< RFM95 header, controlFlag, bit 5
#define RFM95_PACKET_HEADER_VERSION (1u) //!< RFM95 packet header version
#define RFM95_MIN_PACKET_HEADER_VERSION (1u) //!< Minimal RFM95 packet header version
#define RFM95_BIT_ACK_REQUESTED (7u) //!< RFM95 header, controlFlag, bit 7
#define RFM95_BIT_ACK_RECEIVED (6u) //!< RFM95 header, controlFlag, bit 6
#define RFM95_BIT_ACK_RSSI_REPORT (5u) //!< RFM95 header, controlFlag, bit 5
#define RFM95_BROADCAST_ADDRESS (255u) //!< Broadcasting address
#define RFM95_ATC_TARGET_RANGE_PERCENT (5u) //!< ATC target range +/-%
#define RFM95_RSSI_OFFSET (137u) //!< RSSI offset
#define RFM95_TARGET_RSSI (-60) //!< RSSI target
#define RFM95_PROMISCUOUS (false) //!< RFM95 promiscuous mode
#define RFM95_BROADCAST_ADDRESS (255u) //!< Broadcasting address
#define RFM95_ATC_TARGET_RANGE_DBM (2u) //!< ATC target range +/- dBm
#define RFM95_RSSI_OFFSET (137u) //!< RSSI offset
#define RFM95_TARGET_RSSI (-70) //!< RSSI target
#define RFM95_PROMISCUOUS (false) //!< RFM95 promiscuous mode
#define RFM95_FXOSC (32*1000000ul) //!< The crystal oscillator frequency of the module
#define RFM95_FSTEP (RFM95_FXOSC / 524288.0f) //!< The Frequency Synthesizer step = RFM95_FXOSC /
#define RFM95_FXOSC (32*1000000ul) //!< The crystal oscillator frequency of the module
#define RFM95_FSTEP (RFM95_FXOSC / 524288.0f) //!< The Frequency Synthesizer step
// helper macros
#define RFM95_getACKRequested(__value) ((bool)bitRead(__value, RFM95_BIT_ACK_REQUESTED)) //!< getACKRequested
@@ -187,8 +187,8 @@ extern HardwareSPI SPI; //!< SPI
#define RFM95_setACKReceived(__value, __flag) bitWrite(__value, RFM95_BIT_ACK_RECEIVED,__flag) //!< setACKReceived
#define RFM95_setACKRSSIReport(__value, __flag) bitWrite(__value, RFM95_BIT_ACK_RSSI_REPORT,__flag) //!< setACKRSSIReport
#define RFM95_getACKRSSIReport(__value) ((bool)bitRead(__value, RFM95_BIT_ACK_RSSI_REPORT)) //!< getACKRSSIReport
#define RFM95_internalToRSSI(__value) ((int16_t)(__value - RFM95_RSSI_OFFSET)) //!< Convert internal RSSI to RSSI
#define RFM95_RSSItoInternal(__value) ((uint8_t)(__value + RFM95_RSSI_OFFSET)) //!< Convert RSSI to internal RSSI
//#define RFM95_internalToRSSI(__value) ((int16_t)(__value - RFM95_RSSI_OFFSET)) //!< Convert internal RSSI to RSSI
//#define RFM95_RSSItoInternal(__value) ((uint8_t)(__value + RFM95_RSSI_OFFSET)) //!< Convert RSSI to internal RSSI
#define RFM95_internalToSNR(__value) ((int8_t)(__value / 4)) //!< Convert internal SNR to SNR
#define RFM95_MIN_POWER_LEVEL_DBM ((rfm95_powerLevel_t)5u) //!< min. power level
@@ -220,7 +220,7 @@ typedef struct {
/**
* @brief Sequence number data type
*/
typedef uint16_t rfm95_sequenceNumber_t;
typedef uint16_t rfm95_sequenceNumber_t; // will eventually change to uint8_t in 3.0
/**
* @brief RSSI data type
*/
@@ -285,18 +285,18 @@ typedef struct {
* @brief RFM95 internal variables
*/
typedef struct {
uint8_t address; //!< Node address
rfm95_packet_t currentPacket; //!< Buffer for current packet
rfm95_sequenceNumber_t txSequenceNumber; //!< RFM95_txSequenceNumber
rfm95_powerLevel_t powerLevel; //!< TX power level dBm
rfm95_RSSI_t ATCtargetRSSI; //!< ATC: target RSSI
uint8_t address; //!< Node address
rfm95_packet_t currentPacket; //!< Buffer for current packet
rfm95_sequenceNumber_t txSequenceNumber; //!< RFM95_txSequenceNumber
rfm95_powerLevel_t powerLevel; //!< TX power level dBm
rfm95_RSSI_t ATCtargetRSSI; //!< ATC: target RSSI
// 8 bit
rfm95_radioMode_t radioMode : 3; //!< current transceiver state
bool cad : 1; //!< RFM95_cad
bool ATCenabled : 1; //!< ATC enabled
bool ackReceived : 1; //!< ACK received
bool dataReceived : 1; //!< AData received
uint8_t reserved : 1; //!< reserved
rfm95_radioMode_t radioMode : 3; //!< current transceiver state
bool channelActive : 1; //!< RFM95_cad
bool ATCenabled : 1; //!< ATC enabled
bool ackReceived : 1; //!< ACK received
bool dataReceived : 1; //!< Data received
bool reserved : 1; //!< unused
} rfm95_internal_t;
#define LOCAL static //!< static
@@ -318,14 +318,14 @@ LOCAL void RFM95_setAddress(const uint8_t addr);
*/
LOCAL uint8_t RFM95_getAddress(void);
/**
* @brief Sets all the registered required to configure the data modem in the RF95/96/97/98, including the
* @brief Sets all the registers required to configure the data modem in the RF95/96/97/98, including the
* bandwidth, spreading factor etc.
* @param config See modemConfig_t and references therein
*/
LOCAL void RFM95_setModemRegisters(const rfm95_modemConfig_t* config);
LOCAL void RFM95_setModemRegisters(const rfm95_modemConfig_t *config);
/**
* @brief Tests whether a new message is available
* @return True if a new, complete, error-free uncollected message is available to be retreived by @ref RFM95_recv()
* @return True if a new, complete, error-free uncollected message is available to be retreived by @ref RFM95_receive()
*/
LOCAL bool RFM95_available(void);
/**
@@ -334,7 +334,7 @@ LOCAL bool RFM95_available(void);
* @param maxBufSize Max buffer size
* @return Number of bytes
*/
LOCAL uint8_t RFM95_recv(uint8_t* buf, const uint8_t maxBufSize);
LOCAL uint8_t RFM95_receive(uint8_t *buf, const uint8_t maxBufSize);
/**
* @brief RFM95_send
* @param recipient
@@ -344,7 +344,7 @@ LOCAL uint8_t RFM95_recv(uint8_t* buf, const uint8_t maxBufSize);
* @param increaseSequenceCounter
* @return True if packet sent
*/
LOCAL bool RFM95_send(const uint8_t recipient, uint8_t* data, const uint8_t len,
LOCAL bool RFM95_send(const uint8_t recipient, uint8_t *data, const uint8_t len,
const rfm95_controlFlags_t flags, const bool increaseSequenceCounter = true);
/**
* @brief RFM95_sendFrame
@@ -352,7 +352,7 @@ LOCAL bool RFM95_send(const uint8_t recipient, uint8_t* data, const uint8_t len,
* @param increaseSequenceCounter
* @return True if frame sent
*/
LOCAL bool RFM95_sendFrame(rfm95_packet_t &packet, const bool increaseSequenceCounter = true);
LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet, const bool increaseSequenceCounter = true);
/**
* @brief RFM95_setPreambleLength
* @param preambleLength
@@ -377,17 +377,15 @@ LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel);
*/
LOCAL bool RFM95_setTxPowerPercent(const uint8_t newPowerPercent);
#if defined(MY_RFM95_TCXO)
/**
* @brief Enable TCXO mode
* Call this immediately after init(), to force your radio to use an external
* frequency source, such as a Temperature Compensated Crystal Oscillator (TCXO).
* See the comments in the main documentation about the sensitivity of this radio to
* clock frequency especially when using narrow bandwidths.
* Leaves the module in sleep mode.
* @note Has to be called while radio is in sleep mode.
*/
LOCAL void RFM95_enableTCXO(void);
#endif
/**
* @brief Sets the radio into low-power sleep mode
@@ -425,7 +423,7 @@ LOCAL void RFM95_sendACK(const uint8_t recipient, const rfm95_sequenceNumber_t s
* @param retryWaitTime
* @return True if packet successfully sent
*/
LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void* buffer,
LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer,
const uint8_t bufferSize, const uint8_t retries = RFM95_RETRIES,
const uint32_t retryWaitTime = RFM95_RETRY_TIMEOUT_MS);
/**
@@ -433,11 +431,7 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void* buffer,
* @return True if no channel activity detected, False if timeout occured
*/
LOCAL bool RFM95_waitCAD(void);
/**
* @brief RFM95_waitPacketSent
* @return True if packet sent
*/
LOCAL bool RFM95_waitPacketSent(void);
/**
* @brief RFM95_setRadioMode
* @param newRadioMode
@@ -448,6 +442,16 @@ LOCAL bool RFM95_setRadioMode(const rfm95_radioMode_t newRadioMode);
* @brief Low level interrupt handler
*/
LOCAL void RFM95_interruptHandler(void);
/**
* @brief Packet engine
*/
LOCAL void RFM95_interruptHandling(void);
/**
* @brief RFM95_handler
*/
LOCAL void RFM95_handler(void);
/**
* @brief RFM95_getSendingRSSI
* @return RSSI Signal strength of last packet received

View File

@@ -1,34 +1,30 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2016 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley <mikem@airspayce.com>
* Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html
* RFM95 driver refactored and optimized for MySensors, Copyright (C) 2016 Olivier Mauti <olivier@mysensors.org>
*
* Changelog:
* - ACK with sequenceNumber
* - ATC control
*
* Definitions for HopeRF LoRa radios:
* http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
* http://www.hoperf.cn/upload/rfchip/RF96_97_98.pdf
*
*/
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley <mikem@airspayce.com>
* Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html
*
* RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2018 Olivier Mauti <olivier@mysensors.org>
*
* Definitions for HopeRF LoRa radios:
* http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf
*
*/
// Register access
#define RFM95_READ_REGISTER (0x7Fu) //!< reading register
@@ -152,6 +148,7 @@
#define RFM95_PAYLOAD_CRC_ERROR 0x20 //!< PAYLOAD_CRC_ERROR
#define RFM95_RX_DONE 0x40 //!< RX_DONE
#define RFM95_RX_TIMEOUT 0x80 //!< RX_TIMEOUT
#define RFM95_CLEAR_IRQ 0xFF //<! Clear IRQ
// RFM95_REG_18_MODEM_STAT 0x18
#define RFM95_MODEM_STATUS_SIGNAL_DETECTED 0x01 //!< MODEM_STATUS_SIGNAL_DETECTED