Author Topic: How to speed up RFM69 data throughput on Moteino M0? [solved]  (Read 8652 times)

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
I am trying to stream data real time from a sensor on a rotating platform to a stationary receiving station using a Moteino M0 on each end, each with a RFM69HCW 915 Mhz module. 

It takes 13 ms for each sendWithRetry() to complete.  The payload for each packet is a 20 byte struct.  Ideally I would like to get 1000 packets through per second.  So I would like to reduce send time to more like 1 ms.  Is this possible?

Indeed, I tried using send() instead.  That only takes 7 ms which is a good improvement.  Is there any way to improve it further?  I'd prefer to use ACKs so speeding up sendWithRetry would be welcomed. 

Power draw does not matter.  The radios will only be a couple of meters apart.

Thanks!  -JD

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #1 on: March 23, 2020, 10:14:30 AM »
I would start by disabling encryption and maxing out the bitrate, there's some sample code in the OTA examples here:
https://github.com/LowPowerLab/RFM69/tree/master/Examples/WirelessProgramming_OTA

Also you could try bumping up the SPI clock (up to 10mhz is max RFM69/SX1231 supports per DS), but the gains there would likely be marginal.

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #2 on: March 23, 2020, 01:52:59 PM »
Nice, Felix!  From your Target.ino, I did this to both the remote and local sides:

Added this to the global section:
Code: [Select]
#define BR_300KBPS             //run radio at max rate of 300kbps!
Added this to Setup():
Code: [Select]
  #ifdef BR_300KBPS
  radio.writeReg(0x03, 0x00);  //REG_BITRATEMSB: 300kbps (0x006B, see DS p20)
  radio.writeReg(0x04, 0x6B);  //REG_BITRATELSB: 300kbps (0x006B, see DS p20)
  radio.writeReg(0x19, 0x40);  //REG_RXBW: 500kHz
  radio.writeReg(0x1A, 0x80);  //REG_AFCBW: 500kHz
  radio.writeReg(0x05, 0x13);  //REG_FDEVMSB: 300khz (0x1333)
  radio.writeReg(0x06, 0x33);  //REG_FDEVLSB: 300khz (0x1333)
  radio.writeReg(0x29, 240);   //set REG_RSSITHRESH to -120dBm
  #endif
On the remote (sending) side, in loop(), I had already changed:
Code: [Select]
if (radio.sendWithRetry(GATEWAYID, (const void*)(&RFDataPktToSend), sizeof(RFDataPktToSend))) ...
to:
Code: [Select]
      radio.send(GATEWAYID, (const void*)(&RFDataPktToSend), sizeof(RFDataPktToSend));
These measures reduced the time to complete sending from 13 ms (with acks) to 7 ms (without acks) to 3 or 4 ms with bit rate changes.  Good!

Then on both sides, I commented out encryption like this:
Code: [Select]
  //radio.encrypt(ENCRYPTKEY);
That further reduced send time (averaged over 10 sends) to 1.8 to 2.4 ms.  Better! 

On the local side I now see about 4 ms between updates.  Some of that is me.  On the remote side SPI is being used not just to talk to the radio but to collect data from the accelerometer, the latter of which is taking about 2 ms.  So I am hopeful that will make a difference too.  I'll report back once I speed up SPI.

This was easier than I thought it would be.  The future is looking bright!  Thanks!  -JD

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #3 on: March 23, 2020, 02:55:52 PM »
It is also pretty easy to speed up the SPI functions quite a lot.  Felix's library caches and then restores SPI settings before/after every SPI write which takes extra time.  If you only have the radio using the SPI bus, there is no need to thrash settings.  See my work here: https://lowpowerlab.com/forum/low-power-techniques/speeding-up-the-rfm69-library/

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #4 on: March 23, 2020, 03:01:45 PM »
Very good point ChemE, I remember fiddling with that a while ago and forgot this detail.
Definitely removing the arduino SPI session stuff should help in a significant way.
The current SPI clock is at 4Mhz I think. The limiting factor for the clock itself is the RFM69/SX1231h chip 10mhz).

Looking forward to see how fast the M0 can make it go  :)

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #5 on: March 23, 2020, 04:56:20 PM »
Hmmm... I also have an accelerometer on the SPI bus.  Would I do better to move it to I2C then remove the caching from SPI?   The LIS3DH accelerometer board can do either SPI or I2C.

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #6 on: March 23, 2020, 06:09:03 PM »
Depends how often you need updates from the accel. I2c is much slower, even at 1mhz.
I would use another SPI perhaps but you're still tying up the MCU to do the reads so I don't think in your case that is of advantage.

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #7 on: March 23, 2020, 08:19:19 PM »
When going fast I need the updates from the accel at the same rate I need to transmit them over the RF link.  But they won't be asked to happen simultaneously.  I read the accel then send the data out the radio.  When the send is done, I repeat.  Does this mean I could get away with removing the SPI caching for the radio sends while continuing to use SPI for the accel?

I will just try upping the SPI clock rate first.  Thanks!  -JD

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #8 on: March 23, 2020, 09:11:52 PM »
I2C is slow as Christmas no matter what you do (believe me, I tried hard).  SPI comparatively is blazing fast.  If you are motivated by refresh rate, you should endeavor to keep everything 10MHz SPI and pare that code aggressively.  If you want to go find a clock cycle to take back, find it on the SPI bus and you'll get it many thousands of times just through repetition.

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #9 on: March 23, 2020, 11:15:33 PM »
Okay, I give up.  How do I change the SPI clock speed?

In RFM69.cpp I tried this:
Code: [Select]
#ifdef SPI_HAS_TRANSACTION
  //_settings = SPISettings(4000000, MSBFIRST, SPI_MODE0);
  _settings = SPISettings(10000000, MSBFIRST, SPI_MODE0); // JD - attempting to speed up spi clock
#endif
and saw no difference in the radio send time.  Reading it with a scope now, it was 1.93 ms before and after the above change.  Do I just change it, save the file, recompile my code in the IDE then upload it to the Moteino M0?  Or is there some intermediate step to recompile the library or something?

Thanks.  -JD

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #10 on: March 24, 2020, 06:27:56 PM »
When I scope SPI SCK, I see a 1 Mhz square wave regardless of what I put in SPISettings() in RFM69.cpp.  (Should it be this low?  I was expecting 4 MHz.)  So I tried changing SPI.setClockDivider(SPI_CLOCK_DIV16) to SPI.setClockDivider(SPI_CLOCK_DIV2).  Bingo, the clock signal frequency is now 8.02 MHz.  But, since Felix suggested I make it 10 MHz, how do I do that?

This reduced my accelerometer reads from about 165 Ás to 82 Ás, and my radio sends from 1.93 ms to 1.50 ms.  Now my throughput is 318 Hz.  Even better.

Though it is time for me to work on reducing my time spent processing data and sending it out USB, I would still like to see if I can get the send time down further.  But I have no idea what to do to get rid of the SPI caching (thrashing?) that you mentioned, ChemE.  In the thread you pointed me to, it looks like maybe you rewrote the entire library?  The code in these libraries is a bit over my head.  Is there a relatively simple change I could make to remove the SPI settings "thrashing"?

This is exciting stuff!  I am very pleased to have made this much progress so quickly and simply.  -JD

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #11 on: March 24, 2020, 09:42:57 PM »
To get 10mhz you will need to change the way that the SAMD core splits the main clock to assign a clock to the SPI peripheral, mucky work, I would not bother unless you think that extra 2mhz will really make an important difference, IMO 8mhz is plenty.

I will add to the discussion that if you're looking to stream data as fast as possible, the transitions between radio modes are time expensive. If you can stay in TX mode that will save significant time. But this gets you into gutting away lots of code from the library and putting it in custom functions that just do the streaming. Sorry I don't have an example to publish and this would be highly experimental but doable with significant effort and lots of radio packets timings analysis (CurrentRanger + scope very useful in such scenario!).

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #12 on: March 26, 2020, 11:16:01 AM »
I agree, Felix, 8 MHz is plenty and another 2 Mhz would make little further difference.  I am curious though, why you originally expected I would find the clock running at 4 Mhz.

Indeed, I only need the remote system to transmit, never receive.  But I am in no position to try to muck around for that.  I would think I am not the only person who would find a transmit-only library useful though.  Remote streaming sensors can be quite handy in a number of data acquisition situations.

Though this is probably as solved as you can help me make it for now, I will wait a little before I declare this completely solved as I want to see how I can further speed things up by streamlining the rest of my code.  I will post my results here.

For example, cutting out some processing on the local receiving side cut the time spent in the loop in half.  However, that had no effect on throughput because the remote transmitting side appears to what is pacing the transfers.


Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #13 on: March 26, 2020, 01:31:16 PM »
I am curious though, why you originally expected I would find the clock running at 4 Mhz.
Just an overlook, the AVR SPI clock was set to 4mhz, I replied without looking at the code.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Best way to speed up RFM69HCW data throughput on Moteino M0?
« Reply #14 on: March 26, 2020, 04:39:40 PM »
...I would still like to see if I can get the send time down further.  But I have no idea what to do to get rid of the SPI caching (thrashing?) that you mentioned, ChemE.  In the thread you pointed me to, it looks like maybe you rewrote the entire library?  The code in these libraries is a bit over my head.  Is there a relatively simple change I could make to remove the SPI settings "thrashing"?...

Yes, it took me a while but I had fun (mostly).  That is exactly what I did; I rolled my own by distilling others' work down rather than building up from the datasheets.  I don't have any SAMD21 uCs so I'm at a loss for how portable the code I wrote is.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #15 on: March 27, 2020, 06:37:32 AM »
I'm going back through the code and reminding myself where you'll need to rewrite to avoid the SPI session thrashing.  The changes you need to make are in RFM69.cpp around line 437 in RFM69::select and RFM69::unselect

Here is Felix's original code:
Code: [Select]
// select the RFM69 transceiver (save SPI settings, set CS low)
void RFM69::select() {
  noInterrupts();
#if defined (SPCR) && defined (SPSR)
  // save current SPI settings
  _SPCR = SPCR;
  _SPSR = SPSR;
#endif
  // set RFM69 SPI settings
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV4); // decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present
  digitalWrite(_slaveSelectPin, LOW);
}

// unselect the RFM69 transceiver (set CS high, restore SPI settings)
void RFM69::unselect() {
  digitalWrite(_slaveSelectPin, HIGH);
  // restore SPI settings to what they were before talking to RFM69
#if defined (SPCR) && defined (SPSR)
  SPCR = _SPCR;
  SPSR = _SPSR;
#endif
  maybeInterrupts();
}

It is easy enough to just comment out the code that caches and restores the SPI bus settings like this:
Code: [Select]
void RFM69::select() {
  noInterrupts();
//#if defined (SPCR) && defined (SPSR)
  // save current SPI settings
  //_SPCR = SPCR;
  //_SPSR = SPSR;
//#endif
  // set RFM69 SPI settings
  //SPI.setDataMode(SPI_MODE0);
  //SPI.setBitOrder(MSBFIRST);
  //SPI.setClockDivider(SPI_CLOCK_DIV4); // decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present
  digitalWrite(_slaveSelectPin, LOW);
}

// unselect the RFM69 transceiver (set CS high, restore SPI settings)
void RFM69::unselect() {
  digitalWrite(_slaveSelectPin, HIGH);
  // restore SPI settings to what they were before talking to RFM69
//#if defined (SPCR) && defined (SPSR)
  //SPCR = _SPCR;
  //SPSR = _SPSR;
#endif
  maybeInterrupts();
}

Also it is worth noting that Arduino's digitalWrite() function is an absolute dog and manages to waste like 113 clock cycles doing what can be done in 1 clock cycle.  If you want to save even more time, avoid digitalWrite everwhere in your code and just bang bits instead.
Code: [Select]
#define 	SS_PIN			PB2
#define    SS_WRITE_LOW      PORTB &= ~(1<<SS_PIN)
#define    SS_WRITE_HIGH      PORTB |= 1<<SS_PIN
Much more on bit banging here: https://lowpowerlab.com/forum/general-topics/super-sweet-bit-banging-macros/
I'll be curious to see how this changes your refresh rate!
« Last Edit: March 27, 2020, 06:44:12 AM by ChemE »

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #16 on: March 27, 2020, 06:04:39 PM »
Wow, thanks, ChemE.  I will try that.  And I will keep the digitalWrites in mind.  Presently, I am only using them for studying timing with the scope.

Meanwhile, I went hunting through my transmit side code for opportunities.  First, I found that when I commented out some string manipulations for Serial Monitor debug printing, my throughput got worse!!!  A little scoping of both sides at once revealed that I was attempting two sends for each receive cycle.  I found that the next send needed to start at least 550 us after the present receiveDone() became true.  I don't exactly know why this is but I can live with that constraint. 

So I played around with some delay on the send side and found that I could get the throughput up from 318 to about 420 Hz which is another good step in the right direction for me.

Thanks, again, for your help.  -JD

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #17 on: March 27, 2020, 07:20:55 PM »
Okay.  So I tried ChemE's suggestions for modifying the SPI select code.  Without trying to change the digitalWrites, I saw a reduction in send time from about 1.5 ms, to 1.4 ms.  This is good!

I further tried to replace the digitalWrites.  Since PB2 is not defined I tried using _slaveSelectPin instead.  Then the compiler complained with:

Code: [Select]
error: no match for 'operator&=' (operand types are '_EPortType' and 'int')
Also, I don't see PORTB defined anywhere.  Perhaps that is the problem?

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #18 on: March 28, 2020, 10:49:55 AM »
Looks like the slave select pin on the MoteinoM0 is PB09.  Part of the problem is my code is written for and specific to an Atmel 328p because that is all I use and I don't care about portability from one uC to another.

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #19 on: March 28, 2020, 09:09:38 PM »
ChemE's defines are very cool but are for AVR, will not work on SAMD  :'(

For SAMD21 and SAMD51 you can use direct register manipulation to flip bits with the defines shown below.
Notice how you can just toggle a bit without remembering its state like it's typically done on AVRs ;-)

For instance this sample sketch below toggles the MoteinoM0 LED_BUILTIN every second (arduino pin 13, aka pin PA17).
Let us know how fast this makes your code now!  :o

Code: [Select]
#define LED_PIN PIN_PA17

#define PINOP(pin, OP) (PORT->Group[(pin) / 32].OP.reg = (1 << ((pin) % 32)))
#define PIN_OFF() PINOP(LED_PIN, OUTCLR)
#define PIN_ON() PINOP(LED_PIN, OUTSET)
#define PIN_TGL() PINOP(LED_PIN, OUTTGL)

void setup() {
  PINOP(LED_PIN, DIRSET);
}

void loop() {
  PIN_TGL();
  delay(1000);
}

I scoped out the code below and I get these results:
- SAMD21 - ~100ns pulse with the toggles and a  ~1530ns pulse with digitalWrite
- SAMD51 - ~20ns pulse with the toggles and a  ~400ns pulse with digitalWrite

Code: [Select]
void loop() {
  PIN_TGL();
  PIN_TGL();
  delayMicroseconds(1);
  digitalWrite(13,HIGH);
  digitalWrite(13,LOW);
  delay(2);


« Last Edit: March 28, 2020, 09:47:24 PM by Felix »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #20 on: March 28, 2020, 09:42:38 PM »
Awesome, glad you had the solution for the SAM21D Felix!  I'm also curious to see how fast this gets the refresh rate.  Are you at all close to your 1000Hz target yet?

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #21 on: March 29, 2020, 06:08:29 PM »
Hi, Felix.  After some fussing I figured out what I needed to do to incorporate your suggested macros into the RFM69 library.  And it works! 

The radio send time is now further reduced from 1.40 ms to 1.34 ms.  With that came an increase in throughput from about 420 to to 434 Hz.  Even better!  However, I noticed that after about 11 seconds of running, throughput started faltering with dropped receives every now and then.  I had to increase the delay in my sketch from 250 to 270 Ás to stop this so my throughput is now 427 Hz.  Still, this is better, a little bit anyway. 

Here is the code with the SPI save and restore steps commented out and the digital writes replaced with the PIN_OFF and PIN_ON macros.

Code: [Select]
// Modification in Felix's code to remove SPI "thrashing" and speed up digitalWrites - JD
#define SS_PIN            PIN_PB09
#define SS_PINOP(pin, OP) (PORT->Group[(pin) / 32].OP.reg = (1 << ((pin) % 32)))
#define SS_PIN_OFF()      SS_PINOP(SS_PIN, OUTCLR)
#define SS_PIN_ON()       SS_PINOP(SS_PIN, OUTSET)
//#define SS_PIN_TGL() SS_PINOP(SS_PIN, OUTTGL)

// select the RFM69 transceiver (save SPI settings, set CS low)
void RFM69::select() {
//#if defined (SPCR) && defined (SPSR)
  // save current SPI settings
  //_SPCR = SPCR;
  //_SPSR = SPSR;
//#endif

#ifdef SPI_HAS_TRANSACTION
  SPI.beginTransaction(_settings);
#endif

  // set RFM69 SPI settings
  //SPI.setDataMode(SPI_MODE0);
  //SPI.setBitOrder(MSBFIRST);
#ifdef __arm__
//SPI.setClockDivider(SPI_CLOCK_DIV16);
SPI.setClockDivider(SPI_CLOCK_DIV2); // JD - trying to speed up SPI clock
#else
  SPI.setClockDivider(SPI_CLOCK_DIV4); // decided to slow down from DIV2 after SPI stalling in some instances, especially visible on mega1284p when RFM69 and FLASH chip both present
#endif
  //digitalWrite(_slaveSelectPin, LOW);
  SS_PIN_OFF();
}

// unselect the RFM69 transceiver (set CS high, restore SPI settings)
void RFM69::unselect() {
  //digitalWrite(_slaveSelectPin, HIGH);
  SS_PIN_ON();
#ifdef SPI_HAS_TRANSACTION
  SPI.endTransaction();
#endif 
  // restore SPI settings to what they were before talking to RFM69
//#if defined (SPCR) && defined (SPSR)
  //SPCR = _SPCR;
  //SPSR = _SPSR;
//#endif
}
// End of code modified to avoid SPI "thrashing" - JD
Have I done this correctly?  Anything else I should be doing?

ChemE, I originally said I wanted to increase throughput to 1 kHz.  I was dreaming.  I was forgetting that in reality, 400 Hz is good enough for now.  It allows me to still send 100 scans per revolution of my rotating system at 240 RPM.  At speeds below 120 rpm, I can send 200 scans per revolution which is my preferred rate at lower speeds for best resolution of the plots and animations I do with this data.

Unless you all have any further suggestions, I will mark this solved.
« Last Edit: March 29, 2020, 06:10:17 PM by jdheinzmann »

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #22 on: March 29, 2020, 07:03:15 PM »
Unless you all have any further suggestions,
Oh!
Tell us what you're doing transmitting at 400hz with a MoteinoM0!  ;D

And how large in bytes are your packets on average?

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #23 on: March 29, 2020, 08:59:33 PM »
Unless you all have any further suggestions, I will mark this solved.

I still see some session stuff hanging out but you've probably gotten most of the fat trimmed away at this point.  If your life depended on it, you could probably still up the rate 50% by cutting out all those function calls and returns, ditching the last of the SPI session stuff, etc.  But, if it is already fast enough for your use case, then I'd say call it solved.  I'm unusual in that part of my joy of coding is finding that last 1% which most everyone else doesn't care about.

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #24 on: March 30, 2020, 05:35:36 PM »
Felix, what I am doing is making an inertial based wireless shaft angle encoder for use on large stationary steam engines in an antique machinery museum.  The LDIS3DH accelerometer gives me angle from the X and Y directions without needing a mechanical connection to the stationary frame.  And the radio gives me a data connection to the stationary frame without twisting up any wires.  This sensor can be mounted to the end of the shaft of any one of these large machines using magnetic feet with no mod to the shaft.  That signal along with steam pressure signals from sensors on either end of the cylinder yield real-time PV "indicator" diagrams which is the modern way of measuring indicated horsepower (vs. brake horsepower with a dynamometer).  The area enclosed by the PV cycle is the work done on the piston during that cycle.  The shaft angle (along with known geometry of the engine) is also used in displaying a real time animation of the piston and valve gear.  Steam pressure is used to change the color of the steam inside the engine (lighter for positive pressure, darker for negative).  It works quite nicely already with a wired analog angle sensor on a particular engine and an Arduino with Serial connection to Processing on a PC.  The wireless sensor will make the hook-up so much easier for other engines.  Visitors to the museum really like being able to get a better picture of what is going on inside these engines.



Oh, and my packets are 20 bytes long.  I will be shortening them as there is presently debug info in there that will not be needed much longer. 

ChemE, as far as eliminating SPI thrashing goes, though the throughput was higher, the data integrity suffered.  The first element of the struct passed over gets corrupted (in a rather repeatable way) every now and then.  So I removed that but kept Felix's PIN ON/OFF macros.  Perhaps I hadn't implemented the changes properly.  The code in the present library doesn't exactly match the sample you provided.  Should I have commented these out too?
Code: [Select]
#ifdef SPI_HAS_TRANSACTION
  SPI.beginTransaction(_settings);
#endif
My throughput is still a little above 400 Hz so I remain happy.

(How do I get the attached image to display inline?  I have
Code: [Select]
[img]https://IMG_20190927_154904.jpg[/img]
between the above two paragraphs.)
« Last Edit: March 30, 2020, 05:50:09 PM by jdheinzmann »

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #25 on: March 30, 2020, 08:57:10 PM »
JD,
Thanks for sharing the background,
Very fascinating, so great that the Moteino M0 can do the job.

When you add an attachment, unfortunately it will go at the bottom. If you want to insert an image in the middle it will have to be from an online location.
OR if you first post the post+attachment then copy the link of the posted image and edit your post with an additional IMG tag with the link in it:

https://lowpowerlab.com/forum/moteino-m0/best-way-to-speed-up-rfm69hcw-data-throughput-on-moteino-m0/?action=dlattach;attach=3276

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #26 on: April 03, 2020, 05:16:43 PM »
As I try to streamline radio communications, I've added conditional compiles to leave out code needed or not needed for development vs. fast running.

I tried removing the nodeId element from the Payload struct and got rather strange behavior.

Code: [Select]
// constants for specifying mode in which program is to run
#define CAL            (0) // run sketch in calibration mode
#define DEV            (1) // debug data included in packets over radio
#define RUN            (2) // only essential data in packets over radio

#define RUN_MODE       (RUN)  // CAL, DEV or RUN

// Set up structure for radio packet payload. 
// Both sides must have exact same structure to send from and receive into.
#if (RUN_MODE == RUN)
  typedef struct
  {
    float              theta; // [rad], shaft angle
    float               batV; // [V], wireless sensor battery voltage
    boolean              TDC; // true when TDC has been reached, otherwise false
  } Payload;
#else // must be CAL or DEV
  typedef struct
  {
    int               nodeId; // store this nodeId
    unsigned long remTime_ms; // [ms], elapsed time
    float              theta; // [rad], shaft angle
    float                  X; // [g], acceleration in X direction
    float                  Y; // [g], acceleration in Y direction
    float               batV; // [V], wireless sensor battery voltage
    boolean              TDC; // true when TDC has been reached, otherwise false
  } Payload;

Payload RFDataPktToSend;
#endif
When I try to run this, the code hangs up while reading the accelerometer (over SPI) with no error message.  In loop(), it just never makes it past the first read of the accelerometer (as evidenced by a Serial.print on either side of the read.  Only the first one prints then nothing.)
Code: [Select]
  Serial.println("About to read IMU..."); // debug
  X_Raw = myIMU.readFloatAccelX();
  Serial.println("Read X from IMU..."); // debug
If I change to RUN_MODE (DEV), it runs fine.

FYI, this is the send (though it never gets there):
Code: [Select]
      radio.send(GATEWAYID, (const void*)(&RFDataPktToSend), sizeof(RFDataPktToSend));    

1)  I just want to confirm that NodeId does not need to be in the struct for the radio to function, right?

2)  From experimentation, it seems that the Payload struct must start with an int or a long.  Otherwise the sketch will not run.  Any reason that you know of for this?  I mean, an int or a float are both just 4 bytes of binary data.  What does it care?  Hmm...

Any ideas?

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #27 on: April 03, 2020, 09:37:15 PM »
On the brighter side, with my trimming down and streamlining I am now seeing 500 Hz throughput.  Woo hoo!

I got here by removing all Serial.print()s on the transmit side.  Much of the time there is spent in assembling the strings to be printed.  Using sprintf to assemble the strings added 1110 Ás while stringing out a bunch of Serial.print()s (11 of them) adds 910 Ás instead.  On the receive side, I can't completely get rid of the Serial.print()s because I need to send the data gathered from both the remote and local sensors up to the PC.  But I did switch to binary instead of CSV data.  When I activate serial monitor though, the throughput rate slows down quite a bit and the updates jitter a lot.  Hopefully I will be able to do better when the data is going to Processing instead.

One problem I still see though is that the radio.send()s, which typically take 1.33 ms, occasionally take upwards of 9 ms.  By occasionally, I mean once about every 2.5 s, and there is usually a burst of them typically lasting 38 to 72 ms.  What could be causing this?  I am measuring this timing on a scope using digitalWrites immediately before and after each radio.send().    Remember, these sends are without ACKs, and without encryption.  The receive side time spent servicing each received data packet is taking a stable 520 Ás, so it does not appear to be anything on the receive side that is causing this.  Could this be radio interference?  Would the send side feel this?  Without ACKs/retries?  What could be stretching out these sends?

Edit:  I forgot to mention that the above change to the Payload struct reduced the size from 28 to 16 bytes.  (I should also note that in reply #24, I had mistakenly reported the Payload as being 20 bytes.  It was 28.)
« Last Edit: April 03, 2020, 10:40:29 PM by jdheinzmann »

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #28 on: April 04, 2020, 08:06:05 AM »
I would hope the only things your transmit Mote is doing are:
1) getting numeric data from your accelerometer
2) assembling the numeric data into a radio packet
3) broadcasting the packet

If it is doing any string manipulation at all, you are slowing it down.

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #29 on: April 04, 2020, 10:05:26 AM »
Hi, ChemE.  Yes, that is all it is doing when in RUN mode.  When in DEV mode it assembles debug strings and writes them to USB, and yes, it runs slower then.  -JD

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #30 on: April 04, 2020, 05:50:29 PM »
Post the run mode code and I bet you I can find some more Hertz!  What else have I got to do right now?!

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6617
  • Country: us
    • LowPowerLab
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #31 on: April 04, 2020, 07:49:16 PM »
Since your packets are relatively small, how about the radio's continuous mode  ;)
The pipe is open all the time, no more packet overheads, you just probably need to implement a light protocol to ensure some data CRC.
Since corona has put the entire planet on vacation indefinitely, there's plenty of time for experiments.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #32 on: April 05, 2020, 08:39:03 AM »
That's a good thought Felix; I've never fooled with continuous mode.  A few bits of error check would be fast and easy.

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #33 on: April 06, 2020, 12:08:19 PM »
Hi, ChemE.  Not sure how you want me to post the code so, rather than take up a bunch of space inline, I am attaching the files.

I continue to be puzzled by why I see radio.sends stretch out in bursts of 3 or 4 for upwards of 10 ms each every 2.5 s.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #34 on: April 06, 2020, 03:58:15 PM »
Right away I see that there are digitalWrites and analaogWrites in both routines which really are slow.  You can *easily* ditch them in favor of faster variants to pick up speed basically for nothing.  Obviously don't worry about anything in setup just focus on loop.  I'm also betting the floating point math happening in the remote Mote is taking some decent clock.  Why not just read the data off the accelerometer as hex, stuff it in the radio packet as hex, and let the local node handle the floating point math?

jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0?
« Reply #35 on: April 06, 2020, 06:33:06 PM »
Hi, ChemE.  Yes, all good points.  Though the macros for toggling digital pins worked in Felix's library, I could not figure out how to do it from the sketch so I abandoned them there.  (I could not figure out what to #include to have OUTCLR and OUTSET defined).  Unless I can get them working from the sketch, I will wrap the digitalWrites in #if DEBUG_MODE #endifs so that I can easily turn them back on for debug and timing studies.  I hadn't thought about the write to the DAC.  Will do the same there.

I calculate angle from the X and Y accelerometers remotely because I only want to send data when it is important to send it.  Like after the shaft has gone 2░ or reached top dead center (TDC).  If I do it on the local side, that information will be coming across more slowly and the decisions to use the data will be belated.  Doing it on the remote side I can do it nearly instantly.  This is especially important for me in precisely indicating TDC.

I should mention that there are changes I made in RFM69.cpp that aren't included in the files I posted.  The only one that has survived is the SPI clock speed up.  I eventually found that things weren't as stable with the changes to stop SPI thrashing so I took them out.  Sorry to say, I even took out the pin toggle speed ups (I can't remember exactly why).  I just put them back in and the radio.sends dropped from 1.5 ms to 1.44 ms.  Throughput increased from 409 to 419 Hz, so there are 10 Hz recovered. 

I've attached RFM69.cpp as I am presently using it.  Perhaps you can suggest (or even make) specific improvements there for me to try.  You can see my attempts to modify the SPI stuff in a block that is commented out.

If I understood all this better I would pursue these things like you do, but at >400 Hz now, I am less motivated.  I'm still willing to try things though but it has to be well spelled out.




jdheinzmann

  • NewMember
  • *
  • Posts: 35
  • Country: us
Re: How to speed up RFM69 data throughput on Moteino M0? [~Solved]
« Reply #36 on: April 12, 2020, 09:36:53 PM »
I declare this problem solved.  Throughput wise, it is working well enough for me to move on. 

Reliability wise, I am still puzzled by the bursts of long radio.send() durations I see every 2.5 s.  If that becomes a big enough problem, I will take it up as a separate conversation.

Thanks, Felix and ChemE for all your help! 

-JD
« Last Edit: April 12, 2020, 09:40:42 PM by jdheinzmann »