/* * 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 * Copyright (C) 2013-2017 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. * * RFM69 driver refactored for Mysensors * * Based on : * - LowPowerLab RFM69 Lib Copyright Felix Rusu (2014), felix@lowpowerlab.com * - 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) * - Mysensors generic radio driver implementation Copyright (C) 2017 Olivier Mauti * * Changes by : @tekka, @scalz, @marceloagno * * Definitions for Semtech SX1231/H radios: * http://www.semtech.com/images/datasheet/sx1231.pdf * http://www.semtech.com/images/datasheet/sx1231h.pdf */ #include "RFM69_new.h" // debug #if defined(MY_DEBUG_VERBOSE_RFM69) #define RFM69_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< Debug print #else #define RFM69_DEBUG(x,...) //!< DEBUG null #endif volatile rfm69_internal_t RFM69; //!< internal variables #if defined (SREG) && !defined(SPI_HAS_TRANSACTION) uint8_t _SREG; // Used to save and restore the SREG values in SPI transactions #endif #if defined (SPCR) && defined (SPSR) && !defined(SPI_HAS_TRANSACTION) uint8_t _SPCR; //!< _SPCR uint8_t _SPSR; //!< _SPSR #endif #ifdef LINUX_SPI_BCM // SPI RX and TX buffers (max packet len + 1 byte for the command) uint8_t spi_rxbuff[RFM69_MAX_PACKET_LEN + 1]; uint8_t spi_txbuff[RFM69_MAX_PACKET_LEN + 1]; #endif LOCAL void RFM69_csn(const bool level) { hwDigitalWrite(MY_RFM69_CS_PIN, level); } LOCAL void RFM69_prepareSPITransaction(void) { #if !defined(MY_SOFTSPI) && defined(SPI_HAS_TRANSACTION) _SPI.beginTransaction(SPISettings(MY_RFM69_SPI_SPEED, RFM69_SPI_DATA_ORDER, RFM69_SPI_DATA_MODE)); #else #if defined(SREG) _SREG = SREG; #endif noInterrupts(); #if defined(SPCR) && defined(SPSR) // save current SPI settings _SPCR = SPCR; _SPSR = SPSR; #endif // set RFM69 SPI settings #if !defined(MY_SOFTSPI) _SPI.setDataMode(RFM69_SPI_DATA_MODE); _SPI.setBitOrder(RFM69_SPI_DATA_ORDER); _SPI.setClockDivider(RFM69_CLOCK_DIV); #endif #endif } LOCAL void RFM69_concludeSPITransaction(void) { #if !defined(MY_SOFTSPI) && defined(SPI_HAS_TRANSACTION) _SPI.endTransaction(); #else // restore SPI settings to what they were before talking to RFM69 #if defined(SPCR) && defined(SPSR) SPCR = _SPCR; SPSR = _SPSR; #endif // restore the prior interrupt state #if defined(SREG) SREG = _SREG; #endif interrupts(); #endif } LOCAL uint8_t RFM69_spiMultiByteTransfer(const uint8_t cmd, uint8_t* buf, uint8_t len, const bool aReadMode) { uint8_t status; uint8_t* current = buf; RFM69_prepareSPITransaction(); RFM69_csn(LOW); #ifdef LINUX_SPI_BCM uint8_t * prx = spi_rxbuff; uint8_t * ptx = spi_txbuff; uint8_t size = len + 1; // Add register value to transmit buffer *ptx++ = cmd; while (len--) { if (aReadMode) { *ptx++ = (uint8_t)RFM69_NOP; } else { *ptx++ = *current++; } } _SPI.transfernb((char *)spi_txbuff, (char *)spi_rxbuff, size); if (aReadMode) { if (size == 2) { status = *++prx; // result is 2nd byte of receive buffer } else { status = *prx++; // status is 1st byte of receive buffer // decrement before to skip status byte while (--size) { *buf++ = *prx++; } } } else { status = *prx; // status is 1st byte of receive buffer } #else status = _SPI.transfer(cmd); while (len--) { if (aReadMode) { status = _SPI.transfer((uint8_t)RFM69_NOP); if (buf != NULL) { *current++ = status; } } else { status = _SPI.transfer(*current++); } } #endif RFM69_csn(HIGH); RFM69_concludeSPITransaction(); return status; } // low level register access LOCAL uint8_t RFM69_RAW_readByteRegister(const uint8_t address) { const uint8_t value = RFM69_spiMultiByteTransfer(address, NULL, 1, true); //RFM69_DEBUG(PSTR("RFM69:read register, reg=0x%02x, value=%d\n"), address, value); return value; } // low level register access LOCAL uint8_t RFM69_RAW_writeByteRegister(const uint8_t address, uint8_t value) { //RFM69_DEBUG(PSTR("RFM69:write register, reg=0x%02x, value=%d\n"), address & 0x7F, value); return RFM69_spiMultiByteTransfer(address, &value, 1, false); } // macros, saves space #define RFM69_readReg(__reg) RFM69_RAW_readByteRegister(__reg & RFM69_READ_REGISTER) #define RFM69_writeReg(__reg, __value) RFM69_RAW_writeByteRegister((__reg | RFM69_WRITE_REGISTER), __value ) #define RFM69_burstReadReg(__reg, __buf, __len) RFM69_spiMultiByteTransfer( __reg & RFM69_READ_REGISTER, (uint8_t*)__buf, __len, true ) #define RFM69_burstWriteReg(__reg, __buf, __len) RFM69_spiMultiByteTransfer( __reg | RFM69_WRITE_REGISTER, (uint8_t*)__buf, __len, false ) LOCAL bool RFM69_initialise(const uint32_t frequencyHz) { RFM69_DEBUG(PSTR("RFM69:INIT\n")); // power up radio if power pin defined #if defined(MY_RFM69_POWER_PIN) hwPinMode(MY_RFM69_POWER_PIN, OUTPUT); #endif RFM69_powerUp(); // reset radio module if rst pin defined #if defined(MY_RFM69_RST_PIN) hwPinMode(MY_RFM69_RST_PIN, OUTPUT); hwDigitalWrite(MY_RFM69_RST_PIN, HIGH); // 100uS delayMicroseconds(100); hwDigitalWrite(MY_RFM69_RST_PIN, LOW); // wait until chip ready delay(5); RFM69_DEBUG(PSTR("RFM69:INIT:PIN,CS=%d,IQP=%d,IQN=%d,RST=%d\n"), MY_RFM69_CS_PIN,MY_RFM69_IRQ_PIN, MY_RFM69_IRQ_NUM,MY_RFM69_RST_PIN); #else RFM69_DEBUG(PSTR("RFM69:INIT:PIN,CS=%d,IQP=%d,IQN=%d\n"),MY_RFM69_CS_PIN, MY_RFM69_IRQ_PIN, MY_RFM69_IRQ_NUM); #endif // set variables RFM69.address = RFM69_BROADCAST_ADDRESS; RFM69.dataReceived = false; RFM69.ackReceived = false; RFM69.txSequenceNumber = 0; // initialise TX sequence counter RFM69.powerLevel = MY_RFM69_TX_POWER_DBM + 1; // will be overwritten when set RFM69.radioMode = RFM69_RADIO_MODE_SLEEP; RFM69.ATCenabled = false; RFM69.listenModeEnabled = false; RFM69.ATCtargetRSSI = RFM69_RSSItoInternal(RFM69_TARGET_RSSI_DBM); // SPI init hwDigitalWrite(MY_RFM69_CS_PIN, HIGH); hwPinMode(MY_RFM69_CS_PIN, OUTPUT); _SPI.begin(); RFM69_setConfiguration(); RFM69_setFrequency(frequencyHz); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); // Encryption is persistent between resets and can trip you up during debugging. // Disable it during initialization so we always start from a known state. RFM69_encrypt(0); (void)RFM69_setTxPowerLevel(MY_RFM69_TX_POWER_DBM); // IRQ hwPinMode(MY_RFM69_IRQ_PIN, INPUT); #if defined (SPI_HAS_TRANSACTION) && !defined (ESP8266) && !defined (MY_SOFTSPI) _SPI.usingInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN)); #endif #ifdef MY_DEBUG_VERBOSE_RFM69_REGISTERS RFM69_readAllRegs(); #endif if (!RFM69_sanityCheck()) { RFM69_DEBUG( PSTR("!RFM69:INIT:SANCHK FAIL\n")); // sanity check failed, check wiring or replace module return false; } attachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN), RFM69_interruptHandler, RISING); return true; } // IRQ handler: PayloadReady (RX) & PacketSent (TX) mapped to DI0 LOCAL void RFM69_interruptHandler(void) { #if defined(MY_RFM69_ENABLE_LISTENMODE) if (RFM69.listenModeEnabled) { RFM69_listenModeReset(); //noInterrupts(); union { // union to simplify addressing of long and short parts of time offset uint32_t l; uint8_t b[4]; } burstRemaining; burstRemaining.l = 0; } #endif const uint8_t regIrqFlags2 = RFM69_readReg(RFM69_REG_IRQFLAGS2); if ( RFM69.radioMode == RFM69_RADIO_MODE_RX && (regIrqFlags2 & RFM69_IRQFLAGS2_PAYLOADREADY) ) { RFM69.currentPacket.RSSI = RFM69_readRSSI(); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); // use the fifo level irq as indicator if header bytes received if (regIrqFlags2 & RFM69_IRQFLAGS2_FIFOLEVEL) { RFM69_prepareSPITransaction(); RFM69_csn(LOW); #ifdef LINUX_SPI_BCM char data[RFM69_MAX_PACKET_LEN + 1]; // max packet len + 1 byte for the command data[0] = RFM69_REG_FIFO & RFM69_READ_REGISTER; SPI.transfern(data, 3); RFM69.currentPacket.header.packetLen = data[1]; RFM69.currentPacket.header.recipient = data[2]; if (RFM69.currentPacket.header.packetLen > RFM69_MAX_PACKET_LEN) { RFM69.currentPacket.header.packetLen = RFM69_MAX_PACKET_LEN; } data[0] = RFM69_REG_FIFO & RFM69_READ_REGISTER; //SPI.transfern(data, RFM69.currentPacket.header.packetLen - 1); //TODO: Wrong packetLen? SPI.transfern(data, RFM69.currentPacket.header.packetLen); //(void)memcpy((void*)&RFM69.currentPacket.data[2], (void*)&data[1], RFM69.currentPacket.header.packetLen - 2); //TODO: Wrong packetLen? (void)memcpy((void*)&RFM69.currentPacket.data[2], (void*)&data[1], RFM69.currentPacket.header.packetLen - 1); if (RFM69.currentPacket.header.version >= RFM69_MIN_PACKET_HEADER_VERSION) { RFM69.currentPacket.payloadLen = min(RFM69.currentPacket.header.packetLen - (RFM69_HEADER_LEN - 1), RFM69_MAX_PACKET_LEN); RFM69.ackReceived = RFM69_getACKReceived(RFM69.currentPacket.header.controlFlags); RFM69.dataReceived = !RFM69.ackReceived; } #else _SPI.transfer(RFM69_REG_FIFO & RFM69_READ_REGISTER); // set reading pointer uint8_t* current = (uint8_t*)&RFM69.currentPacket; bool headerRead = false; // first read header uint8_t readingLength = RFM69_HEADER_LEN; while (readingLength--) { *current++ = _SPI.transfer((uint8_t)0x00); if (!readingLength && !headerRead) { // header read headerRead = true; if (RFM69.currentPacket.header.version >= RFM69_MIN_PACKET_HEADER_VERSION) { // read payload readingLength = RFM69.currentPacket.header.packetLen - (RFM69_HEADER_LEN - 1); if (readingLength < RFM69_MAX_PACKET_LEN) { readingLength = RFM69_MAX_PACKET_LEN; } RFM69.currentPacket.payloadLen = readingLength; RFM69.ackReceived = RFM69_getACKReceived(RFM69.currentPacket.header.controlFlags); RFM69.dataReceived = !RFM69.ackReceived; } } } #endif RFM69_csn(HIGH); RFM69_concludeSPITransaction(); } // radio remains in stdby until paket read } else if (RFM69.radioMode == RFM69_RADIO_MODE_TX && (regIrqFlags2 & RFM69_IRQFLAGS2_PACKETSENT) ) { RFM69.dataSent = true; // back to RX (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); } } LOCAL bool RFM69_available(void) { if (RFM69.dataReceived) { // data received - we are still in STDBY from IRQ handler //(void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); return true; } else if (RFM69.radioMode == RFM69_RADIO_MODE_TX) { return false; } else if (RFM69.radioMode != RFM69_RADIO_MODE_RX) { // we are not in RX, and no data received RFM69_setRadioMode(RFM69_RADIO_MODE_RX); } return false; } LOCAL uint8_t RFM69_recv(uint8_t* buf, const uint8_t maxBufSize) { // atomic noInterrupts(); const uint8_t payloadLen = RFM69.currentPacket.payloadLen < maxBufSize? RFM69.currentPacket.payloadLen : maxBufSize; const uint8_t sender = RFM69.currentPacket.header.sender; const rfm69_sequenceNumber_t sequenceNumber = RFM69.currentPacket.header.sequenceNumber; const uint8_t controlFlags = RFM69.currentPacket.header.controlFlags; const rfm69_RSSI_t RSSI = RFM69.currentPacket.RSSI; // of incoming packet if (buf != NULL) { (void)memcpy((void*)buf, (void*)RFM69.currentPacket.payload, payloadLen); // packet read RFM69.dataReceived = false; } interrupts(); // explicitly re-enable interrupts if (RFM69_getACKRequested(controlFlags) && !RFM69_getACKReceived(controlFlags)) { RFM69_sendACK(sender, sequenceNumber,RSSI); } return payloadLen; } LOCAL bool RFM69_sendFrame(rfm69_packet_t &packet, const bool increaseSequenceCounter) { /* if(!RFM69_waitCAD()) { // channel not free return false; } */ // set radio to standby to load fifo (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); if (increaseSequenceCounter) { // increase sequence counter, overflow is ok RFM69.txSequenceNumber++; } packet.header.sequenceNumber = RFM69.txSequenceNumber; RFM69_writeReg(RFM69_REG_PACKETCONFIG2, (RFM69_readReg(RFM69_REG_PACKETCONFIG2) & 0xFB) | RFM69_PACKET2_RXRESTART); // avoid RX deadlocks // write packet const uint8_t finalLen = packet.payloadLen + RFM69_HEADER_LEN; // including length byte RFM69_burstWriteReg(RFM69_REG_FIFO, packet.data, finalLen); // send message (void)RFM69_setRadioMode(RFM69_RADIO_MODE_TX); const uint32_t txStart = hwMillis(); // irq handler set radiomode to rx once packet sent while (!RFM69.dataSent && hwMillis() - txStart < RFM69_TX_LIMIT_MS) { // wait until packet sent doYield(); } return RFM69.dataSent; } LOCAL bool RFM69_send(const uint8_t recipient, uint8_t* data, const uint8_t len, const rfm69_controlFlags_t flags, const bool increaseSequenceCounter) { // assemble packet rfm69_packet_t packet; packet.header.version = RFM69_PACKET_HEADER_VERSION; packet.header.sender = RFM69.address; packet.header.recipient = recipient; packet.header.controlFlags = 0x00; // reset packet.payloadLen = min(len, (uint8_t)RFM69_MAX_PAYLOAD_LEN); packet.header.controlFlags = flags; (void)memcpy(&packet.payload, data, packet.payloadLen); // copy payload packet.header.packetLen = packet.payloadLen + (RFM69_HEADER_LEN - 1); // -1 length byte return RFM69_sendFrame(packet, increaseSequenceCounter); } LOCAL void RFM69_setFrequency(const uint32_t frequencyHz) { const uint32_t freqHz = (uint32_t)(frequencyHz / RFM69_FSTEP); RFM69_writeReg(RFM69_REG_FRFMSB, freqHz >> 16); RFM69_writeReg(RFM69_REG_FRFMID, freqHz >> 8); RFM69_writeReg(RFM69_REG_FRFLSB, freqHz); } LOCAL void RFM69_setHighPowerRegs(const bool onOff) { #if defined(RFM69_VERSION_HW) RFM69_writeReg(RFM69_REG_OCP, (onOff ? RFM69_OCP_OFF : RFM69_OCP_ON ) | RFM69_OCP_TRIM_95); RFM69_writeReg(RFM69_REG_TESTPA1, onOff ? 0x5D : 0x55); RFM69_writeReg(RFM69_REG_TESTPA2, onOff ? 0x7C : 0x70); #else (void)onOff; #endif } LOCAL bool RFM69_setTxPowerLevel(rfm69_powerlevel_t newPowerLevel) { // limit power levels newPowerLevel = max((rfm69_powerlevel_t)RFM69_MIN_POWER_LEVEL_DBM, newPowerLevel); newPowerLevel = min((rfm69_powerlevel_t)RFM69_MAX_POWER_LEVEL_DBM, newPowerLevel); if (RFM69.powerLevel == newPowerLevel) { RFM69_DEBUG(PSTR("RFM69:PTX:NO ADJ\n")); return false; } RFM69.powerLevel = newPowerLevel; uint8_t palevel; #if !defined(RFM69_VERSION_HW) // -18dBm to +13dBm, PA0, offset 0, i.e. -18dBm = level 0 palevel = RFM69_PALEVEL_PA0_ON | ((uint8_t)(newPowerLevel + 18)); #else if (newPowerLevel <= (rfm69_powerlevel_t)13) { // -2dBm to +13dBm, PA1, offset 16, i.e. -2dBm = level 16 palevel = RFM69_PALEVEL_PA1_ON | ((uint8_t)(newPowerLevel + 18)); } else if (newPowerLevel >= (rfm69_powerlevel_t)RFM69_HIGH_POWER_DBM) { // +18dBm to +20dBm, PA1 and PA2, Boost settings, 18dBm = level 29 palevel = RFM69_PALEVEL_PA1_ON | RFM69_PALEVEL_PA2_ON | ((uint8_t)(newPowerLevel + 11)); } else { // +14dBm to +17dBm, PA1 and PA2 palevel = RFM69_PALEVEL_PA1_ON | RFM69_PALEVEL_PA2_ON | ((uint8_t)(newPowerLevel + 14)); } #endif RFM69_writeReg(RFM69_REG_PALEVEL, palevel); RFM69_DEBUG(PSTR("RFM69:PTX:LEVEL=%d dBm\n"),newPowerLevel); return true; } LOCAL void RFM69_setAddress(const uint8_t addr) { (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); RFM69.address = addr; RFM69_writeReg(RFM69_REG_NODEADRS, addr); } LOCAL uint8_t RFM69_getAddress(void) { return RFM69.address; } LOCAL bool RFM69_setRadioMode(const rfm69_radio_mode_t newRadioMode) { if (RFM69.radioMode == newRadioMode) { // no change return false; } uint8_t regMode; if (newRadioMode == RFM69_RADIO_MODE_STDBY) { regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY; } else if (newRadioMode == RFM69_RADIO_MODE_SLEEP) { regMode = RFM69_OPMODE_SEQUENCER_OFF | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_SLEEP; } else if (newRadioMode == RFM69_RADIO_MODE_RX) { RFM69.dataReceived = false; RFM69.ackReceived = false; regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_RECEIVER; RFM69_writeReg(RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_01); // Interrupt on PayloadReady, DIO0 // disable high power settings RFM69_setHighPowerRegs(false); RFM69_writeReg(RFM69_REG_PACKETCONFIG2, (RFM69_readReg(RFM69_REG_PACKETCONFIG2) & 0xFB) | RFM69_PACKET2_RXRESTART); // avoid RX deadlocks } else if (newRadioMode == RFM69_RADIO_MODE_TX) { RFM69.dataSent = false; regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_TRANSMITTER; RFM69_writeReg(RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_00); // Interrupt on PacketSent, DIO0 RFM69_setHighPowerRegs(RFM69.powerLevel >= (rfm69_powerlevel_t)RFM69_HIGH_POWER_DBM); } else if (newRadioMode == RFM69_RADIO_MODE_SYNTH) { regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_SYNTHESIZER; } #if defined(MY_RFM69_ENABLE_LISTENMODE) else if (newRadioMode == RFM69_RADIO_MODE_LISTEN) { // Start LISTENMODE regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_ON; RFM69.listenModeEnabled = true; RFM69_DEBUG(PSTR("RFM69:LSM:Start..\n")); while (RFM69_readReg(RFM69_REG_IRQFLAGS2) & RFM69_IRQFLAGS2_PACKETSENT == 0x00); // wait for ModeReady RFM69_listenModeReset(); detachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN)); attachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN), RFM69_interruptHandler, RISING); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY); //standby mode //TODO _isHighSpeed = true; //TODO _haveEncryptKey = false; uint32_t rxDuration = RFM69_LISTEN_RX_US; uint32_t idleDuration = RFM69_LISTEN_IDLE_US; RFM69_listenModeSetDurations(rxDuration, idleDuration); RFM69_writeReg(RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_01); RFM69_writeReg(RFM69_REG_FRFMSB, RFM69_readReg(RFM69_REG_FRFMSB) + 1); RFM69_writeReg(RFM69_REG_FRFLSB, RFM69_readReg(RFM69_REG_FRFLSB)); // MUST write to LSB to affect change! RFM69_listenModeApplyHighSpeedSettings(); RFM69_writeReg(RFM69_REG_PACKETCONFIG1, RFM69_PACKET1_FORMAT_VARIABLE | RFM69_PACKET1_DCFREE_WHITENING | RFM69_PACKET1_CRC_ON | RFM69_PACKET1_CRCAUTOCLEAR_ON); RFM69_writeReg(RFM69_REG_PACKETCONFIG2, RFM69_PACKET2_RXRESTARTDELAY_NONE | RFM69_PACKET2_AUTORXRESTART_ON | RFM69_PACKET2_AES_OFF); RFM69_writeReg(RFM69_REG_SYNCVALUE1, 0x5A); RFM69_writeReg(RFM69_REG_SYNCVALUE2, 0x5A); RFM69_setListenConfig(idleListenResolution, rxListenResolution, RFM69_LISTEN1_CRITERIA_RSSI, RFM69_LISTEN1_END_10); RFM69_setListenCoefIdle(idleListenCoef); RFM69_setListenCoefRx(rxListenCoef); RFM69_writeReg(RFM69_REG_RSSITHRESH, 180); RFM69_writeReg(RFM69_REG_RXTIMEOUT2, 75); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_STANDBY); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_ON | RFM69_OPMODE_STANDBY); //WIP } #endif else { regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY; } // set new mode RFM69_writeReg(RFM69_REG_OPMODE, regMode); // Waking from sleep mode may take longer if (RFM69.radioMode == RFM69_RADIO_MODE_SLEEP) { // wait for ModeReady if (!RFM69_isModeReady()) { return false; } } RFM69.radioMode = newRadioMode; return true; } LOCAL void RFM69_powerUp(void) { #if defined(MY_RFM69_POWER_PIN) hwDigitalWrite(MY_RFM69_POWER_PIN, HIGH); delay(RFM69_POWERUP_DELAY_MS); #endif } LOCAL void RFM69_powerDown(void) { #if defined(MY_RFM69_POWER_PIN) hwDigitalWrite(MY_RFM69_POWER_PIN, LOW); #endif } LOCAL bool RFM69_sleep(void) { RFM69_DEBUG(PSTR("RFM69:RSL\n")); // put radio to sleep return RFM69_setRadioMode(RFM69_RADIO_MODE_SLEEP); } LOCAL bool RFM69_standBy(void) { RFM69_DEBUG(PSTR("RFM69:RSB\n")); // put radio to standby return RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); } // should be called immediately after reception in case sender wants ACK LOCAL void RFM69_sendACK(const uint8_t recipient, const rfm69_sequenceNumber_t sequenceNumber, const rfm69_RSSI_t RSSI) { RFM69_DEBUG(PSTR("RFM69:SAC:SEND ACK,TO=%d,RSSI=%d\n"),recipient, RFM69_internalToRSSI(RSSI)); rfm69_ack_t ACK; ACK.sequenceNumber = sequenceNumber; ACK.RSSI = RSSI; rfm69_controlFlags_t flags = 0x00; // reset flags RFM69_setACKReceived(flags, true); RFM69_setACKRSSIReport(flags, true); (void)RFM69_send(recipient, (uint8_t*)&ACK, sizeof(rfm69_ack_t), flags); } LOCAL bool RFM69_executeATC(const rfm69_RSSI_t currentRSSI, const rfm69_RSSI_t targetRSSI) { rfm69_powerlevel_t newPowerLevel = RFM69.powerLevel; if (RFM69_internalToRSSI(currentRSSI) < RFM69_internalToRSSI(targetRSSI * (1 + RFM69_ATC_TARGET_RANGE_PERCENT/100)) && newPowerLevel < RFM69_MAX_POWER_LEVEL_DBM) { // increase transmitter power newPowerLevel++; } else if (RFM69_internalToRSSI(currentRSSI) > RFM69_internalToRSSI(targetRSSI * (1 - RFM69_ATC_TARGET_RANGE_PERCENT/100)) && newPowerLevel > RFM69_MIN_POWER_LEVEL_DBM) { // decrease transmitter power newPowerLevel--; } else { // nothing to adjust return false; } RFM69_DEBUG(PSTR("RFM69:ATC:ADJ TXL,cR=%d,tR=%d,TXL=%d\n"), RFM69_internalToRSSI(currentRSSI), RFM69_internalToRSSI(targetRSSI), newPowerLevel); return RFM69_setTxPowerLevel(newPowerLevel); } LOCAL void RFM69_ATCmode(const bool onOff, const int16_t targetRSSI) { RFM69.ATCenabled = onOff; RFM69.ATCtargetRSSI = RFM69_RSSItoInternal(targetRSSI); } LOCAL bool RFM69_sendWithRetry(const uint8_t recipient, const void* buffer, const uint8_t bufferSize, const uint8_t retries, const uint32_t retryWaitTimeMS) { for (uint8_t retry = 0; retry <= retries; retry++) { RFM69_DEBUG(PSTR("RFM69:SWR:SEND,TO=%d,RETRY=%d\n"), recipient, retry); rfm69_controlFlags_t flags = 0x00; // reset all flags RFM69_setACKRequested(flags, (recipient != RFM69_BROADCAST_ADDRESS)); RFM69_setACKRSSIReport(flags, RFM69.ATCenabled); (void)RFM69_send(recipient, (uint8_t*)buffer, bufferSize, flags, !retry); if (recipient == RFM69_BROADCAST_ADDRESS) { // no ACK requested return true; } // radio is in RX const uint32_t enterMS = hwMillis(); while (hwMillis() - enterMS < retryWaitTimeMS) { if (RFM69.ackReceived) { // radio is in stdby const uint8_t sender = RFM69.currentPacket.header.sender; const rfm69_sequenceNumber_t ACKsequenceNumber = RFM69.currentPacket.ACK.sequenceNumber; const rfm69_controlFlags_t flags = RFM69.currentPacket.header.controlFlags; const rfm69_RSSI_t RSSI = RFM69.currentPacket.ACK.RSSI; RFM69.ackReceived = false; // packet read, back to RX RFM69_setRadioMode(RFM69_RADIO_MODE_RX); if (sender == recipient && ACKsequenceNumber == RFM69.txSequenceNumber) { RFM69_DEBUG(PSTR("RFM69:SWR:ACK,FROM=%d,SEQ=%d,RSSI=%d\n"),sender,ACKsequenceNumber, RFM69_internalToRSSI(RSSI)); // ATC if (RFM69.ATCenabled && RFM69_getACKRSSIReport(flags)) { (void)RFM69_executeATC(RSSI,RFM69.ATCtargetRSSI); } // ATC // return to RX return true; } // seq check } doYield(); } RFM69_DEBUG(PSTR("!RFM69:SWR:NACK\n")); if (RFM69.ATCenabled) { // No ACK received, maybe out of reach: increase power level RFM69_setTxPowerLevel(RFM69.powerLevel + 1); } // random 100ms const uint32_t enterCSMAMS = hwMillis(); const uint16_t randDelayCSMA = enterMS & 40; // 61 bytes + ACK take ~40ms while (hwMillis() - enterCSMAMS < randDelayCSMA) { doYield(); } } return false; } LOCAL int16_t RFM69_getSendingRSSI(void) { // own RSSI, as measured by the recipient - ACK part if (RFM69_getACKRSSIReport(RFM69.currentPacket.header.controlFlags)) { return RFM69_internalToRSSI(RFM69.currentPacket.ACK.RSSI); } else { // not valid return 127; } } LOCAL int16_t RFM69_getReceivingRSSI(void) { // RSSI from sender return RFM69_internalToRSSI(RFM69.currentPacket.RSSI); } LOCAL bool RFM69_setTxPowerPercent(uint8_t newPowerPercent) { newPowerPercent = min(newPowerPercent, (uint8_t)100); // limit const rfm69_powerlevel_t newPowerLevel = static_cast (RFM69_MIN_POWER_LEVEL_DBM + (RFM69_MAX_POWER_LEVEL_DBM - RFM69_MIN_POWER_LEVEL_DBM) * (newPowerPercent / 100.0f)); RFM69_DEBUG(PSTR("RFM69:SPP:PCT=%d,TX LEVEL=%d\n"), newPowerPercent,newPowerLevel); return RFM69_setTxPowerLevel(newPowerLevel); } LOCAL rfm69_powerlevel_t RFM69_getTxPowerLevel(void) { // report TX level in dBm return RFM69.powerLevel; } LOCAL uint8_t RFM69_getTxPowerPercent(void) { // report TX level in % const uint8_t result = static_cast(100.0f * (RFM69.powerLevel - RFM69_MIN_POWER_LEVEL_DBM) / (RFM69_MAX_POWER_LEVEL_DBM - RFM69_MIN_POWER_LEVEL_DBM)); return result; } LOCAL bool RFM69_sanityCheck(void) { bool result = true; // default result &= RFM69_readReg(RFM69_REG_DATAMODUL) == (RFM69_DATAMODUL_DATAMODE_PACKET | RFM69_DATAMODUL_MODULATIONTYPE_FSK | RFM69_DATAMODUL_MODULATIONSHAPING_00); // check Bitrate result &= RFM69_readReg(RFM69_REG_BITRATEMSB) == MY_RFM69_BITRATE_MSB; result &= RFM69_readReg(RFM69_REG_BITRATELSB) == MY_RFM69_BITRATE_LSB; // default: 5KHz, (FDEV + BitRate / 2 <= 500KHz) result &= RFM69_readReg(RFM69_REG_FDEVMSB) == RFM69_FDEVMSB_50000; result &= RFM69_readReg(RFM69_REG_FDEVLSB) == RFM69_FDEVLSB_50000; // check network result &= RFM69_readReg(RFM69_REG_SYNCVALUE2) == MY_RFM69_NETWORKID; return result; } LOCAL void RFM69_setConfiguration(void) { const uint8_t CONFIG[][2] = { { RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY }, { RFM69_REG_DATAMODUL, RFM69_DATAMODUL_DATAMODE_PACKET | RFM69_DATAMODUL_MODULATIONTYPE_FSK | RFM69_DATAMODUL_MODULATIONSHAPING_00 }, { RFM69_REG_BITRATEMSB, MY_RFM69_BITRATE_MSB }, { RFM69_REG_BITRATELSB, MY_RFM69_BITRATE_LSB }, { RFM69_REG_FDEVMSB, RFM69_FDEVMSB_50000 }, { RFM69_REG_FDEVLSB, RFM69_FDEVLSB_50000 }, //{ RFM69_REG_FRFMSB, RFM69_FREQ_MSB }, //{ RFM69_REG_FRFMID, RFM69_FREQ_MID }, //{ RFM69_REG_FRFLSB, RFM69_FREQ_LSB }, { RFM69_REG_LNA, RFM69_LNA_ZIN_200 | RFM69_LNA_CURRENTGAIN }, { RFM69_REG_RXBW, RFM69_RXBW_DCCFREQ_010 | RFM69_RXBW_MANT_16 | RFM69_RXBW_EXP_2 }, //{ RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_01 }, { RFM69_REG_DIOMAPPING2, RFM69_DIOMAPPING2_CLKOUT_OFF }, { RFM69_REG_IRQFLAGS2, RFM69_IRQFLAGS2_FIFOOVERRUN }, { RFM69_REG_RSSITHRESH, 220 /*RFM69_RSSITHRESH_VALUE*/ }, { RFM69_REG_PREAMBLEMSB, 0 }, // default { RFM69_REG_PREAMBLELSB, 3 }, // default { RFM69_REG_SYNCCONFIG, RFM69_SYNC_ON | RFM69_SYNC_FIFOFILL_AUTO | RFM69_SYNC_SIZE_2 | RFM69_SYNC_TOL_0 }, { RFM69_REG_SYNCVALUE1, RFM69_SYNCVALUE1 }, { RFM69_REG_SYNCVALUE2, MY_RFM69_NETWORKID }, // changed from dcfree_none to dcfree_whitening { RFM69_REG_PACKETCONFIG1, RFM69_PACKET1_FORMAT_VARIABLE | RFM69_PACKET1_DCFREE_WHITENING | RFM69_PACKET1_CRC_ON | RFM69_PACKET1_CRCAUTOCLEAR_ON | RFM69_PACKET1_ADRSFILTERING_NODEBROADCAST }, { RFM69_REG_PAYLOADLENGTH, 66 }, // in variable length mode: the max frame size, not used in TX { RFM69_REG_NODEADRS, RFM69_BROADCAST_ADDRESS }, // init { RFM69_REG_BROADCASTADRS, RFM69_BROADCAST_ADDRESS }, { RFM69_REG_FIFOTHRESH, RFM69_FIFOTHRESH_TXSTART_FIFOTHRESH | (RFM69_HEADER_LEN - 1) }, // start transmitting when rfm69 header loaded, fifo level irq when header bytes received (irq asserted when n bytes exceeded) { RFM69_REG_PACKETCONFIG2, RFM69_PACKET2_RXRESTARTDELAY_2BITS | RFM69_PACKET2_AUTORXRESTART_OFF | RFM69_PACKET2_AES_OFF }, { RFM69_REG_TESTDAGC, RFM69_DAGC_IMPROVED_LOWBETA0 }, { 255, 0} }; for (uint8_t i = 0; CONFIG[i][0] != 255; i++) { RFM69_writeReg(CONFIG[i][0], CONFIG[i][1]); } } LOCAL bool RFM69_isModeReady(void) { const uint32_t enterMS = hwMillis(); while (hwMillis() - enterMS < RFM69_MODE_READY_TIMEOUT_MS) { if (RFM69_readReg(RFM69_REG_IRQFLAGS1) & RFM69_IRQFLAGS1_MODEREADY) { return true; } } return false; } LOCAL void RFM69_encrypt(const char* key) { (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); if (key != NULL) { RFM69_burstWriteReg(RFM69_REG_AESKEY1, key, 16); } RFM69_writeReg(RFM69_REG_PACKETCONFIG2, (RFM69_readReg(RFM69_REG_PACKETCONFIG2) & 0xFE) | (key ? 1 : 0)); } LOCAL rfm69_RSSI_t RFM69_readRSSI(bool forceTrigger) { // RSSI trigger not needed if DAGC is in continuous mode (we are) if (forceTrigger) { RFM69_writeReg(RFM69_REG_RSSICONFIG, RFM69_RSSI_START); uint16_t timeout = 0xFFFF; while (!(RFM69_readReg(RFM69_REG_RSSICONFIG) & RFM69_RSSI_DONE) && timeout--) { }; } return RFM69_readReg(RFM69_REG_RSSIVALUE); } /* UNUSED LOCAL uint8_t RFM69_getVersion(void) { return RFM69_readReg(RFM69_REG_VERSION); } LOCAL uint8_t RFM69_readTemperature(const uint8_t calFactor) { (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); RFM69_writeReg(RFM69_REG_TEMP1, RFM69_TEMP1_MEAS_START); const uint32_t enterMS = hwMillis(); while ((RFM69_readReg(RFM69_REG_TEMP1) & RFM69_TEMP1_MEAS_RUNNING) && hwMillis() - enterMS < 500); return ~RFM69_readReg(RFM69_REG_TEMP2) + RFM69_COURSE_TEMP_COEF + calFactor; // 'complement' corrects the slope, rising temp = rising val } LOCAL void RFM69_rcCalibration(void) { RFM69_writeReg(RFM69_REG_OSC1, RFM69_OSC1_RCCAL_START); const uint32_t enterMS = hwMillis(); while (!(RFM69_readReg(RFM69_REG_OSC1) & RFM69_OSC1_RCCAL_DONE) && hwMillis() - enterMS < 500) { }; } LOCAL uint8_t RFM69_setLNA(const uint8_t newReg) { const uint8_t oldReg = RFM69_readReg(RFM69_REG_LNA); RFM69_writeReg(RFM69_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 } LOCAL void RFM69_ATCmode(const bool onOff, const int16_t targetRSSI) { RFM69.ATCenabled = onOff; RFM69.ATCtargetRSSI = RFM69_RSSItoInternal(targetRSSI); } LOCAL bool RFM69_waitCAD(void) { const uint32_t enterMS = hwMillis(); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); while (RFM69.radioMode == RFM69_RADIO_MODE_RX && !RFM69.dataReceived && RFM69_RSSItoInternal(RFM69_readRSSI()) > RFM69_RSSItoInternal(RFM69_CSMA_LIMIT_DBM)) { if (hwMillis() - enterMS > RFM69_CSMA_TIMEOUT_MS) { return false; } } return true; } */ // ************************ LISTENMODE SECTION ************************ #if defined(MY_RFM69_ENABLE_LISTENMODE) LOCAL void RFM69_setListenConfig(const uint8_t listenResolIdle, const uint8_t listenResolRx, const uint8_t listenCriteria, const uint8_t listenEnd) { /* ListenResolIdle one of: RFM69_LISTEN_RESOL_IDLE_64US RFM69_LISTEN_RESOL_IDLE_4_1MS RFM69_LISTEN_RESOL_IDLE_262MS Sets the listen mode idle time to 64 usec, 4.1ms or 262 ms ListenResolRx one of: RFM69_LISTEN_RESOL_RX_64US RFM69_LISTEN_RESOL_RX_4_1MS RFM69_LISTEN_RESOL_RX_262MS Sets the listen mode Rx time to 64 usec, 4.1ms or 262 ms ListenCriteria Criteria for packet acceptance in listen mode, either: RFM69_LISTEN_CRITERIA_RSSI signal strength above RssiThreshold RFM69_LISTEN_CRITERIA_RSSI_SYNC Signal strength above RssiThresshold and SyncAddress matched. ListenEnd: Action taken after acceptance of a packet in Listen mode: RFM69_LISTEN_END_STAY_RX_LISTEN_STOP Chip stays in Rx mode, listen mode stops and must be disabled. RFM69_LISTEN_END_RX_UNTIL_LISTEN_STOP Chip stays in Rx mode until PayloadReady or Timeout interrupt occurs. Listen mode stops and must be disabled. RFM69_LISTEN_END_RX_UNTIL_LISTEN_RESUME Chip stays in Rx mode until PayloadReady or Timeout interrupt occurs. Listen mode then resumes in Idle State. FIFO lost at next RX wakeup. Default: (RFM69_LISTEN_RESOL_IDLE_4_1MS, RFM69_LISTEN_RESOL_RX_64US, RFM69_LISTEN_CRITERIA_RSSI, RFM69_LISTEN_END_RX_UNTIL_LISTEN_STOP) */ RFM69_writeReg(RFM69_REG_LISTEN1, listenResolIdle | listenResolRx | listenCriteria | listenEnd); } LOCAL void RFM69_setListenCoefIdle(const uint8_t coeffIdle) { /* Duration of the Idle phase in Listen mode. t ListenIdle = ListenCoefIdle * ListenResolIdle Default 0xf5; 245 */ RFM69_writeReg(RFM69_REG_LISTEN2, coeffIdle); } LOCAL void RFM69_setListenCoefRx(const uint8_t coeffRX) { /* Duration of the Rx phase in Listen mode. t ListenRx = ListenCoefRx * ListenResolRx Default 0x20; 32 */ RFM69_writeReg(RFM69_REG_LISTEN3, coeffRX); } LOCAL void RFM69_listenModeApplyHighSpeedSettings(void) { if (!_isHighSpeed) { return; } RFM69_writeReg(RFM69_REG_BITRATEMSB, RFM69_BITRATEMSB_200000); RFM69_writeReg(RFM69_REG_BITRATELSB, RFM69_BITRATELSB_200000); RFM69_writeReg(RFM69_REG_FDEVMSB, RFM69_FDEVMSB_100000); RFM69_writeReg(RFM69_REG_FDEVLSB, RFM69_FDEVLSB_100000); RFM69_writeReg(RFM69_REG_RXBW, RFM69_RXBW_DCCFREQ_000 | RFM69_RXBW_MANT_20 | RFM69_RXBW_EXP_0); // Force LNA to the highest gain //RFM69_writeReg(RFM69_REG_LNA, (RFM69_readReg(REG_LNA) << 2) | RF_LNA_GAINSELECT_MAX); } LOCAL void RFM69_listenModeReset(void) { // reset vars //DATALEN = 0; //SENDERID = 0; //TARGETID = 0; //PAYLOADLEN = 0; //ACK_REQUESTED = 0; //ACK_RECEIVED = 0; //LISTEN_BURST_REMAINING_MS = 0; } LOCAL void RFM69_ListenModeStart(void) { (void)RFM69_setRadioMode(RFM69_RADIO_MODE_LISTEN); /* RFM69_DEBUG(PSTR("RFM69:LSM:Start..\n")); while (RFM69_readReg(RFM69_REG_IRQFLAGS2) & RFM69_IRQFLAGS2_PACKETSENT == 0x00); // wait for ModeReady RFM69_listenModeReset(); detachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN)); attachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN), RFM69_interruptHandler, RISING); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); RFM69_writeReg(RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_01); RFM69_writeReg(RFM69_REG_FRFMSB, RFM69_readReg(RFM69_REG_FRFMSB) + 1); RFM69_writeReg(RFM69_REG_FRFLSB, RFM69_readReg(RFM69_REG_FRFLSB)); // MUST write to LSB to affect change! RFM69_listenModeApplyHighSpeedSettings(); RFM69_writeReg(RFM69_REG_PACKETCONFIG1, RFM69_PACKET1_FORMAT_VARIABLE | RFM69_PACKET1_DCFREE_WHITENING | RFM69_PACKET1_CRC_ON | RFM69_PACKET1_CRCAUTOCLEAR_ON); RFM69_writeReg(RFM69_REG_PACKETCONFIG2, RFM69_PACKET2_RXRESTARTDELAY_NONE | RFM69_PACKET2_AUTORXRESTART_ON | RFM69_PACKET2_AES_OFF); RFM69_writeReg(RFM69_REG_SYNCVALUE1, 0x5A); RFM69_writeReg(RFM69_REG_SYNCVALUE2, 0x5A); RFM69_setListenConfig(idleListenResolution, rxListenResolution, RFM69_LISTEN1_CRITERIA_RSSI, RFM69_LISTEN1_END_10); RFM69_setListenCoefIdle(idleListenCoef); RFM69_setListenCoefRx(rxListenCoef); RFM69_writeReg(RFM69_REG_RSSITHRESH, 180); RFM69_writeReg(RFM69_REG_RXTIMEOUT2, 75); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_STANDBY); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_ON | RFM69_OPMODE_STANDBY); */ } LOCAL bool RFM69_listenModeEnd(void) { RFM69_DEBUG(PSTR("RFM69:LSM:End..\n")); detachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN)); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTENABORT | RFM69_OPMODE_STANDBY); RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_STANDBY); RFM69_writeReg(RFM69_REG_RXTIMEOUT2, 0); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); // wait for ModeReady //if (!rfm69_ismodeready()) { // return false; //} RFM69_isModeReady(); RFM69_listenModeReset(); RFM69_reinitRadio(); } LOCAL bool RFM69_reinitRadio(void) { if (!RFM69_initialise(MY_RFM69_FREQUENCY)) { return false; } //TODO //if (_haveEncryptKey) RFM69_encrypt(_encryptKey); // Restore the encryption key if necessary //if (_isHighSpeed) RFM69_writeReg(RFM69_REG_LNA, (RFM69_readReg(RFM69_REG_LNA) & ~0x3) | RFM69_LNA_GAINSELECT_AUTO); return true; } LOCAL void RFM69_listenModeSendBurst(const uint8_t recipient, uint8_t* data, const uint8_t len) { detachInterrupt(digitalPinToInterrupt(MY_RFM69_IRQ_PIN)); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); RFM69_writeReg(RFM69_REG_PACKETCONFIG1, RFM69_PACKET1_FORMAT_VARIABLE | RFM69_PACKET1_DCFREE_WHITENING | RFM69_PACKET1_CRC_ON | RFM69_PACKET1_CRCAUTOCLEAR_ON); RFM69_writeReg(RFM69_REG_PACKETCONFIG2, RFM69_PACKET2_RXRESTARTDELAY_NONE | RFM69_PACKET2_AUTORXRESTART_ON | RFM69_PACKET2_AES_OFF); RFM69_writeReg(RFM69_REG_SYNCVALUE1, 0x5A); RFM69_writeReg(RFM69_REG_SYNCVALUE2, 0x5A); RFM69_listenModeApplyHighSpeedSettings(); RFM69_writeReg(RFM69_REG_FRFMSB, RFM69_readReg(RFM69_REG_FRFMSB) + 1); RFM69_writeReg(RFM69_REG_FRFLSB, RFM69_readReg(RFM69_REG_FRFLSB)); // MUST write to LSB to affect change! // TODO set high power level union { // union to simplify addressing of long and short parts of time offset int32_t l; uint8_t b[4]; } timeRemaining; uint16_t cycleDurationMs = listenCycleDurationUs / 1000; timeRemaining.l = cycleDurationMs; RFM69_DEBUG(PSTR("RFM69:LSM:Send burst for %d ms\n"), cycleDurationMs); (void)RFM69_setRadioMode(RFM69_RADIO_MODE_TX); uint32_t startTime = hwMillis(); while (timeRemaining.l > 0) { // send burst // write to FIFO TODO refactorize with func, check if send/sendframe could be used, and prepare packet struct RFM69_prepareSPITransaction(); RFM69_csn(LOW); SPI.transfer(RFM69_REG_FIFO | 0x80); SPI.transfer(len + 4); // two bytes for target and sender node, two bytes for the burst time remaining SPI.transfer(recipient); SPI.transfer(_address); // We send the burst time remaining with the packet so the receiver knows how long to wait before trying to reply SPI.transfer(timeRemaining.b[0]); SPI.transfer(timeRemaining.b[1]); for (uint8_t i = 0; i < len; i++) { SPI.transfer(((uint8_t*)data)[i]); } RFM69_csn(HIGH); RFM69_concludeSPITransaction(); while ((RFM69_readReg(RFM69_REG_IRQFLAGS2) & RFM69_IRQFLAGS2_FIFONOTEMPTY) != 0x00); // make sure packet is sent before putting more into the FIFO timeRemaining.l = cycleDurationMs - (hwMillis() - startTime); } (void)RFM69_setRadioMode(RFM69_RADIO_MODE_TX); RFM69_reinitRadio(); } LOCAL uint32_t RFM69_getUsForResolution(uint8_t resolution) { switch (resolution) { case RFM69_LISTEN1_RESOL_RX_64: case RFM69_LISTEN1_RESOL_IDLE_64: return 64; case RFM69_LISTEN1_RESOL_RX_4100: case RFM69_LISTEN1_RESOL_IDLE_4100: return 4100; case RFM69_LISTEN1_RESOL_RX_262000: case RFM69_LISTEN1_RESOL_IDLE_262000: return 262000; default: // Whoops return 0; } } LOCAL uint32_t RFM69_getCoefForResolution(uint8_t resolution, uint32_t duration) { uint32_t resolDuration = RFM69_getUsForResolution(resolution); uint32_t result = duration / resolDuration; // If the next-higher coefficient is closer, use that if (abs(duration - ((result + 1) * resolDuration)) < abs(duration - (result * resolDuration))) { return result + 1; } return result; } LOCAL bool RFM69_chooseResolutionAndCoef(uint8_t *resolutions, uint32_t duration, uint8_t& resolOut, uint8_t& coefOut) { for (int i = 0; resolutions[i]; i++) { uint32_t coef = RFM69_getCoefForResolution(resolutions[i], duration); if (coef <= 255) { coefOut = coef; resolOut = resolutions[i]; return true; } } // out of range return false; } LOCAL bool RFM69_listenModeSetDurations(uint32_t& rxDuration, uint32_t& idleDuration) { uint8_t rxResolutions[] = { RFM69_LISTEN1_RESOL_RX_64, RFM69_LISTEN1_RESOL_RX_4100, RFM69_LISTEN1_RESOL_RX_262000, 0 }; uint8_t idleResolutions[] = { RFM69_LISTEN1_RESOL_IDLE_64, RFM69_LISTEN1_RESOL_IDLE_4100, RFM69_LISTEN1_RESOL_IDLE_262000, 0 }; if (!RFM69_chooseResolutionAndCoef(rxResolutions, rxDuration, rxListenResolution, rxListenCoef)) { return false; } if (!RFM69_chooseResolutionAndCoef(idleResolutions, idleDuration, idleListenResolution, idleListenCoef)) { return false; } rxDuration = RFM69_getUsForResolution(rxListenResolution) * rxListenCoef; idleDuration = RFM69_getUsForResolution(idleListenResolution) * idleListenCoef; listenCycleDurationUs = rxDuration + idleDuration; return true; } LOCAL void RFM69_listenModeGetDurations(uint32_t &rxDuration, uint32_t &idleDuration) { rxDuration = RFM69_getUsForResolution(rxListenResolution) * rxListenCoef; idleDuration = RFM69_getUsForResolution(idleListenResolution) * idleListenCoef; } #endif #ifdef MY_DEBUG_VERBOSE_RFM69_REGISTERS LOCAL void RFM69_readAllRegs(void) { #ifdef RFM69_REGISTER_DETAIL int16_t capVal; //... State Variables for intelligent decoding uint8_t modeFSK = 0; int16_t bitRate = 0; int16_t freqDev = 0; int32_t freqCenter = 0; #endif RFM69_DEBUG(PSTR("RFM69:DUMP:Registers Address | HEX value \n")); for (uint8_t regAddr = 1; regAddr <= 0x4F; regAddr++) { uint8_t regVal = RFM69_readReg(regAddr); RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x Value=0x%02x\n"), regAddr, regVal); #ifdef RFM69_REGISTER_DETAIL switch (regAddr) { case 0x1: { RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x Controls the automatic Sequencer(see section 4.2)\n"), regAddr); if (0x80 & regVal) { RFM69_DEBUG(PSTR("RFM69:DUMP:SequencerOff : 1 -> Mode is forced by the user\n")); } else { RFM69_DEBUG( PSTR("RFM69:DUMP:SequencerOff : 0 -> Operating mode as selected with Mode bits in RegOpMode is automatically reached with the Sequencer\n")); } RFM69_DEBUG(PSTR("RFM69:DUMP:Enables Listen mode, should be enabled whilst in Standby mode\n")); if (0x40 & regVal) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenOn : 1 -> On\n")); } else { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenOn : 0->Off(see section 4.3)\n")); } RFM69_DEBUG( PSTR("RFM69:DUMP:Aborts Listen mode when set together with ListenOn=0 See section 4.3.4 for details (Always reads 0.)\n")); if (0x20 & regVal) { RFM69_DEBUG( PSTR("RFM69:DUMP:ERROR - ListenAbort should NEVER return 1 this is a write only register\n")); } RFM69_DEBUG(PSTR("RFM69:DUMP:Transceiver's operating modes\n")); capVal = (regVal >> 2) & 0x7; if (capVal == 0b000) { RFM69_DEBUG(PSTR("RFM69:DUMP:Mode : 000 -> Sleep mode (SLEEP)\n")); } else if (capVal = 0b001) { RFM69_DEBUG(PSTR("RFM69:DUMP:Mode : 001 -> Standby mode (STDBY)\n")); } else if (capVal = 0b010) { RFM69_DEBUG(PSTR("RFM69:DUMP:Mode : 010 -> Frequency Synthesizer mode (FS)\n")); } else if (capVal = 0b011) { RFM69_DEBUG(PSTR("RFM69:DUMP:Mode : 011 -> Transmitter mode (TX)\n")); } else if (capVal = 0b100) { RFM69_DEBUG(PSTR("RFM69:DUMP:Mode : 100 -> Receiver Mode (RX)\n")); } else { RFM69_DEBUG(PSTR("RFM69:DUMP:Mode : %d capVal \n"), capVal); } break; } case 0x2: { RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x Data Processing mode \n"), regAddr); capVal = (regVal >> 5) & 0x3; if (capVal == 0b00) { RFM69_DEBUG(PSTR("RFM69:DUMP:DataMode : 00 -> Packet mode\n")); } else if (capVal == 0b01) { RFM69_DEBUG(PSTR("RFM69:DUMP:DataMode : 01 -> reserved\n")); } else if (capVal == 0b10) { RFM69_DEBUG(PSTR("RFM69:DUMP:DataMode : 10 -> Continuous mode with bit synchronizer\n")); } else if (capVal == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:DataMode : 11 -> Continuous mode without bit synchronizer\n")); } RFM69_DEBUG(PSTR("RFM69:DUMP:Modulation scheme\n")); capVal = (regVal >> 3) & 0x3; if (capVal == 0b00) { RFM69_DEBUG(PSTR("RFM69:DUMP:Modulation Type : 00 -> FSK\n")); modeFSK = 1; } else if (capVal == 0b01) { RFM69_DEBUG(PSTR("RFM69:DUMP:Modulation Type : 01 -> OOK\n")); } else if (capVal == 0b10) { RFM69_DEBUG(PSTR("RFM69:DUMP:Modulation Type : 10 -> reserved\n")); } else if (capVal == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:Modulation Type : 11 -> reserved\n")); } if (modeFSK) { RFM69_DEBUG(PSTR("RFM69:DUMP:Data shaping : in FSK\n")); } else { RFM69_DEBUG(PSTR("RFM69:DUMP:Data shaping : in OOK\n")); } capVal = regVal & 0x3; if (modeFSK) { if (capVal == 0b00) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 00 -> no shaping\n")); } else if (capVal == 0b01) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 01 -> Gaussian filter, BT = 1.0\n")); } else if (capVal == 0b10) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 10 -> Gaussian filter, BT = 0.5\n")); } else if (capVal == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 11 -> Gaussian filter, BT = 0.3\n")); } } else { if (capVal == 0b00) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 00 -> no shaping\n")); } else if (capVal == 0b01) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 01 -> filtering with f(cutoff) = BR\n")); } else if (capVal == 0b10) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : 10 -> filtering with f(cutoff) = 2*BR\n")); } else if (capVal == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:ModulationShaping : ERROR - 11 is reserved\n")); } } break; } case 0x3: { bitRate = (regVal << 8); break; } case 0x4: { bitRate |= regVal; RFM69_DEBUG( PSTR("RFM69:DUMP:REG=0x%02x Bit Rate (Chip Rate when Manchester encoding is enabled)\n"), regAddr); uint32_t val = 32UL * 1000UL * 1000UL / bitRate; RFM69_DEBUG(PSTR("RFM69:DUMP:BitRate : %lu\n"), val); break; } case 0x5: { freqDev = ((regVal & 0x3f) << 8); break; } case 0x6: { freqDev |= regVal; uint32_t val = 61UL * freqDev; RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x Frequency deviation\n"), regAddr); RFM69_DEBUG(PSTR("RFM69:DUMP:Fdev : %lu\n"), val); break; } case 0x7: { uint32_t tempVal = regVal; freqCenter = (tempVal << 16); break; } case 0x8: { uint32_t tempVal = regVal; freqCenter = freqCenter | (tempVal << 8); break; } case 0x9: { freqCenter = freqCenter | regVal; uint32_t val = 61UL * freqCenter; RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x RF Carrier frequency \n"), regAddr); RFM69_DEBUG(PSTR("RFM69:DUMP:FRF : %lu\n"), val); break; } case 0xa: { RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x RC calibration control & status\n"), regAddr); if (0x40 & regVal) { RFM69_DEBUG(PSTR("RFM69:DUMP:RcCalDone : 1 -> RC calibration is over\n")); } else { RFM69_DEBUG(PSTR("RFM69:DUMP:RcCalDone : 0 -> RC calibration is in progress\n")); } break; } case 0xb: { RFM69_DEBUG( PSTR("RFM69:DUMP:REG=0x%02x Improved AFC routine for signals with modulation index lower than 2. Refer to section 3.4.16 for details\n"), regAddr); if (0x20 & regVal) { RFM69_DEBUG(PSTR("RFM69:DUMP:AfcLowBetaOn : 1 -> Improved AFC routine\n")); } else { RFM69_DEBUG(PSTR("RFM69:DUMP:AfcLowBetaOn : 0 -> Standard AFC routine\n")); } break; } case 0xc: { RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x Reserved\n"), regAddr); break; } case 0xd: { byte val; RFM69_DEBUG(PSTR("RFM69:DUMP:REG=0x%02x Resolution of Listen mode Idle time (calibrated RC osc)\n"), regAddr); val = regVal >> 6; if (val == 0b00) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolIdle : 00 -> reserved\n")); } else if (val == 0b01) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolIdle : 01 -> 64 us\n")); } else if (val == 0b10) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolIdle : 10 -> 4.1 ms\n")); } else if (val == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolIdle : 11 -> 262 ms\n")); } RFM69_DEBUG(PSTR("RFM69:DUMP:Resolution of Listen mode Rx time (calibrated RC osc)\n")); val = (regVal >> 4) & 0x3; if (val == 0b00) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolRx : 00 -> reserved\n")); } else if (val == 0b01) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolRx : 01 -> 64 us\n")); } else if (val == 0b10) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolRx : 10 -> 4.1 ms\n")); } else if (val == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenResolRx : 11 -> 262 ms\n")); } RFM69_DEBUG(PSTR("RFM69:DUMP:Criteria for packet acceptance in Listen mode\n")); if (0x8 & regVal) { RFM69_DEBUG( PSTR("RFM69:DUMP:ListenCriteria : 1 -> signal strength is above RssiThreshold and SyncAddress matched\n")); } else { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenCriteria : 0 -> signal strength is above RssiThreshold\n")); } RFM69_DEBUG(PSTR("RFM69:DUMP:Action taken after acceptance of a packet in Listen mode\n")); val = (regVal >> 1) & 0x3; if (val == 0b00) { RFM69_DEBUG( PSTR("RFM69:DUMP:ListenEnd : 00 -> chip stays in Rx mode. Listen mode stops and must be disabled (see section 4.3)\n")); } else if (val == 0b01) { RFM69_DEBUG( PSTR("RFM69:DUMP:ListenEnd : 01 -> chip stays in Rx mode until PayloadReady or Timeout interrupt occurs. It then goes to the mode defined by Mode. Listen mode stops and must be disabled (see section 4.3)\n")); } else if (val == 0b10) { RFM69_DEBUG( PSTR("RFM69:DUMP:ListenEnd : 10 -> chip stays in Rx mode until PayloadReady or Timeout occurs. Listen mode then resumes in Idle state. FIFO content is lost at next Rx wakeup.\n")); } else if (val == 0b11) { RFM69_DEBUG(PSTR("RFM69:DUMP:ListenEnd : 11 -> Reserved\n")); } break; } default: { } } #endif (void)regVal; } } #endif