boolean RF22ReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address) { // Assemble the message uint8_t thisSequenceNumber = ++_lastSequenceNumber; Timer t; uint8_t retries = 0; while (retries++ <= _retries) { setHeaderId(thisSequenceNumber); setHeaderFlags(0); sendto(buf, len, address); waitPacketSent(3000); // Never wait for ACKS to broadcasts: if (address == RF22_BROADCAST_ADDRESS) return true; if (retries > 1) _retransmissions++; t.start(); unsigned long thisSendTime = t.read_ms(); // Timeout does not include original transmit time // Compute a new timeout, random between _timeout and _timeout*2 // This is to prevent collisions on every retransmit // if 2 nodes try to transmit at the same time uint16_t timeout = _timeout + (_timeout * (rand() % 100) / 100); while (t.read_ms() < (thisSendTime + timeout)) { if (available()) { clearRxBuf(); // Not using recv, so clear it ourselves uint8_t from = headerFrom(); uint8_t to = headerTo(); uint8_t id = headerId(); uint8_t flags = headerFlags(); // Now have a message: is it our ACK? if ( from == address && to == _thisAddress && (flags & RF22_FLAGS_ACK) && (id == thisSequenceNumber)) { // Its the ACK we are waiting for return true; } else if ( !(flags & RF22_FLAGS_ACK) && (id == _seenIds[from])) { // This is a request we have already received. ACK it again acknowledge(id, from); } // Else discard it } // Not the one we are waiting for, maybe keep waiting until timeout exhausted } // Timeout exhausted, maybe retry } return false; }
bool RHReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address) { // Assemble the message uint8_t thisSequenceNumber = ++_lastSequenceNumber; uint8_t retries = 0; while (retries++ <= _retries) { setHeaderId(thisSequenceNumber); setHeaderFlags(0); sendto(buf, len, address); waitPacketSent(); // Never wait for ACKS to broadcasts: if (address == RH_BROADCAST_ADDRESS) return true; if (retries > 1) _retransmissions++; unsigned long thisSendTime = millis(); // Timeout does not include original transmit time // Compute a new timeout, random between _timeout and _timeout*2 // This is to prevent collisions on every retransmit // if 2 nodes try to transmit at the same time uint16_t timeout = _timeout + (_timeout * random(0, 256) / 256); while ((millis() - thisSendTime) < timeout) { if (available()) { uint8_t from, to, id, flags; if (recvfrom(0, 0, &from, &to, &id, &flags)) // Discards the message { // Now have a message: is it our ACK? if ( from == address && to == _thisAddress && (flags & RH_FLAGS_ACK) && (id == thisSequenceNumber)) { // Its the ACK we are waiting for return true; } else if ( !(flags & RH_FLAGS_ACK) && (id == _seenIds[from])) { // This is a request we have already received. ACK it again acknowledge(id, from); } // Else discard it } } // Not the one we are waiting for, maybe keep waiting until timeout exhausted YIELD; } // Timeout exhausted, maybe retry YIELD; } return false; }
void RHReliableDatagram::acknowledge(uint8_t id, uint8_t from) { setHeaderId(id); setHeaderFlags(RH_FLAGS_ACK); // We would prefer to send a zero length ACK, // but if an RH_RF22 receives a 0 length message with a CRC error, it will never receive // a 0 length message again, until its reset, which makes everything hang :-( // So we send an ACK of 1 octet // REVISIT: should we send the RSSI for the information of the sender? uint8_t ack = '!'; sendto(&ack, sizeof(ack), from); waitPacketSent(); }
boolean RF22::init() { // Wait for RF22 POR (up to 16msec) delay(16); // Initialise the slave select pin pinMode(_slaveSelectPin, OUTPUT); digitalWrite(_slaveSelectPin, HIGH); // start the SPI library: // Note the RF22 wants mode 0, MSB first and default to 1 Mbps _spi->begin(); _spi->setDataMode(SPI_MODE0); _spi->setBitOrder(MSBFIRST); _spi->setClockDivider(SPI_CLOCK_DIV16); // (16 Mhz / 16) = 1 MHz delay(100); // Software reset the device reset(); // Get the device type and check it // This also tests whether we are really connected to a device _deviceType = spiRead(RF22_REG_00_DEVICE_TYPE); if ( _deviceType != RF22_DEVICE_TYPE_RX_TRX && _deviceType != RF22_DEVICE_TYPE_TX) return false; // Set up interrupt handler if (_interrupt == 0) { _RF22ForInterrupt[0] = this; attachInterrupt(0, RF22::isr0, LOW); } else if (_interrupt == 1) { _RF22ForInterrupt[1] = this; attachInterrupt(1, RF22::isr1, LOW); } else return false; clearTxBuf(); clearRxBuf(); // Most of these are the POR default spiWrite(RF22_REG_7D_TX_FIFO_CONTROL2, RF22_TXFFAEM_THRESHOLD); spiWrite(RF22_REG_7E_RX_FIFO_CONTROL, RF22_RXFFAFULL_THRESHOLD); spiWrite(RF22_REG_30_DATA_ACCESS_CONTROL, RF22_ENPACRX | RF22_ENPACTX | RF22_ENCRC | RF22_CRC_CRC_16_IBM); // Configure the message headers // Here we set up the standard packet format for use by the RF22 library // 8 nibbles preamble // 2 SYNC words 2d, d4 // Header length 4 (to, from, id, flags) // 1 octet of data length (0 to 255) // 0 to 255 octets data // 2 CRC octets as CRC16(IBM), computed on the header, length and data // On reception the to address is check for validity against RF22_REG_3F_CHECK_HEADER3 // or the broadcast address of 0xff // If no changes are made after this, the transmitted // to address will be 0xff, the from address will be 0xff // and all such messages will be accepted. This permits the out-of the box // RF22 config to act as an unaddresed, unreliable datagram service spiWrite(RF22_REG_32_HEADER_CONTROL1, RF22_BCEN_HEADER3 | RF22_HDCH_HEADER3); spiWrite(RF22_REG_33_HEADER_CONTROL2, RF22_HDLEN_4 | RF22_SYNCLEN_2); setPreambleLength(8); uint8_t syncwords[] = { 0x2d, 0xd4 }; setSyncWords(syncwords, sizeof(syncwords)); setPromiscuous(false); // Check the TO header against RF22_DEFAULT_NODE_ADDRESS spiWrite(RF22_REG_3F_CHECK_HEADER3, RF22_DEFAULT_NODE_ADDRESS); // Set the default transmit header values setHeaderTo(RF22_DEFAULT_NODE_ADDRESS); setHeaderFrom(RF22_DEFAULT_NODE_ADDRESS); setHeaderId(0); setHeaderFlags(0); // Ensure the antenna can be switched automatically according to transmit and receive // This assumes GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit // This assumes GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive spiWrite (RF22_REG_0B_GPIO_CONFIGURATION0, 0x12) ; // TX state spiWrite (RF22_REG_0C_GPIO_CONFIGURATION1, 0x15) ; // RX state // Enable interrupts spiWrite(RF22_REG_05_INTERRUPT_ENABLE1, RF22_ENTXFFAEM | RF22_ENRXFFAFULL | RF22_ENPKSENT | RF22_ENPKVALID | RF22_ENCRCERROR | RF22_ENFFERR); spiWrite(RF22_REG_06_INTERRUPT_ENABLE2, RF22_ENPREAVAL); // Set some defaults. An innocuous ISM frequency, and reasonable pull-in setFrequency(434.0, 0.05); // setFrequency(900.0); // Some slow, reliable default speed and modulation setModemConfig(FSK_Rb2_4Fd36); // setModemConfig(FSK_Rb125Fd125); // Minimum power setTxPower(RF22_TXPOW_8DBM); // setTxPower(RF22_TXPOW_17DBM); return true; }
bool RHReliableDatagram::sendtoWait(uint8_t* buf, uint8_t len, uint8_t address) { // Assemble the message uint8_t thisSequenceNumber = ++_lastSequenceNumber; uint8_t retries = 0; while (retries++ <= _retries) { setHeaderId(thisSequenceNumber); // Set and clear header flags depending on if this is an // initial send or a retry. uint8_t headerFlagsToSet = RH_FLAGS_NONE; // Always clear the ACK flag uint8_t headerFlagsToClear = RH_FLAGS_ACK; if (retries == 1) { // On an initial send, clear the RETRY flag in case // it was previously set headerFlagsToClear |= RH_FLAGS_RETRY; } else { // Not an initial send, set the RETRY flag headerFlagsToSet = RH_FLAGS_RETRY; } setHeaderFlags(headerFlagsToSet, headerFlagsToClear); sendto(buf, len, address); waitPacketSent(); // Never wait for ACKS to broadcasts: if (address == RH_BROADCAST_ADDRESS) return true; if (retries > 1) _retransmissions++; unsigned long thisSendTime = millis(); // Timeout does not include original transmit time // Compute a new timeout, random between _timeout and _timeout*2 // This is to prevent collisions on every retransmit // if 2 nodes try to transmit at the same time #if (RH_PLATFORM == RH_PLATFORM_RASPI) // use standard library random(), bugs in random(min, max) uint16_t timeout = _timeout + (_timeout * (random() & 0xFF) / 256); #else uint16_t timeout = _timeout + (_timeout * random(0, 256) / 256); #endif int32_t timeLeft; while ((timeLeft = timeout - (millis() - thisSendTime)) > 0) { if (waitAvailableTimeout(timeLeft)) { uint8_t from, to, id, flags; if (recvfrom(0, 0, &from, &to, &id, &flags)) // Discards the message { // Now have a message: is it our ACK? if ( from == address && to == _thisAddress && (flags & RH_FLAGS_ACK) && (id == thisSequenceNumber)) { // Its the ACK we are waiting for return true; } else if ( !(flags & RH_FLAGS_ACK) && (id == _seenIds[from])) { // This is a request we have already received. ACK it again acknowledge(id, from); } // Else discard it } } // Not the one we are waiting for, maybe keep waiting until timeout exhausted YIELD; } // Timeout exhausted, maybe retry YIELD; } // Retries exhausted return false; }
bool RH_RF22::init() { if (!RHSPIDriver::init()) return false; // Software reset the device reset(); // Get the device type and check it // This also tests whether we are really connected to a device _deviceType = spiRead(RH_RF22_REG_00_DEVICE_TYPE); if ( _deviceType != RH_RF22_DEVICE_TYPE_RX_TRX && _deviceType != RH_RF22_DEVICE_TYPE_TX) { return false; } // Add by Adrien van den Bossche <*****@*****.**> for Teensy #if defined (__MK20DX128__) || defined (__MK20DX256__) // ARM M4 requires the below. else pin interrupt doesn't work properly. pinMode(_interruptPin, INPUT); #endif // Enable interrupt output on the radio. Interrupt line will now go high until // an interrupt occurs spiWrite(RH_RF22_REG_05_INTERRUPT_ENABLE1, RH_RF22_ENTXFFAEM | RH_RF22_ENRXFFAFULL | RH_RF22_ENPKSENT | RH_RF22_ENPKVALID | RH_RF22_ENCRCERROR | RH_RF22_ENFFERR); spiWrite(RH_RF22_REG_06_INTERRUPT_ENABLE2, RH_RF22_ENPREAVAL); // Set up interrupt handler // Since there are a limited number of interrupt glue functions isr*() available, // we can only support a limited number of devices simultaneously // On some devices, notably most Arduinos, the interrupt pin passed in is actually the // interrupt number. You have to figure out the interruptnumber-to-interruptpin mapping // yourself based on knowledge of what Arduino board you are running on. _deviceForInterrupt[_interruptCount] = this; if (_interruptCount == 0) attachInterrupt(_interruptPin, isr0, FALLING); else if (_interruptCount == 1) attachInterrupt(_interruptPin, isr1, FALLING); else if (_interruptCount == 2) attachInterrupt(_interruptPin, isr2, FALLING); else return false; // Too many devices, not enough interrupt vectors _interruptCount++; setModeIdle(); clearTxBuf(); clearRxBuf(); // Most of these are the POR default spiWrite(RH_RF22_REG_7D_TX_FIFO_CONTROL2, RH_RF22_TXFFAEM_THRESHOLD); spiWrite(RH_RF22_REG_7E_RX_FIFO_CONTROL, RH_RF22_RXFFAFULL_THRESHOLD); spiWrite(RH_RF22_REG_30_DATA_ACCESS_CONTROL, RH_RF22_ENPACRX | RH_RF22_ENPACTX | RH_RF22_ENCRC | (_polynomial & RH_RF22_CRC)); // Configure the message headers // Here we set up the standard packet format for use by the RH_RF22 library // 8 nibbles preamble // 2 SYNC words 2d, d4 // Header length 4 (to, from, id, flags) // 1 octet of data length (0 to 255) // 0 to 255 octets data // 2 CRC octets as CRC16(IBM), computed on the header, length and data // On reception the to address is check for validity against RH_RF22_REG_3F_CHECK_HEADER3 // or the broadcast address of 0xff // If no changes are made after this, the transmitted // to address will be 0xff, the from address will be 0xff // and all such messages will be accepted. This permits the out-of the box // RH_RF22 config to act as an unaddresed, unreliable datagram service spiWrite(RH_RF22_REG_32_HEADER_CONTROL1, RH_RF22_BCEN_HEADER3 | RH_RF22_HDCH_HEADER3); spiWrite(RH_RF22_REG_33_HEADER_CONTROL2, RH_RF22_HDLEN_4 | RH_RF22_SYNCLEN_2); setPreambleLength(8); uint8_t syncwords[] = { 0x2d, 0xd4 }; setSyncWords(syncwords, sizeof(syncwords)); setPromiscuous(false); // Check the TO header against RH_RF22_DEFAULT_NODE_ADDRESS spiWrite(RH_RF22_REG_3F_CHECK_HEADER3, RH_RF22_DEFAULT_NODE_ADDRESS); // Set the default transmit header values setHeaderTo(RH_RF22_DEFAULT_NODE_ADDRESS); setHeaderFrom(RH_RF22_DEFAULT_NODE_ADDRESS); setHeaderId(0); setHeaderFlags(0); // Set some defaults. An innocuous ISM frequency, and reasonable pull-in setFrequency(434.0, 0.05); // setFrequency(900.0); // Some slow, reliable default speed and modulation setModemConfig(FSK_Rb2_4Fd36); // setModemConfig(FSK_Rb125Fd125); // Lowish power setTxPower(RH_RF22_TXPOW_8DBM); return true; }
bool RF22::init() { //TODO check. wiringPiSetup(); // // Wait for RF22 POR (up to 16msec) // delay(16); if (wiringPiSPISetup(_slaveSelectPin, 1000000) == -1) { printf("Could not initialize SPI\n"); return false; } delay(20); // Software reset the device reset(); delay(1); // Get the device type and check it // This also tests whether we are really connected to a device _deviceType = spiRead(RF22_REG_00_DEVICE_TYPE); if (_deviceType != RF22_DEVICE_TYPE_RX_TRX && _deviceType != RF22_DEVICE_TYPE_TX) return false; // Set up interrupt handler if (_interrupt == 0) { _RF22ForInterrupt[0] = this; //attaching interrupt to gpio pins. see wiringPi.com -> gpio chart wiringPiISR(6, INT_EDGE_FALLING, RF22::isr0); } else if (_interrupt == 1) { _RF22ForInterrupt[1] = this; wiringPiISR(4, INT_EDGE_FALLING, RF22::isr1); } else return false; clearTxBuf(); clearRxBuf(); // Most of these are the POR default spiWrite(RF22_REG_7D_TX_FIFO_CONTROL2, RF22_TXFFAEM_THRESHOLD); spiWrite(RF22_REG_7E_RX_FIFO_CONTROL, RF22_RXFFAFULL_THRESHOLD); spiWrite(RF22_REG_30_DATA_ACCESS_CONTROL, RF22_ENPACRX | RF22_ENPACTX | RF22_ENCRC | RF22_CRC_CRC_16_IBM); // Configure the message headers // Here we set up the standard packet format for use by the RF22 library // 8 nibbles preamble // 2 SYNC words 2d, d4 // Header length 4 (to, from, id, flags) // 1 octet of data length (0 to 255) // 0 to 255 octets data // 2 CRC octets as CRC16(IBM), computed on the header, length and data // On reception the to address is check for validity against RF22_REG_3F_CHECK_HEADER3 // or the broadcast address of 0xff // If no changes are made after this, the transmitted // to address will be 0xff, the from address will be 0xff // and all such messages will be accepted. This permits the out-of the box // RF22 config to act as an unaddresed, unreliable datagram service spiWrite(RF22_REG_32_HEADER_CONTROL1, RF22_BCEN_HEADER3 | RF22_HDCH_HEADER3); spiWrite(RF22_REG_33_HEADER_CONTROL2, RF22_HDLEN_4 | RF22_SYNCLEN_2 | 0x1); setPreambleLength(8); uint8_t syncwords[] = { 0x2d, 0xd4 }; setSyncWords(syncwords, sizeof(syncwords)); setPromiscuous(false); // Check the TO header against RF22_DEFAULT_NODE_ADDRESS spiWrite(RF22_REG_3F_CHECK_HEADER3, RF22_DEFAULT_NODE_ADDRESS); // Set the default transmit header values setHeaderTo(RF22_DEFAULT_NODE_ADDRESS); setHeaderFrom(RF22_DEFAULT_NODE_ADDRESS); setHeaderId(0); setHeaderFlags(0); // Ensure the antenna can be switched automatically according to transmit and receive // This assumes GPIO0(out) is connected to TX_ANT(in) to enable tx antenna during transmit // This assumes GPIO1(out) is connected to RX_ANT(in) to enable rx antenna during receive spiWrite(RF22_REG_0B_GPIO_CONFIGURATION0, 0x12); // TX state spiWrite(RF22_REG_0C_GPIO_CONFIGURATION1, 0x15); // RX state //spiWrite(RF22_REG_0D_GPIO_CONFIGURATION2, 0x14); //raw data to port 4 have to activate it later? //put into a mode where the interrupt frequency is not as high as the clock of the rfm22. //the raspberry is connected to that pin and would freeze if an isr is registered. spiWrite(RF22_REG_0D_GPIO_CONFIGURATION2, 0x13); // Enable interrupts spiWrite(RF22_REG_05_INTERRUPT_ENABLE1, RF22_ENTXFFAEM | RF22_ENRXFFAFULL | RF22_ENPKSENT | RF22_ENPKVALID | RF22_ENCRCERROR | RF22_ENFFERR); spiWrite(RF22_REG_06_INTERRUPT_ENABLE2, RF22_ENPREAVAL); // Set some defaults. An innocuous ISM frequency, and reasonable pull-in setFrequency(434.0, 0.05); // setFrequency(900.0); // Some slow, reliable default speed and modulation setModemConfig(FSK_Rb2_4Fd36); // setModemConfig(FSK_Rb125Fd125); // Minimum power setTxPower(RF22_TXPOW_8DBM); // setTxPower(RF22_TXPOW_17DBM); return true; }