BTW a useful exercise anyway. I noticed this way (scoping) that the si7021 lib for arduino keeps the 328p awake while waiting >10ms for the measurement. What a waste of power. I now use powerDown while waiting. That will be a big improvement for nodes that measure often but only send on significant changes.
Yup, the HTU21D code I found likes to make the same measurements using HOLD thus keeping the uC awake and wasting energy. By switching to sleep for 15ms immediately after issuing a NO_HOLD command, the uC is only awake for 440uS during one entire temp/RH measurement cycle. 30ms asleep vs. 10ms awake is no comparison. I also noticed when I dug deeply into the HTU21D code that the two wire library uses a pretty slow baud rate thus prolonging the wake period unnecessarily. I don't have a Si7021 to play with but see if this code can be adapted to reduce the wake cycle.
HTU21D Code
#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); while ((TWCR & (1<<TWSTO)) && ++counter)
#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 stopTWI() {
uint16_t counter;
STOP_TWI;
}
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
}
Main Program Code
void setup() {
initTWI();
radio.initialize(FREQUENCY,NODEID,NETWORKID);
radio.setPowerLevel(31);
radio.sleep();
DDRC |= _BV(PC3) | _BV(PC2); // Set PC3 and PC2 to be outputs
PORTC |= _BV(PC2); // pull PC2 high to act as 3.3V for the HTU21D
PORTC &= ~_BV(PC3); // pull PC3 low to act as ground for the HTU21D
}
void loop(){
uint8_t data[4]; //Temp & RH stored as 16-bit raw values; no floating point math
uint8_t sleep_count = 0;
// Make the measurements
issueCommand(WRITE_USER_REGISTER, ELEVEN_BIT_TEMP);
issueCommand(TRIGGER_TEMP_MEASURE_NOHOLD,0);
LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);
readRaw(&data[0]);
issueCommand(WRITE_USER_REGISTER, EIGHT_BIT_RH);
issueCommand(TRIGGER_HUMD_MEASURE_NOHOLD,0);
LowPower.powerDown(SLEEP_15MS, ADC_OFF, BOD_OFF);
readRaw(&data[2]);
stopTWI();
// Send the data
radio.send(RECEIVER, data, 4, 0); //target node Id, message as string or byte array, message length, ack requested
radio.sleep();
do { // Sleep for 64 seconds
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
} while (++sleep_count < 8);
}
I still find it amusing that that utterly disgusting mess of libraries which called each other to implement a Two Wire interface could be boiled down to 55 lines of code inclusive of comments.
Yeah no kidding. The si7021 library is terrible now that I've taken a look at it. I really liked this one:
void SI7021::_command(byte * cmd, byte * buf ) {
_writeReg(cmd, sizeof( cmd ));
_readReg(buf, sizeof( but));
}
Oh well.
I've used this (http://homepage.hispeed.ch/peterfleury/doxygen/avr-gcc-libraries/group__pfleury__ic2master.html) one in some projects. It's nice, tight and small.
I've added the powerDown into the existing si7021 lib though - just feeling lazy today ...