DS18B20 - temperature on request, battery lifetime?

Started by Lukapple, July 26, 2017, 03:27:23 AM

ChemE

I just added my own code to sleep the radio and measured the sleep current at 7.4uA with my uCurrent from EEVblog.  This is running from 2xAA Energizer Ultimate Lithium batteries (L91 hereafter) and using the stock LDO.  The battery set is currently putting out 3.36V so the LDO is still playing nice and not going past its rated quiescent current.  A pair of L91's will put out around 1,500mAh until they drop to 3.3V at which point the LDO will dominate and quickly drain the batteries so let's say the system is dead at 3.3V.

1,500mAh / 8uA = 21 years

Granted there is some self-discharge of the batteries but without performing surgery on your Mote and using a battery-unfriendly DS18B20 you can still make an extremely long-lived temperature node.

Felix

Thanks ChemE for the code and knowledge, can you add the radio sleep code to your code sample?
Also I wonder why you use static inline for every function?

ChemE

You know, I'm glad I decided to start fooling around with this even though I thought that I understood it.  Turns out my turning on and off the DS18B20 wasn't doing anything which is why I'm getting 7.4uA sleep currents.  Pull the probe out of the breadboard and you go down to the 6.4uA we expect.  I've revised my code again (I know I'm crazy) and am using pins 4-6 in order to not conflict with the on-board LED.  I'm also now turning the ground pin into an input prior to sleeping and that is having the desired effect. Attached is my huge somewhat messy code that sleeps the radio as well as properly depowers the DS18B20.

Side note that is totally bizarre and I might need to open a new thread: I notice on my 4th 8S sleep that my current goes from 6.4uA down to 5.9uA.  On a whim, I added 3 15ms sleeps prior to my 32s sleep loop and sure enough now I spend all 32s at 5.9uA.  If that is real for some strange reason that is a big darn deal!

I static inline the routines to make everything faster Felix.  Yes I know I'm only saving a few cycles on a few jumps but I write such tiny code that I can afford to have my routines inlined and save the jumps/returns.  Net effect, slightly larger .hex and faster execution.  I don't have a scope so I can't strictly confirm that but it should be the case based on my understanding.

Lukapple

ChemE,
Thanks for sharing your code with us. Your measurements sounds fantastic. I'll try your code as soon as I get home. Is there any other techniques for precise current measurements except using special instruments like uCurrent adapter? I've just multimeter and some USB scope, which I used only once :)

ChemE

I am not aware of another means of detecting such low currents other than the uCurrent.  On a side note, using a uCurrent and FTDI breakout with a Mote creates quite a rat's nest of wires so I've designed a teeny board to clean this up.  I'll be getting four copies but only need one, so if anyone would like a copy for themselves message me.



https://pcbs.io/share/8D2M0

Lukapple

Hi,
I've tested your code and it works fine!
Presence pulse: Detected	ROM is: 0x28,0xFF,0x0C,0xBF,0x63,0x16,0x04,0x04		Temperature: 78.8F	Conversion took 0 ms


A lot of low-level stuff in your code :) now I have to figure out how to send temperature data to my base node. Can you maybe help me and add a "low-level" method for sending data? Otherwise I'll probably just include RFM69 Felix's library and use sendWithRetry method. :D

And thanks for sharing "teeny board" for uCurrent :)

ChemE

Glad it is working for you, it will barely make a dent in your 9V battery even after a few months.  As far as a low level send, I have some code I'll post shortly which should do the trick.  It can't receive an ACK so it isn't totally reliable; my own library is still pretty young.  I would just send the 2-byte raw value read from the DS18B20 back to the gateway and let the gateway do the conversion to Celsius.  This is what you're after:

uint8_t tempLSB = read();
uint8_t tempMSB = read();


No need to do the calculations on a battery powered node.  If you have a voltage divider you could also read the battery voltage and send it back as well; that is quite commonly done.

ChemE

This should work with the code I've already posted.
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;
}


It is called like so:

uint8_t data[8];  //16-bit temp, 16-bit RH, 16-bit Vcc, 16-bit packet counter
...
data[6] = (++packet_cnt & 0xFF);                  // Increment packet_count and stash the LSB
data[7] = (packet_cnt >> 8);                         // Stash the MSB of the packet counter
...
SendFrame(RECEIVER, data, 8);                   // Send the data

Lukapple

Great, thanks.
Did a quick test, and it doesn't work.
I'm using RFM69HW on RF69_433MHZ. I probably need some additional settings?

ChemE

Yeah sorry that low-level code is really just written for my radio right now which is a 915MHz RFM69CW.  It isn't even safe to use my code because it might fry your radio since it doesn't do anything with PA1 and PA2.

Lukapple

Ah, ok, my radio still works. Do you have plans to support RFM69HW 433MHZ  ;) ?
Do I have to remove following parts of code if I want to use RFM69.h lib?
Probably this parts:
  CHANGE_OP_MODE(SLEEP_MODE); // sleep the radio right away
...
UCSR0B = (1<<TXEN0);//Enable transmitter


and call RFM69's init method instead:
Quote_radio.initialize(FREQUENCY,ID,NETWORKID);

ChemE

Exactly right.  And yes, eventually my library will support all radios.  It is actually just three lines in radio.init that need to change.

Lukapple

Sounds great, thanks. I'll try to find those 3 lines later when I get home :)

ChemE

I forgot that I was able to be super-sneaky.  When the radio module reboots, its frequency defaults to 915MHz so I just removed the three lines which set the RF carrier frequency.  Here are the lines in Felix's library:

{ REG_FRFMSB, (uint8_t) (freqBand==RF69_315MHZ ? RF_FRFMSB_315 : (freqBand==RF69_433MHZ ? RF_FRFMSB_433 : (freqBand==RF69_868MHZ ? RF_FRFMSB_868 : RF_FRFMSB_915))) },
    /* 0x08 */ { REG_FRFMID, (uint8_t) (freqBand==RF69_315MHZ ? RF_FRFMID_315 : (freqBand==RF69_433MHZ ? RF_FRFMID_433 : (freqBand==RF69_868MHZ ? RF_FRFMID_868 : RF_FRFMID_915))) },
    /* 0x09 */ { REG_FRFLSB, (uint8_t) (freqBand==RF69_315MHZ ? RF_FRFLSB_315 : (freqBand==RF69_433MHZ ? RF_FRFLSB_433 : (freqBand==RF69_868MHZ ? RF_FRFLSB_868 : RF_FRFLSB_915))) },


And for specifically 433MHz and translated to my routines it should look like:

#define REG_FRFMSB                  0x07
#define REG_FRFMID                  0x08
#define REG_FRFLSB                  0x09
#define RF_FRFMSB_433             0x6C
#define RF_FRFMID_433             0x40
#define RF_FRFLSB_433             0x00
writeReg( REG_FRMSB, RF_FRMSB_433 );
writeReg( REG_FRMID, RF_FRMID_433 );
writeRef( REG_FRLSB, RF_FRLSB_433 );


The only other thing you might need to do is tweak your PA1 and PA2 settings.  Use Felix's library to initialize your radio and set the power level to what you need and then do readRegsCompact to see what the PA1 and PA2 registers are set at.  Then use the writeReg routines to set those in my radio_init routine and you should be good.

Lukapple

Thanks for hints. Somehow I can't get it to work, I must be missing something  :'(
Is REG_PALEVEL the right register for PA1 and PA2? I can't find method readRegsCompact, so I used:
uint8_t f_palevel = readReg(REG_PALEVEL);
... and on the end of the RadioInit
    writeReg(REG_PALEVEL, f_palevel); //7F
    writeReg( REG_FRFMSB, RF_FRFMSB_433 );
    writeReg( REG_FRFMID, RF_FRFMID_433 );
    writeReg( REG_FRFLSB, RF_FRFLSB_433 );   


I've attached modified sketch.