Author Topic: Speeding Up The RFM69 Library  (Read 15491 times)

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #15 on: January 15, 2017, 04:03:31 PM »
Thanks for all the good ideas everyone.  There are lots of interesting things to test out here.  I would love to build a send only version that is self contained, very small code size (cause I always shoot for that), uses auto mode to transmit and then immediately go back to sleep, and can actually begin transmitting as the FIFO is still being written because that is just cool and why not reduce latency.  Plus if the radio can be putting data into the FIFO while it is transmitting and then automatically put itself to sleep once the packet is sent, the radio itself will spend less time awake and more time asleep.  So faster and less power.  Hopefully once I have something working someone with a scope can verify that this is the case.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #16 on: January 19, 2017, 08:00:41 AM »
Well, I'm quite impressed with automatic mode so far.  It took an entire 3 minutes to implement (I'm really not used to things working on the first try with the RFM69W).  The uC is now awake for 324 microseconds and packets are still getting out just as before.  This 324 microseconds breaks down as follows:
    292 microseconds = get a Vin, Temp, RH measurement
     32 microseconds = write 11 bytes to the FIFO

Without having to manually switch modes my send frame code now looks like this:
Code: [Select]
static inline void SendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize) {
  SELECT;                      // write to FIFO using SPI burst mode
  SPI_XFER(REG_FIFO | 0x80);
  SPI_XFER(bufferSize + 3);    // LEN byte
  SPI_XFER(toAddress);         // 1st byte
  SPI_XFER(NODEID);            // 2nd byte
  SPI_XFER(0x00);              // 3rd byte
  for (uint8_t i = 0; i < bufferSize; i++) SPI_XFER(((uint8_t*) buffer)[i]);  // Write 6 more bytes to the FIFO
  UNSELECT;
}

Someone with a scope would have to verify whether or not the radio spends more/less/the same time drawing current but with certainty the uC is awake less using auto mode.  I still have some decoupling to do from the Arduino libraries, and general code clean up/compaction, and I do want to add back 4 error check bits, but this super stripped down power optimized code for nodes that only send back data is looking very promising.

EDIT: Forgot to mention that I came across an initialization setting that shaves 30 microseconds off a normal broadcast.  Not sure what if any the implications are of a faster PA ramp rate but this certainly sped up my manual mode transmissions:
Code: [Select]
writeReg(REG_PARAMP, B00001111);    // Save 30us by speeding up the PA ramp rate
« Last Edit: January 19, 2017, 08:07:52 AM by ChemE »

emjay

  • Full Member
  • ***
  • Posts: 119
  • Country: nl
Re: Speeding Up The RFM69 Library
« Reply #17 on: January 19, 2017, 07:44:33 PM »
@ChemE,

Quote
the implications are of a faster PA ramp rate

In a phrase, spectral splatter.  Just look at it as the start of an OOK transmission (no RF -> full RF).  OOK of course has a spectrum centered around the raw carrier frequency with skirts that get wider as the modulation rate increases. Now if you allow the PA to race from zero to max in the shortest time, this is analogous to a high baud rate - hence the skirts are wide, potentially upsetting adjacent channels. 
Unfortunately you need a good spectrum analyser to catch this - it's beyond the capability of a typical "dongle" SA
 

WhiteHare

  • Hero Member
  • *****
  • Posts: 1300
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #18 on: January 19, 2017, 08:52:40 PM »
In terms of receiving, I can see there'd be a palpable benefit in emptying the receive FIFO as it fills rather than waiting for it to fully fill: you can issue an ACK faster.  Of course, to do it you have to trade away hardware encryption (since that works on the entire FIFO queue all at once), but, meh, for most people full-bore encryption probably isn't really needed anyway.
« Last Edit: January 19, 2017, 09:28:10 PM by WhiteHare »

perky

  • Hero Member
  • *****
  • Posts: 873
  • Country: gb
Re: Speeding Up The RFM69 Library
« Reply #19 on: January 20, 2017, 06:48:03 AM »
A proprietory hashing algorithm that uses a counter and a unique serial number would probably be sufficient in place of encryption, that's extremely quick to calculate.
Mark.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #20 on: January 20, 2017, 08:27:29 PM »
**WARNING:  This code is just for W's not HW's.  My code might screw up your HW if you don't make adjustments.

Uff, it took vastly longer than I thought it would, but I've finally stripped out all references to the Arduino libraries as well as taken just a teeny subset of Felix's library and rewritten it to make use of auto mode as well as sped that up as much as I could.  The only outside reference is to LowPower for sleep duties. 

Regarding code size, I'm down to 1,418 bytes with the fastest version of the code which uses always inline on some RFM69CW routines.  With that macro commented out, the compiler will optimize for smaller code which adds 8 microseconds to the loop but drops the hex file down to 1,084 bytes.  The difference between fast and "slow" is 312 microseconds vs. 304 microseconds of awake time to get a Temp, RH, Vin measurement and send it all to the radio.  Almost all of that is the brutally slow I2C conversation.

Main program
Code: [Select]
include "HTU21D.h"
#include "LowPower.h"
#include "ADC.h"
#include "RFM69CW.h"
#define DEBUG 0

int main(void) {
  uint8_t data[6];  //16-bit temp, 16-bit RH, 16-bit Vcc
  #if DEBUG
    uint16_t startT, elapsed;
    startT = micros();           // ==================== START THE CLOCK ====================
  #endif
 
  Fast_ADC_Init();    // Initialize the AVR registers needed
  RadioInit();  // Configure the radio while it is asleep
  initTWI();  // Initialize the I2C bus at 400kHz
  #if DEBUG
    elapsed = micros()-startT;   // ==================== STOP THE CLOCK ====================
    Serial.begin(115200);
    Serial.print("\nInitialization took ");
    Serial.print(elapsed);
    Serial.print(" us\n");
    _delay_ms(2);
  #endif

  for(;;) {  // Loop forever
    #if DEBUG
      startT = micros();           // ==================== START THE CLOCK ====================
    #endif
    ENABLE_ADC;                                            // Turn on the ADC just prior to needing it
    START_ADC_CONVERSION;                                  // start a conversion
    issueCommand(WRITE_USER_REGISTER, ELEVEN_BIT_TEMP);    // Change the measurement resolution to 11 bits
    issueCommand(TRIGGER_TEMP_MEASURE_NOHOLD,0);           // Begin a temperature measurement
    data[5] = ADCL;                                        // Avoid 16-bit math in this loop since it adds 40us
    data[6] = ADCH;                                        // Read back the results of the ADC
    DISABLE_ADC;                                           // Turn off the ADC to save power
    LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);      // Sleep while the sensor measures...
    readRaw(&data[0]);                                     // Retreive the measurement results
    issueCommand(WRITE_USER_REGISTER, EIGHT_BIT_RH);       // Change the measurement resolution to 8 bits
    issueCommand(TRIGGER_HUMD_MEASURE_NOHOLD,0);           // Begin a RH measurement
    LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);      // Sleep while the sensor measures...
    readRaw(&data[2]);                                     // Retreive the measurement results
    TWCR = (1<<TWINT)|(1<<TWEN)| (1<<TWSTO);               // stop the TWI
    SendFrame(RECEIVER, data, 6);                          // Send the data
    #if DEBUG
      elapsed = micros()-startT;   // ==================== STOP THE CLOCK ====================
      float Tamb = ((uint16_t) (data[0]<<8 | data[1])) * (316.296 / 65535.0) - 52.33;
      float RHamb = ((uint16_t) (data[2]<<8 | data[3])) * (125.0 / 65535.0) - 6.0;
      Serial.print("\nTemperature: ");
      Serial.print(Tamb);
      Serial.print("\tHumidity: ");
      Serial.print(RHamb);
      Serial.print("\t\tVcc: ");
      Serial.print(112296/(data[6]<<8 | data[5]));
      Serial.print("\t\tLoop took: ");
      Serial.print(elapsed);
      Serial.print(" us");
      _delay_ms(4);  // Give the UART time to get our output across
      Serial.flush();
    #endif
   
    for ( uint8_t sleep_count=0; sleep_count<1; sleep_count++) LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);    // Sleep for 32 seconds
  }  // end for
}  // end main

ADC.h
Code: [Select]
#include "Arduino.h"

// Define various ADC prescaler
#define       ADC_PS_16               (1 << ADPS2)
#define       ADC_PS_32               (1 << ADPS2) |                (1 << ADPS0)
#define       ADC_PS_64               (1 << ADPS2) | (1 << ADPS1)
#define       ADC_PS_128              (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)
#define       sbi(sfr, bit)           (_SFR_BYTE(sfr) |= _BV(bit))
#define       cbi(sfr, bit)           (_SFR_BYTE(sfr) &= ~_BV(bit))
#define       START_ADC_CONVERSION    sbi(ADCSRA, ADSC);  // start a conversion
#define       ENABLE_ADC              cbi(PRR, PRADC); ADCSRA |= bit(ADEN); // Enable the ADC
#define       DISABLE_ADC             cbi(ADCSRA, ADEN); sbi(PRR, PRADC); // Disable the ADC to save power

static inline void Fast_ADC_Init(void) {
  // Timer 0 initialization from wiring.c for a ATmega 328P (Arduino Uno rev 3) + 12 bytes to sketch size
  TCCR0A = _BV(WGM01) | _BV(WGM00);      // set timer 0 prescale factor to 64
  TCCR0B = _BV(CS01) | _BV(CS00);        // set timer 0 prescale factor to 64
  TIMSK0 = _BV(TOIE0);                 // enable timer 0 overflow interrupt
 
  // Timer 2 initialization from wiring.c for an ATmega 328P (Arduino Uno rev 3) + 20 bytes to sketch size
  TCCR2A |= _BV(COM2A1) | _BV(WGM20);    // Enable timer 2 to _delay_ms() works properly
  TCCR2B |= CS22;                        // set clkT2S/64 (From prescaler)
 
  // ADC Housekeeping
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);    // Set the multiplexer to read the internal bandgap voltage
  ADCSRA |= ADC_PS_32;    // set our own prescaler to 32
}

HTU21D.h
Code: [Select]
#include "Arduino.h"
#define   BAUD_RATE                     8000000ul
#define   TRIGGER_TEMP_MEASURE_NOHOLD   0xF3
#define   TRIGGER_HUMD_MEASURE_NOHOLD   0xF5
#define   WRITE_USER_REGISTER           0xE6
#define   ELEVEN_BIT_TEMP               B10000011
#define   EIGHT_BIT_RH                  B00000011
#define   SLA_W                         TWDR = (0x40 << 1)
#define   SLA_R                         TWDR = ((0x40 << 1) + 0x01)
#define   START_TWI                     TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); WAIT_FOR_TWI_INT
#define   RESTART_TWI                   TWCR = (1<<TWINT) | (1<<TWEN);              WAIT_FOR_TWI_INT
#define   RESTART_TWI_ACK               TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);  WAIT_FOR_TWI_INT
#define   WAIT_FOR_TWI_INT              while (!(TWCR & (1<<TWINT)) && ++counter)
#define   STOP_TWI                      TWCR = (1<<TWINT)|(1<<TWEN)| (1<<TWSTO)
#define   NOT_READY                     (TWSR & 0xF8) == 0x48 && ++counter

static inline void initTWI() {
  DDRC |= (1<<PC3) | (1<<PC2);
  PORTC |= (1<<PC2) | (1<<PC4) | (1<<PC5);
  TWBR=1;  //TWBR = ((F_CPU / BAUD_RATE) - 16) / 2;
}

static inline void issueCommand(uint8_t comm, uint8_t res) {
  uint16_t counter;
  START_TWI; 
  SLA_W;
  RESTART_TWI;
  TWDR = comm;    // Send the command
  RESTART_TWI;
  if (comm == WRITE_USER_REGISTER) {  // Send the new resolution
    TWDR = res;
    RESTART_TWI;
  } else {    // Issue a stop on the I2C bus so we can enter sleep
    STOP_TWI;
  }
}

static inline void readRaw(uint8_t *ptr) {
  uint16_t counter;
  do {  // Start + SLA(R) until we get an ACK
    START_TWI;
    SLA_R;
    RESTART_TWI;
  } while (NOT_READY);

  // Measurement is ready, read back 2 bytes an store them at the address of ptr
  RESTART_TWI_ACK;    // Set the ACK bit to let the transmitter know we need another byte
  *ptr++ = TWDR; // Write the MSB
  RESTART_TWI;    // Set the NACK bit to let the transmitter know we are done
  *ptr = TWDR & 0xFC; // Write the LSB with the status bits masked off
}

RFM69CW.h
Code: [Select]
#include "Arduino.h"
#define         OPT_FLAG                                //__attribute__((always_inline))  // Comment this definition out to optimize for size
// ============================== User Definitions ==============================
#define         NETWORKID                               100    //the same on all nodes that talk to each other - 170 is 10101010 DC free value
#define         RECEIVER                                1      // ID of the gateway that all nodes report back to
#define         NODEID                                  2      // ID of this Node
#define SS_PIN                 PB2    // Slave select pin

// ============================== SPI Definitions ==============================
#define SELECT                 noInterrupts(); PORTB &= ~(1<<SS_PIN)
#define UNSELECT                 SS_WRITE_HIGH; interrupts()
#define    SS_WRITE_HIGH                      PORTB |= 1<<SS_PIN
#define         WAIT_WHILE_SPI_BUSY                     asm volatile("nop"); while (!(SPSR & 1<<SPIF))

// ============================== RFM69CW Definitions ==============================
#define         SLEEP_MODE                              B00000000
#define         AUTO_TRANSMITTER                        B01011011    // Enter = FIFO level; Exit = Packet Sent; Intermediate Mode = TX
#define         CHANGE_OP_MODE(mode)                    writeReg(REG_OPMODE, mode)
#define         SET_POWER_LEVEL(level)                  writeReg(REG_PALEVEL, 0x80 | (level & 0x0F));  // Only works for the RFM69CW not the RFM69HCW
#define         REG_FIFO                                0x00
#define         REG_OPMODE                              0x01
#define         REG_BITRATEMSB                          0x03
#define         REG_BITRATELSB                          0x04
#define         REG_FDEVMSB                             0x05
#define         REG_FDEVLSB                             0x06
#define         REG_PALEVEL                             0x11
#define         REG_RXBW                                0x19
#define         REG_RSSITHRESH                          0x29
#define         REG_SYNCCONFIG                          0x2E
#define         REG_SYNCVALUE1                          0x2F
#define         REG_SYNCVALUE2                          0x30
#define         REG_PACKETCONFIG1                       0x37
#define         REG_AUTOMODES                           0x3B
#define         REG_PACKETCONFIG2                       0x3D
#define         RF_BITRATEMSB_300000                    0x00    // Begin 300 kbps auto Tx settings
#define         RF_BITRATELSB_300000                    0x6B
#define         RF_FDEVMSB_300000                       0x13
#define         RF_FDEVLSB_300000                       0x33
#define         RF_RXBW_DCCFREQ_111                     0xE0
#define         RF_RXBW_MANT_16                         0x00
#define         RF_RXBW_EXP_0                           0x00
#define         RF_SYNC_ON                              0x80
#define         RF_SYNC_FIFOFILL_AUTO                   0x00
#define         RF_SYNC_SIZE_2                          0x08
#define         RF_SYNC_TOL_0                           0x00
#define         RF_PACKET1_FORMAT_VARIABLE              0x80
#define         RF_PACKET1_DCFREE_OFF                   0x00
#define         RF_PACKET1_CRC_OFF                      0x00
#define         RF_PACKET1_CRCAUTOCLEAR_OFF             0x08
#define         RF_PACKET1_ADRSFILTERING_OFF            0x00
#define         RF_PACKET2_RXRESTARTDELAY_2BITS         0x10
#define         RF_PACKET2_AUTORXRESTART_ON             0x02
#define         RF_PACKET2_AES_OFF                      0x00    // End 300 kbps auto Tx settings

static inline void SPI_INIT(void) {  // Level 0 code - initialize the SPI bus at fosc/2 (8MHz)
  PORTB |= 1<<SS_PIN;
  DDRB |= _BV(SS_PIN);
  SPCR |= _BV(MSTR) | _BV(SPE);
  SPSR |= (1<<SPI2X);    // Set the SPI bus speed to Fosc/2 = 8MHz at full speed
 
  // No clue why this is neccessary as opposed to DDRB |= 1<<SCK | 1<<MOSI
  volatile uint8_t *reg;
  reg = &DDRB;
  *reg |= 0x28;  // Bit mask of SCK and MOSI
}

OPT_FLAG uint8_t SPI_XFER(uint8_t data) {    // Level 0 code - move data over the SPI bus
  SPDR = data;
  WAIT_WHILE_SPI_BUSY;
  return SPDR;
}

OPT_FLAG uint8_t readReg(uint8_t addr) {    // Level 1 code - interact witht the radio's registers
  SELECT;
  SPDR = ( addr & 0x7F );
  WAIT_WHILE_SPI_BUSY;
  SPDR = ( 0 );
  WAIT_WHILE_SPI_BUSY;
  UNSELECT;
  return SPDR;
}

OPT_FLAG void writeReg(uint8_t addr, uint8_t value) {  // Level 1 code - interact witht the radio's registers
  SELECT;
  SPDR = ( addr | 0x80 );
  WAIT_WHILE_SPI_BUSY;
  SPDR = ( value );
  WAIT_WHILE_SPI_BUSY;
  UNSELECT;
}

static inline void SendFrame(uint8_t toAddress, const void* buffer, uint8_t bufferSize) {  // Level 2 code - do useful work
  SELECT;
  SPI_XFER(REG_FIFO | 0x80);   // write to FIFO using SPI burst mode
  SPI_XFER(bufferSize + 3);    // LEN byte
  SPI_XFER(toAddress);         // 1st byte
  SPI_XFER(NODEID);            // 2nd byte
  SPI_XFER(0x00);              // 3rd byte
  for (uint8_t i = 0; i < bufferSize; i++) SPI_XFER(((uint8_t*) buffer)[i]);  // Write 6 more bytes to the FIFO
  UNSELECT;
}

static inline void RadioInit(void) {
    SPI_INIT();
    CHANGE_OP_MODE(SLEEP_MODE);  // Put the radio to sleep ASAP to save power
    writeReg( REG_BITRATEMSB, RF_BITRATEMSB_300000 ); // 0x03
    writeReg( REG_BITRATELSB, RF_BITRATELSB_300000 ); // 0x04
    writeReg( REG_FDEVMSB, RF_FDEVMSB_300000 ); // 0x05
    writeReg( REG_FDEVLSB, RF_FDEVLSB_300000 ); // 0x06
    writeReg( REG_RXBW, RF_RXBW_DCCFREQ_111 | RF_RXBW_MANT_16 | RF_RXBW_EXP_0 ); // 0x19
    writeReg( REG_RSSITHRESH, 220 ); // 0x29
    writeReg( REG_SYNCCONFIG, RF_SYNC_ON | RF_SYNC_FIFOFILL_AUTO | RF_SYNC_SIZE_2 | RF_SYNC_TOL_0 ); // 0x2E - 2 sync bytes
    writeReg( REG_SYNCVALUE1, 0xAA ); // 0x2F
    writeReg( REG_SYNCVALUE2, NETWORKID ); // 0x30
    writeReg( REG_PACKETCONFIG1, RF_PACKET1_FORMAT_VARIABLE | RF_PACKET1_DCFREE_OFF | RF_PACKET1_CRC_OFF | RF_PACKET1_CRCAUTOCLEAR_OFF | RF_PACKET1_ADRSFILTERING_OFF );  // 0x37 // 0x37
    writeReg( REG_AUTOMODES, AUTO_TRANSMITTER );  // 0x3B - Put the radio in automatic mode
    writeReg( REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_2BITS | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF ); // 0x3D
    SET_POWER_LEVEL(0);
}

I now plan to look into adding back ACKs (done added 4 bytes) and generalizing back to HWs as well.  Once that is done I'll need to experiment with auto receivers.  Other pending issues are CRC-4 or some sort of encryption.
« Last Edit: January 20, 2017, 10:03:42 PM by ChemE »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #21 on: January 26, 2017, 11:04:39 AM »
I managed to track down a subtle bug that was preventing much of anything from actually functioning when DEBUG was set to 0 (no serial output).  The culprit was a lack of a Timer 0 overflow vector.  This fix was this simple once I realized what was going on:

Code: [Select]
#if !DEBUG
  ISR(TIMER0_OVF_vect){}  // Needed to make the WDT work
#endif

The library can now be switched between Auto or Manual mode since it appears that there is a LONG delay in auto mode of putting the radio to sleep.  It might just be a bad setting on my part but I haven't tracked down a fix for that yet.  We know we can get to sleep fast in manual mode.  The code size is 970bytes in auto mode and 1066 bytes in manual mode.  I do still plan to add CRC-4 though I wonder if a parity bit would be sufficient for such small payloads.

zingbat

  • NewMember
  • *
  • Posts: 15
Re: Speeding Up The RFM69 Library
« Reply #22 on: January 31, 2018, 04:37:28 AM »
@ChemE ,

It's awesome to have such a small and dependency free implementation. Did you end up writing optimized receive code as well? I'm tinkering with an Optiboot variant that does OTA programming and thus am looking around for slim RFM69 implementations.

Jeff

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Speeding Up The RFM69 Library
« Reply #23 on: January 31, 2018, 04:29:06 PM »
I'm tinkering with an Optiboot variant that does OTA programming

@zingbat - which one might that be?

zingbat

  • NewMember
  • *
  • Posts: 15
Re: Speeding Up The RFM69 Library
« Reply #24 on: February 03, 2018, 11:56:09 AM »
I'm working on it - no established project.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #25 on: February 03, 2018, 01:31:25 PM »
@ChemE ,

It's awesome to have such a small and dependency free implementation. Did you end up writing optimized receive code as well? I'm tinkering with an Optiboot variant that does OTA programming and thus am looking around for slim RFM69 implementations.

Jeff

Jeff,

I did start fooling with the receive code but got bogged down in the registers not seeming to work properly.  I wanted an implementation that I could simply periodically poll the registers to see if a packet had been received but I never got that to work.  It looks like I'll need to set it up using interrupts just as Felix did.  I have been meaning to pick this project back up since I've put it down for so very long.  I'm glad at least the send code is useful for you though.  One day I'll have a reliable datagram working, dependancy-free, and shoehorned into a few kilobytes of code space!
« Last Edit: February 03, 2018, 01:44:23 PM by ChemE »

zingbat

  • NewMember
  • *
  • Posts: 15
Re: Speeding Up The RFM69 Library
« Reply #26 on: February 03, 2018, 02:08:15 PM »
That would be awesome if you could send it! I think I'm running in to the same problem: I'm polling the FIFO and just getting 999999 back.

Also, I haven't looked closely at this yet but I believe the plainRFM69 library is using auto-receive mode, not sure if he's polling or using interrupts:

https://github.com/iwanders/plainRFM69

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Speeding Up The RFM69 Library
« Reply #27 on: February 03, 2018, 06:55:40 PM »
I'm working on it - no established project.
You mean you are developing it yourself?

zingbat

  • NewMember
  • *
  • Posts: 15
Re: Speeding Up The RFM69 Library
« Reply #28 on: February 03, 2018, 08:44:03 PM »
Yep! Slowly, since it has to fit in under 4k..

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Speeding Up The RFM69 Library
« Reply #29 on: February 03, 2018, 11:04:34 PM »
Probably should take this to a new thread, but I wonder if there should be an open-source Dual Optiboot 2.0 that does OTA.  Joe has done it but it is closed source but he seems to help with hints.  Felix could help too though I know he won't go crazy on it since he has other commitments.  But still, there are enough of us interested/able that I bet we could get it done and make a big leap forward in capability.