Closed philippedc closed 4 years ago
Hi Philipp,
0xAE
command is the only way to put ssd1306 display to sleep mode. According to datasheet in sleep mode ssd1306 controller power consumption should be less than 10uA for Vdd (logic power), and less than 10uA for Vcc (panel power) without panel attached. That means, that values from datasheet depend on display panel manufacturer. But anyway OLED display panel should not consume itself much power, if no pixels are ON. For display ON modes ssd1306 datasheet says that logic consumption is upto 150uA, and panel consumption is upto 780uA. Maybe, on 2 circuits the display actually doesn't go to sleep more for some reason?
Another thing: how did you implement pull up for i2c lines in your schematics?
Many Thanks for your quick reply ! Here is the code:
`/*
Station météorologique pour ATtiny85 à économie d'énergie
pour la partie OLED écran I2C : https://github.com/lexus2k/ssd1306
pour la partie sonde BME280 :
https://github.com/conurb/low_energy_sensor/blob/master/low_energy_sensor.ino
et pour la partie SLEEP :
https://github.com/blevien/attiny85-sleep/blob/master/attiny85-sleep.ino
https://gist.github.com/JChristensen/5616922
ATTENTION attachInterrupt(0, poussoir, LOW) gère par défaut les interruption en INT0,
cad en PB2 déjà utilisé pour le SCL
D'où l'astuce pour changer la commande de l'interruption :
https://www.insidegadgets.com/2011/02/27/how-to-use-the-pin-change-interrupt-on-attiny85/
+-------+
1|* |8 VCC
PB3 2| |7 PB2 = SCL (BME280 + OLED)
PB4 3| |6 PB1 => LED
GND 4| |5 PB0 = SDA (BME280 + OLED)
+-------+
connection I2C :
SDA en PB0 = pin5
SCL en PB2 = pin7
A3 -> PB3 = pin2 => détecteur d'humidité
LED en PB1 = pin6 => led d'avertissement de sécheresse
SWI en PB4 = pin3 => push-button to wake-up
*/
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <EEPROM.h>
#include "ssd1306.h" // https://github.com/lexus2k/ssd1306
#include "bme280_tiny_i2c.h" // https://github.com/conurb/low_energy_sensor
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
int v;
int memo[]={0,0,0};
char chaine[20];
bool r1=false;
volatile bool r2=false;
byte j;
BME280 bme;
void setup() {
pinMode(1, OUTPUT); // LED
pinMode(3, INPUT_PULLUP); // pin2 ground humidity level sensor
pinMode(4, INPUT_PULLUP); // pin3 to push-button
j = EEPROM.read(3);
//bme280_init(&bme);
sbi(GIMSK,PCIE); // Turn on Pin Change interrupt
sbi(PCMSK,PCINT4); // Which pins are affected by the interrupt
// the watch dog sets the interval delay :
// 0=16ms, 1=32ms,2=64ms, 3=128ms, 4=250ms, 5=500ms, 6=1s, 7=2s, 8=4s, 9=8s
setup_watchdog(9);
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode available
// Replace the line below with ssd1306_128x32_i2c_init() if you need to use 128x32 display */
//ssd1306_128x64_i2c_init();
//ssd1306_fillScreen(0x00);
//ssd1306_setFixedFont(ssd1306xled_font6x8);
//ssd1306_printFixed(0, 8, "Line 1. Normal text", STYLE_NORMAL);
//ssd1306_printFixed(0, 16, "Line 2. Bold text", STYLE_BOLD);
//ssd1306_printFixed(0, 24, "Line 3. Italic text", STYLE_ITALIC);
//ssd1306_printFixedN(0, 32, "Line 4. Double size", STYLE_BOLD, FONT_SIZE_2X);
}
void lecture() {
// read values from sensor
bme280_read_sensor(&bme);
v = bme.temperature /10;
afficher(0);
v = bme.humidity /1000;
afficher(1);
v = bme.pressure /100;
afficher(2);
// battery level part
//v = min(22528/analogRead(12),100); // for 5V power supply
v = min(33300/analogRead(12),99);
afficher(3);
}
void afficher( byte q ) { // q: 0 => t, 1 => h, 2 => p
char e;
// value in string of characters
if( q < 2) {
if( q == 0 ) { // case temperature
e = tendance( v/5, memo[q] ); // only half degree precision
memo[q] = v/5;
chaine[0] = 'T'; chaine[1] = ':'; chaine[2] = ' ';
if( v < 0 ) chaine[3] = '-';
else if( v > 99 ) { chaine[3] = v/100 +48; v=v%100; }
if( chaine[3] == '0' ) chaine[3] = ' ';
chaine[4] = v/10 +48; // +48 for ASCII reasons
chaine[5] = '.';
chaine[6] = v%10 +48;
chaine[7] = '\''; chaine[8] = 'C'; chaine[9] = ' ';
chaine[10] = e; chaine[11] = ' '; chaine[12] = ' ';
} // end of test q == 0
else { // case hygrometry
e = tendance( v, memo[q] );
memo[q] = v;
chaine[13] = 'H'; chaine[14] = ':'; chaine[15] = ' ';
chaine[16] = v/10 +48;
chaine[17] = v%10 +48;
chaine[18] = '%'; chaine[19] = ' ';
chaine[20] = e;
// display line 1 to OLED
ssd1306_printFixed(0, 25, chaine, STYLE_NORMAL);
} // end of else q == 0
} // end of test q < 2
else {
if( q == 2 ) { // case pressure
e = tendance( v, memo[q] );
memo[q] = v;
chaine[0] = 'P'; chaine[1] = ':'; chaine[2] = ' ';
chaine[3] = v/1000 +48; v=v%1000;
if( chaine[3] == '0' ) chaine[3] = ' ';
chaine[4] = v/100 +48; v=v%100;
chaine[5] = v/10 +48;
chaine[6] = v%10 +48;
chaine[7] = 'm'; chaine[8] = 'B'; chaine [9] = ' ';
chaine[10] = e; chaine[11] = ' '; chaine[12] = ' ';
} // end of test q == 2
else { // case battery level
chaine[13] = 'B'; chaine[14] = 'a'; chaine[15] = 't';
chaine[16] = ':'; chaine[17] = ' ';
if( v > 99 ) {
chaine[18] = v/100 +48; v=v%100;;
chaine[19] = v/10 +48;
chaine[20] = v%10 +48;
}
else {
chaine[18] = v/10 +48;
chaine[19] = v%10 +48;
chaine[20] = ' ';
}
// display line 2 to OLED
ssd1306_printFixed(0, 50, chaine, STYLE_NORMAL);
} // end of else q == 2
} // end of else q < 2
} // end of afficher()
char tendance( int w, int n ) {
if( w > n ) return('+');
else if( w < n ) return('-');
else return('=');
}
void ledflash() {
digitalWrite( 1, HIGH );
delay(1);
digitalWrite( 1, LOW );
//delay(500);
}
void loop() {
if( r2 == true ) { // when push-button is pressed to LOW
r2 = false;
r1 = false;
bme280_init(&bme);
memo[0] = EEPROM.read(0)-30;
memo[1] = EEPROM.read(1);
memo[2] = EEPROM.read(2)+900;
ssd1306_128x64_i2c_init();
ssd1306_fillScreen(0x00);
ssd1306_setFixedFont(ssd1306xled_font6x8);
ssd1306_printFixed(24, 2, "Station Meteo", STYLE_BOLD);
lecture();
EEPROM.write(0, memo[0]+30); // +30 to prevent against any negatice value
EEPROM.write(1, memo[1]);
EEPROM.write(2, memo[2]-900); // -900 to stay in byte range
EEPROM.write(3, j); // save the counter
delay(3000);
ssd1306_clearScreen();
ssd1306_displayOff();
}
if( r1 == true ) { // every 8s
r1 = false;
// ground humidity part
if( j++ > 14 ) { // every 8x15 seconds
j = 0;
sbi(ADCSRA,ADEN); // Switch Analog to Digital converter ON
if( analogRead(3) > 800 ) {
ledflash();
j = 15;
}
}
}
system_sleep(); // go to sleep after process
}
// set system into the sleep state - system wakes up when wtchdog is timed out
void system_sleep() {
cbi(ADCSRA,ADEN); // Switch ADC OFF, bacause ADC uses ~320uA
EEPROM.write(3, j); // save the counter
power_all_disable(); // power off ADC, Timer 0 and 1, serial interface
sleep_enable();
sleep_cpu(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
power_all_enable(); // power everything back on
}
void setup_watchdog( byte tempo ) {
byte val;
val = tempo & 7;
if (tempo > 7) val |= (1<<5);
val |= (1<<WDCE);
MCUSR &= ~(1<<WDRF); // start timed sequence
WDTCR |= (1<<WDCE) | (1<<WDE); // set new watchdog timeout value
WDTCR = val;
WDTCR |= _BV(WDIE);
}
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) { r1 = true; r2 = false; } // set global flag
// External Interrupt Service / is executed when push-button is pressed
ISR(PCINT0_vect) { r2 = true;} // set global flag
Hello,
why don't you call sd1306_clearScreen( );
and ssd1306_displayOff();
before entering sleep mode in your example?
The system_sleep()
code looks OK: you set sleep mode SLEEP_MODE_PWR_DOWN
, then disable ADC, sleep_enable(), and perform sleep_cpu().
And could you please, clarify? If you say that you have 3 devices, and 2 of them consume too much power, then maybe the root cause is hardware issue?
I call ssd1306_clearScreen( );
and ssd1306_displayOff();
once I do not need to display anything, so in the same loop() sequence than initialization with ssd1306_128x64_i2c_init();
etc...
Hoever you're right, I will try by repeating the displayOff instruction in the sleep function.
Thanks
So I made tests : when I insert ssd1306_clearScreen( );
and ssd1306_displayOff();
in the sleep function, I get a sleep current of 1mA. After I push the wakeup button, the next sleeping mode gives 0,24mA.
So it can be said that these 2 instructions without a ssd1306 initialisation will put the ssd1306 in a mode that it will have a current about 4 times the "normal" sleeping current.
So it is worst than expected.
Hello,
I've performed tests for ssd1306 display (i2c, 128x64) and ssd1306_displayOff()
/ssd1306_displayOn()
functions. And here are my results:
So, I would recommend you to check schematics, and pcb assembly.
You are right, I have done the same tests, with similar results. I have ordered new BME280 to be able to test them separately. As the circuit is only a BME280, a SSD1306 and one LED, the circuit is quickly checked... Many thanks for your help.
Let me know, when you find the root cause. Thank you
Any updates on low power mode?
Hi @lexus2k I confirm the over power consumption comes from the BME280. Thanks
Thank you very much
Hi Aleksei, I'm wondering if there is a better way than _
ssd1306_displayOff()
_ to put the oled display in low power mode when the Attiny85 is in sleep mode ? Strangely I have 3 identical circuits based around an Attiny85, a BME280 and an oled SSD1306. In sleep mode I have one with 0.22mA, another with 0.25mA and one with 0.011mA only. I cannot understand why I get 20 time less power for this last one, except the oled itself.