Quote from: Lukapple on July 26, 2017, 03:27:23 AMIs there a reason you want to be able to request the temperature at any arbitrary moment?
Hi all,
I'm working on simple temperature node, which will report temperature reading on request - base node sends RF "request-temperature" message. I've already searched forum for simmilar projects, but most of projects reports temperature reading to base station in intervals, so radio can be most of time in a sleep mode.
Currently my setup(see attachment) drains 9v battery in a day or so.
Do you guys have any suggestion how to preserve battery life in my case?
QuoteIs there a reason you want to be able to request the temperature at any arbitrary moment?Well probably not, looks like I've chosen wrong approach. I'm measuring pool water temperature and for template sketch I took my garage mote sketch, which sends door status on request :). I'll probably switch to periodic reporting. Thanks for your explanation.
QuoteHow are you sleeping your radio and microcontroller?Currently I'm not using any sleep methods. In a loop I call radio.receiveDone() and then 1s delay.
QuoteThe temperature sensor draws little enough current that you can elect to power it from a digital pin and thus be able to turn it off completely when not needed.For temperature reading I'm using DallasTemperature library, with default settings:
_oneWire = OneWire(pin);
_sensors = DallasTemperature(&_oneWire);
_sensors.begin(); _sensors.requestTemperatures();
_sensors.getTempCByIndex(0);This code is also non-blocking so you can sleep the uC while the temperature conversion happens and then wake back up and TX the result. You should be able to get the project down well below 1uA and your 9V battery should last well over a year.Quote from: Lukapple on July 26, 2017, 09:12:19 AM
Thanks for your answers guys. Sorry for newbie questions - electronics is something that interests me, but otherwise I'm a software dev.
...
That sounds fantastic, thanks for the code, I'll check it out.
My goal is to get battery running for at least for a month or 2. I hope that your code will help me to achieve this goal, otherwise I'll probably switch to periodic reporting.
Quote from: Lukapple on July 26, 2017, 09:12:19 AMThe setup will work quite well on a couple of AAA batteries IF you remove the voltage regulator chip (easy to do, it's 3 pins and you lift the single pin side first) AND you reprogram the fuses to operate at 8MHz instead of 16MHz (which further extends battery life).
About batteries, will my setup work on two AAA batteries, 2x1.5V? Doesn't Moteino require minimum 3.3V input? Sorry again for noob questions.
Quote from: Felix on July 26, 2017, 10:35:01 AMIf you use can use LiPo batteries, with a simple solar charger, the battery will last 'forever'1 and you WILL need to keep the LDO in that case.
IMHO before you sweat too much about the LDO, try it without [sic removing the LDO], you can screw up too many variables going that route and you will (I did too). Getting your code right and sleeping your sensors and any other power drains is more important than saving 2uA.
| Battery | 2x 1.5V AA (do I have to remove LDO voltage regulator if on 2xAA?) | 1x 9V |
| Voltage regulator(LDO) | yes(default) | no |
| Data request | periodic | on demand |
| Pullup resistor | external | internal(328p) - sketch DS18B20 posted by ChemE |
| Sleep mode MCU | sleep MCU between temp. measurements (lib by Felix) and periodic sleep (so this is watchdog sleep?) | none |
| Sleep mode temp. sensor DS18B20 | not sure how. sensor doesn't have sleep mode. I should use mosfet or feed it from another digital pin? | none |
| Temperature lib | DallasTemperature https://github.com/milesburton/Arduino-Temperature-Control-Library (https://github.com/milesburton/Arduino-Temperature-Control-Library) | DS18B20 by ChemE https://github.com/cdl1051/DS18B20_NROO (https://github.com/cdl1051/DS18B20_NROO) |
| Brown out protection bits | on(default) do I need to turn that off if on 2x1.5 AA? | off |
| 328p clock | 8Mhz (do I have to remove LDO?) | 16Mhz |
| Solar charger | yes | no |
Quote from: Felix on July 27, 2017, 08:58:18 AM
Brown-out - I would think you don't need this even if running from 2xAA. When voltage gets below 2V I would consider that the dead point.
QuoteFor 2xAA you will need to remove LDO to make consumption low enough.For now I don't want to remove LDO. Could you suggest which LiPo cells should I use(V, mAH) ?
QuoteThe DS18B20 is not exactly a very low power friendly sensor.I choose DS18B20 because it's in waterproof housing (https://www.adafruit.com/product/381). I hope that it will last for 2 months with LiPo.
QuoteThe Dallas library will work fine it is just bloated, blocking, and slow compared to my code (not that I'm biased mind).Thanks, I'll try to use your code for DS18B20.
#include <LowPower.h>
// Solder-free method of detecting the ROM of a DS18B20 - spread the 3 legs of the sensor wide enough to fit into GND, 13, and 12
// and place the sensor in these pins with the flat side facing the LED on the Uno and the round side facing away from the Uno.
// Then upload and open a serial monitor with a baud rate of 9600.
#include <util/delay.h>
// ====================================================== Pre-Compiler Definitions ======================================================
#define DEBUG 1 // Controls the inclusion or exclusion of serial debug information
#define CLOCK 1 // Controls whether or not temperature reading duration is timed
// Direct port manipulation needed to conduct the OneWire bus
#define PowerPin PB1 // Pin 12 - we will be using this pin to supply Vcc to the DS18B20
#define POWER_TEMP_PROBE PORTB |= (1<<PowerPin) // Define method for powering the DB18B20
#define DEPOWER_TEMP_PROBE PORTB &= ~(1<<PowerPin) // Define method for depowering the DB18B20
#define GroundPin PD7
#define Pin PB0 // Set up pin 13 as the data pin
#define DIRECT_MODE_OUTPUT DDRB |= (1<<Pin)//_BV(Pin) // Much faster and smaller version of pinMode(Pin, OUTPUT)
#define DIRECT_MODE_INPUT DDRB &= ~(1<<Pin) // Much faster and smaller version of pinMode(Pin, INPUT)
#define DIRECT_WRITE_HIGH PORTB |= (1<<Pin) // Much faster and smaller version of digitalWrite(Pin, HIGH)
#define DIRECT_WRITE_LOW PORTB &= ~(1<<Pin) // Much faster and smaller version of digitalWrite(Pin, LOW)
#define DIRECT_READ PINB & (1<<Pin) ? 1 : 0 // One line if else statement using the format [test ? true return : false return]
// Delay values needed for conducting a OneWire bus
#define clk_div 1 // This code assumes a processor frequency of 16MHz but this can be lowered as long as clk_div is updated
#define DELAY_A 6/clk_div // Delay values obtained from http://www.maximintegrated.com/app-notes/index.mvp/id/126
#define DELAY_B 64/clk_div
#define DELAY_C 60/clk_div
#define DELAY_D 10/clk_div
#define DELAY_E 9/clk_div
#define DELAY_F 55/clk_div
#define DELAY_G 0/clk_div
#define DELAY_H 480/clk_div
#define DELAY_I 72/clk_div
#define DELAY_J 410/clk_div
// DS18B20 command codes
#define READROM 0x33 // Read the ROM of a OneWire device; there must only be one OneWire device on the bus!
#define STARTCONVO 0x44 // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH 0xBE // Read from the scratchpad
#define WRITESCRATCH 0x4E // Write to the scratchpad
#define COPYSCRATCH 0x48 // Tells the DS18B20 to copy the contents of the scratchpad to EEPROM
#define SKIPROM 0xCC // Tells all OneWire sensors on the bus that the next command applies to them
#define MATCHROM 0x55 // Tells all OneWire sensors on the bus to listen for a specific ROM next
#define BAUD_RATE 57600
#define myubbr (F_CPU/clk_div/16/BAUD_RATE-1) // Baud rate for UART
int main() {
bool present = 0;
uint8_t ROM[8] ;
#if DEBUG
#if CLOCK
unsigned long start_time, end_time;
// 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
#endif
// Initialize the UART
UBRR0H = (unsigned char)(myubbr>>8);
UBRR0L = (unsigned char)myubbr;
UCSR0A = 0;//Disable U2X mode
UCSR0B = (1<<TXEN0);//Enable transmitter
UCSR0C = (3<<UCSZ00);//N81
_delay_ms(100);
#endif
// Setup for the power pin
DDRB |= (1<<PowerPin); // Set the power pin as an output
POWER_TEMP_PROBE; // Drive the power pin high to power the DS18B20
// Setup for the ground pin
DDRD |= (1<<GroundPin); // Set the ground pin as an output
PORTD &= ~(1<<GroundPin); // Pull the ground pin low
// Set the sensor's resolution to 11 bits
SetResolution(9);
for(;;) { // Loop forever
POWER_TEMP_PROBE;
// Perform a OneWire reset pulse and see if we detect a presence pulse afterward
present = reset();
// If a one-wire device is present, attempt to read its ROM
if (present) {
write(READROM);
for(uint8_t i=0;i<8;i++) {
ROM[i]=read();
}
}
#if DEBUG
simpletx("Presence pulse: ");
if(present) {
simpletx("Detected");
} else {
simpletx("Not Detected");
}
simpletx("\tROM is: ");
for(uint8_t i=0;i<8;i++) {
simpletx("0x");
txByteAsHex(ROM[i]);
if (i!=7) simpletx(",");
}
simpletx("\t\t");
#endif
// If we detected a Dallas family sensor, let's go ahead and take a temperature reading
if (ROM[0]=0x28) { // The first byte of all dallas sensors is always 0x28
reset();
write(SKIPROM);
write(STARTCONVO);
#if CLOCK
start_time = millis();
#endif
LowPower.powerDown(SLEEP_60MS, ADC_OFF, BOD_OFF); // Put the uC to sleep while the temperature conversion proceeds to save power
LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF); // Put the uC to sleep while the temperature conversion proceeds to save power
//while(!read()); //_delay_ms(750); // Can either wait 750 ms for the conversion to be done or else read until we get a 1 back from the DS18B20 meaning it is signaling complete
#if CLOCK
end_time = millis();
#endif
reset();
write(SKIPROM);
write(READSCRATCH);
uint8_t tempLSB = read();
uint8_t tempMSB = read();
DEPOWER_TEMP_PROBE; // Rather than perform a reset to tell the probe to stop sending data, just cut the power and it will get the message!
#if DEBUG
simpletx("Temperature: ");
txRawTempAsFloat( tempMSB<<8 | tempLSB );
simpletx("F");
#if CLOCK
simpletx("\tConversion took ");
txInt(end_time-start_time);
simpletx(" ms");
#endif
simpletx("\n");
_delay_us(300);
#endif
}
uint8_t sleep_count=0;
do LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
while (++sleep_count < 4);
//_delay_ms(10000);
} // End for
} // End main
// ============================================================================================================================================================
// Sets the temperature measurement resolution of the DS18B20 to either 9, 10, 11, or 12 bits If any other number is passed, the sensor will be set to 12 bits
// Only works if there is a single DS18B20 on the one wire network
// ============================================================================================================================================================
static inline void SetResolution(uint8_t resolution) {
reset();
write(SKIPROM);
write(WRITESCRATCH);
write(0x00);
write(0x00);
switch (resolution) {
case 9: write(0x1F); break;
case 10: write(0x3F); break;
case 11: write(0x5F); break;
default: write(0x7F); break;
}
reset();
write(SKIPROM);
write(COPYSCRATCH);
//_delay_ums(15);
}
static inline uint8_t read() {
uint8_t r=0;
noInterrupts();
for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
DIRECT_MODE_OUTPUT;
DIRECT_WRITE_LOW;
_delay_us(DELAY_A);
DIRECT_MODE_INPUT;
DIRECT_WRITE_HIGH; // New line for no resistor modification / enable pull-up resistor
_delay_us(DELAY_E);
if (DIRECT_READ) r |= bitMask;
_delay_us(DELAY_F);
}
interrupts();
return r;
}
static inline void write(uint8_t v) {
noInterrupts();
for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) {
DIRECT_WRITE_LOW;
DIRECT_MODE_OUTPUT;
if (bitMask & v) {
_delay_us(DELAY_A);
DIRECT_WRITE_HIGH;
_delay_us(DELAY_B);
} else {
_delay_us(DELAY_C);
DIRECT_WRITE_HIGH;
_delay_us(DELAY_D);
}
}
DIRECT_MODE_INPUT;
interrupts();
}
static inline uint8_t reset(void) {
noInterrupts();
DIRECT_MODE_INPUT;
DIRECT_WRITE_LOW;
DIRECT_MODE_OUTPUT;
_delay_us(DELAY_H);
DIRECT_MODE_INPUT;
DIRECT_WRITE_HIGH; // New line for no resistor modification / enable pull-up resistor
_delay_us(DELAY_I);
uint8_t ret = !(DIRECT_READ);
interrupts();
_delay_us(DELAY_J);
return ret;
}
static inline void simpletx( char * string ) {
/*if (UCSR0B != (1<<TXEN0)) { //do we need to init the uart?
UBRR0H = (unsigned char)(myubbr>>8);
UBRR0L = (unsigned char)myubbr;
UCSR0A = 0;//Disable U2X mode
UCSR0B = (1<<TXEN0);//Enable transmitter
UCSR0C = (3<<UCSZ00);//N81
_delay_ms(30);
}*/
while (*string) {
while ( !( UCSR0A & (1<<UDRE0)) );
UDR0 = *string++; //send the data
}
}
static inline void txByteAsHex(uint8_t inp) {
char snd[3];
uint8_t tmp = inp>>4;
if (tmp<10) {
snd[0]=48+tmp;
} else {
snd[0]=55+tmp;
}
tmp=inp%16;
if (tmp<10) {
snd[1]=48+tmp;
} else {
snd[1]=55+tmp;
}
snd[2]='\0';
simpletx(snd);
}
static inline void txInt(long inp) {
long temp = inp;
uint8_t numChars=0;
boolean isNegative=false;
// Check to see if there is a negative sign
if(temp<0){
isNegative=true;
numChars++;
temp*=-1;
}
do {
numChars++;
temp /= 10;
} while ( temp );
char buf[numChars];
// Write the negative sign if present and the terminating null character
temp=inp;
buf[numChars]=0;
if(isNegative) {
temp*=-1;
buf[0]='-';
}
int i = numChars - 1;
do {
buf[i--] = temp%10 + '0';
temp /= 10;
} while (temp);
simpletx(buf);
}
// Converts the raw temperature from a DS18B20 directly to a string containing the temperature in °F with 1 decimal place
// avoids unnecessary floating point math, float variables, and casts, and 32-bit math
// TODO: May not work properly with temperatures below 32°F
static inline void txRawTempAsFloat(uint16_t raw) {
char buffer[6];
uint8_t decimalPos = 2; // default case of a temp between 0 and 99.9
uint8_t nullPos = 4; // default case of a temp between 0 and 99.9
uint16_t temp;
// Check to see if the temperature passed in is negative
if (raw>>11) {
// Can't get here unless one of the 5 most-significant bits are ones which means we have a negative number, convert it
raw = ~(raw-1); // Convert the two's compliment number back into one's compliment
if (raw > 284) { // This temperature is far enough negative in the celcius scale that it is also negative on the farhenheit scale
decimalPos += 1; // Account for the negative sign's place in the string
nullPos += 1; // Account for the negative sign's place in the string
buffer[0] = '-'; // Write the negative sign in the string
}
temp = (9*raw)/8-320; // Keeps only 1 decimal place but uses 16-bit math
} else {
temp = (9*raw)/8+320; // Keeps only 1 decimal place but uses 16-bit math
}
// Convert the raw temperature into the temperature in Fx10 so that one decimal place is kept
//uint32_t temp = (raw*1125ul+320000ul)/1000ul; // Keeps all 4 decimal places but uses 32-bit math
if(temp>=1000) { // We're looking at a positive number with three digits
decimalPos += 1;
nullPos += 1;
}
buffer[nullPos--] = '\0';
do {
if (nullPos==decimalPos) buffer[nullPos--] = '.';
buffer[nullPos--] = temp % 10 + '0';
temp /= 10;
} while (temp);
simpletx(buffer);
}
Presence pulse: Detected ROM is: 0x28,0xFF,0x0C,0xBF,0x63,0x16,0x04,0x04 Temperature: 78.8F Conversion took 0 ms
uint8_t tempLSB = read();
uint8_t tempMSB = read();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;
}
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
CHANGE_OP_MODE(SLEEP_MODE); // sleep the radio right away
...
UCSR0B = (1<<TXEN0);//Enable transmitterQuote_radio.initialize(FREQUENCY,ID,NETWORKID);
{ 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))) },
#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 );
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 );
Transceiver's operating modes:
000 → Sleep mode (SLEEP)
001 → Standby mode (STDBY)
010 → Frequency Synthesizer mode (FS) 011 → Transmitter mode (TX)
100 → Receiver mode (RX)#define AUTO_TRANSMITTER B00111011 // Enter = FIFO not empty; Exit = Packet Sent; Intermediate Mode = TXQuote from: ChemE on July 30, 2017, 07:00:12 AM
Hmm, I might have found the setting mismatch causing the node to not transmit. I had set automode to TX on rising edge of FIFO threshold but I'm not setting what that threshold is. Try changing your definition of automode to this:Code Select#define AUTO_TRANSMITTER B00111011 // Enter = FIFO not empty; Exit = Packet Sent; Intermediate Mode = TX
This will force the node to TX as soon as the first byte hits the FIFO. Also all settings on both the node and gateway must match other than automode and FIFO level. The settings that I've been posting are 300kbps so that the radio broadcasts for 1/6th as long as the default library. Felix's default bandwidth is 55.555kbps. So you had a node speaking at 300 and a gateway listening at 55.
PRR = B10111101; // halt other peripherials that are not neededBit 7 – PRTWI0: Power Reduction TWI0
Writing a logic one to this bit shuts down the TWI 0 by stopping the clock to the module. When waking up the TWI again, the TWI should be re initialized to ensure proper operation.
Bit 6 – PRTIM2: Power Reduction Timer/Counter2
Writing a logic one to this bit shuts down the Timer/Counter2 module in synchronous mode (AS2 is 0). When the Timer/Counter2 is enabled, operation will continue like before the shutdown.
Bit 5 – PRTIM0: Power Reduction Timer/Counter0
Writing a logic one to this bit shuts down the Timer/Counter0 module. When the Timer/Counter0 is enabled, operation will continue like before the shutdown.
Bit 3 – PRTIM1: Power Reduction Timer/Counter1
Writing a logic one to this bit shuts down the Timer/Counter1 module. When the Timer/Counter1 is enabled, operation will continue like before the shutdown.
Bit 2 – PRSPI0: Power Reduction Serial Peripheral Interface 0
If using debugWIRE On-chip Debug System, this bit should not be written to one. Writing a logic one to this bit shuts down the Serial Peripheral Interface by stopping the clock to the module. When waking up the SPI again, the SPI should be re initialized to ensure proper operation.
Bit 1 – PRUSART0: Power Reduction USART0
Writing a logic one to this bit shuts down the USART by stopping the clock to the module. When waking up the USART again, the USART should be re initialized to ensure proper operation.
Bit 0 – PRADC: Power Reduction ADC
Writing a logic one to this bit shuts down the ADC. The ADC must be disabled before shut down. The analog comparator cannot use the ADC input MUX when the ADC is shut down.
Quote from: ChemE on July 31, 2017, 01:08:34 PM
Felix uses millis() to control the timouts in SendWithRetry. Timer 0 controls the counter that millis() relies upon to function. So you would want to change that bit to 0 so it stays on. That should fix that issue.