On the previous article I wrote about how to use the real-time clock module with the EEPROM non-volatile memory. Now it is time to get something working that resembles a watch. Make something that matters. How about adding a display? Last post it was possible to achieve a power consumption of only 16 uA (improved) 0.7uA on the smartwatch prototype. Now we can see how much power the watch will consume. In this case, it is an application that needs to consume the smallest current possible but also needs a display, then one can use a OLED display, which is the abbreviation of “Organic Light-Emitting Diode”, which is a much more economic display in terms of current than the regular LED displays. An overview about OLEDs, first.

OLED Advantages and Disadvantages

The LCD is currently the most used display in small devices and in large-screen TV’s. Regular LEDs often form the digits on clocks and other electronic devices. However OLEDs offer many advantages over LCDs and LEDs:

  • OLEDs do not require backlighting like LCDs. LCDs work by selectively blocking areas of the backlight to make the images that you see, while OLEDs generate light themselves. Because of that, OLEDs consume much less power than LCDs;
  • OLEDs are easier to produce and can be made to larger sizes;
  • Because LCDs work by blocking light, they have an inherent viewing obstacle from certain angles. OLEDs produce their own light, so they have a much wider viewing range;
  • The layers of a OLED are thinner, lighter and more flexible than the layers in an LED or LCD;
  • The substrate of a OLED can be flexible instead of rigid. OLED substrates can be plastic rather than the glass used for LEDs and LCDs, and;
  • OLEDs are brighter than LEDs. Because the organic layers of a OLED are much thinner than the corresponding inorganic crystal layers of an LED, the conductive and emissive layers of an OLED can be multi-layered. Also, LEDs and LCDs require glass for support, and glass absorbs some light. OLEDs do not require glass.

Disadvantages with OLED:

  • OLED films have shorter lifetimes;
  • Manufacturing processes are expensive right now, and;
  • Water can easily damage OLEDs.

Choosing and placing the display

The cheapest OLED displays available are probably the ones found on Ebay. There you can find into many types,with different colors and sizes. The most common is the 128×64 pixels all white display. But there are others, 128×32, or with part yellow color, part blue. All of those displays are supported by the U8glib (library we’ll use). Also, you can find the display already soldered into a module, easy to use, or just the part, yet to be soldered. Because we are just testing at our lab we will use the complete module. In the future I’ll make an article about soldering this display to our watch.

If you followed the previous article, here, your breadboard must be almost ready to receive the OLED display. Just put it there, connecting the VCC and GND to their corresponding pins and also the SCL and SDA to Arduino pins A5 and A4, respectively.

Which library to use

There are many possible libraries the programmer can use. It s also possible to make your own custom-made library. The three most common are:

Following a video posted by Julian Ilett on YouTube, he compares the time of response of each library with his own method. The result is very interesting.

When choosing the library, not only you have to take into account the response time, but also the support available on the web, the functions, the ROM and RAM usage, and also their fonts available to use. As we will see below, making your own font, or choosing one with a restricted number of characters can make total difference for your program. This is because the larger the font, the more ROM will be consumed.

Saving ROM space with the font type

As we are making a smartwatch with an Arduino Pro Mini (bare bones), we don’t have much memory available. This is one of the major problems of the Atmega 328p chip. You can try to compile many different codes with the function u8g.setFont() and you will easily see how quickly you will run out of memory. This is because:

  • The bigger the font type, the more memory it uses;
  • More characters means more ROM used and less memory left, and;
  • The more fonts you use, the less memory left.

In order to avoid running out of program memory, avoid using many different font types on the same application and avoid using big fonts. Furthermore, you should use fonts ended with “R” or “r” because that means they only have some restricted chars, so they don’t have useless characters you don’t want. Also fonts ended with “n” consume less ROM space. For example, the same code below, compiled with different font types “u8g_font_fub14n“, “u8g_font_fub14r“, “u8g_font_fub14” and “u8g_font_fub17” ended up having:

  • u8g_font_fub14n: ROM usage 35%;
  • u8g_font_fub14r: ROM usage 42%
  • u8g_font_fub14: ROM usage 51%;
  • u8g_font_fub17: ROM usage 55%;

It is also possible to make your own font type by following this discussion here. Basically you can import or draw yourself a font with FontForge and generate the .bdf file. Then use the program bdf2u8g.exe to generate to the internal u8glib font format. At last you have to go to the U8glib library and insert all calls to your new font for the Arduino IDE to recognize it.

General tips about u8glib

Now using this library to make the display sleep is relatively easy, because you just have to call the functions below. The hard part is that you have to adjust your code really nicely for it to work. For example, you can’t call the display to sleep and put the Arduino on power-down mode right away. The same happens to wake it up. One common error is to putting the display to sleep into a certain way it wont wake up when you want it to. You also have to test the code with the multimeter measuring the current, because the display can appear to be sleeping, but still consuming a lot of power.

 u8g.sleepOn();
 u8g.sleepOff();

Program the Arduino

Just copy the code below and send it to the Atmega 328p.

//INCLUDES
#include <LowPower.h>
#include <Wire.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include 
#include  //sleep mode
#include 

//GLOBAL
#define LED_PIN 13

//variables
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);

//BUTTON
#define BUTTON 2
#define NUMCOUNTB1 7
volatile bool buttonInterrupt = false;
unsigned int BUTTON1 = 1;

//DISPLAY
unsigned long int timer_draw = 0;
String str1; //declaring string

//TIME
#define PCF8563address 0x51
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
String days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };

//EEPROM
int addr = 2;
#define addStp 0 //address of byte to store number of steps
#define addCal 4 //address of byte to store number of calories 
#define addKm 8 //address of byte to store number of km 

byte bcdToDec(byte value){
 return ((value / 16) * 10 + value % 16);
}

byte decToBcd(byte value) {
 return (value / 10 * 16 + value % 10);
}

void readPCF8563() {
 Wire.beginTransmission(PCF8563address);
 Wire.write(0x02);
 Wire.endTransmission();
 Wire.requestFrom(PCF8563address, 7);
 second = bcdToDec(Wire.read() & B01111111); // remove VL error bit
 minute = bcdToDec(Wire.read() & B01111111); // remove unwanted bits from MSB
 hour = bcdToDec(Wire.read() & B00111111);
 dayOfMonth = bcdToDec(Wire.read() & B00111111);
 dayOfWeek = bcdToDec(Wire.read() & B00000111);
 month = bcdToDec(Wire.read() & B00011111); // remove century bit, 1999 is over
 year = bcdToDec(Wire.read());
}

void wakeUp() {
 while (!digitalRead(BUTTON)) {};
 BUTTON1++;
 if (BUTTON1 == 1) {
 u8g.sleepOff();
 delay(50);
 }
 else {
 if (BUTTON1 == 5) {
 BUTTON1 = 0;
 u8g.sleepOn();
 }
 }
 while (!digitalRead(BUTTON)) {};
 delay(100);
}


void setup() {
 pinMode(BUTTON, INPUT_PULLUP);
 pinMode(LED_PIN, OUTPUT);
 pinMode(0, OUTPUT);
 pinMode(1, OUTPUT);
 pinMode(4, OUTPUT);
 pinMode(5, OUTPUT);
 pinMode(6, OUTPUT);
 pinMode(7, OUTPUT);
 pinMode(8, OUTPUT);
 pinMode(9, OUTPUT);
 pinMode(10, OUTPUT);
 pinMode(11, OUTPUT);
 pinMode(12, OUTPUT);

 digitalWrite(0, LOW);
 digitalWrite(1, LOW);
 digitalWrite(4, LOW);
 digitalWrite(5, LOW);
 digitalWrite(6, LOW);
 digitalWrite(7, LOW);
 digitalWrite(8, LOW);
 digitalWrite(9, LOW);
 digitalWrite(10, LOW);
 digitalWrite(11, LOW);
 digitalWrite(12, LOW);
 digitalWrite(LED_PIN, LOW);

 u8g.setFont(u8g_font_fub14r);
 u8g.setColorIndex(1);
 attachInterrupt(0, wakeUp, LOW);
 delay(100);
}


void loop(){
 while (BUTTON1 != 0) {
 if (millis() - timer_draw > 490) {
 u8g.firstPage();
 do {
 switch (BUTTON1) {
 case 0:
 break;
 case 1:
 drawtime();
 break;
 }
 } while ( u8g.nextPage() );
 timer_draw = millis();
 }
 }
 delay(10);
 LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
 delay(10);
}

void drawtime(void) {
 char b[20];
 readPCF8563();
 str1 = String(hour); //converting integer into a string
 if (hour < 10) str1 = "0" + str1;
 str1 = str1 + ":";
 if (minute < 10) str1 = str1 + "0" + String(minute);
 else str1 = str1 + String(minute);
 str1 = str1 + ":";
 if (second < 10) str1 = str1 + "0" + String(second);
 else str1 = str1 + String(second);
 str1.toCharArray(b, 11);
 u8g.drawStr( 24, 50, b);

 str1 = String(dayOfMonth);
 if (dayOfMonth < 10) str1 = "0" + str1;
 str1 = str1 + "/";
 if (month < 10) str1 = str1 + "0" + String(month);
 else str1 = str1 + String(month);
 str1 = str1 + "/" + "20" + String(year);
 str1.toCharArray(b, 11);
 u8g.drawStr( 11, 20, b);

 u8g.drawLine(0, 0, 127, 0);
 u8g.drawLine(0, 23, 127, 23);
 u8g.drawLine(127, 0, 127, 63);
 u8g.drawLine(127, 63, 0, 63);
 u8g.drawLine(0, 63, 0, 0);
}

void EEPROMWritelong(int address, long value) {
 //Decomposition from a long to 4 bytes by using bitshift.
 //One = Most significant -> Four = Least significant byte
 byte four = (value & 0xFF);
 byte three = ((value >> 8) & 0xFF);
 byte two = ((value >> 16) & 0xFF);
 byte one = ((value >> 24) & 0xFF);

 //Write the 4 bytes into the eeprom memory.
 EEPROM.write(address, four);
 EEPROM.write(address + 1, three);
 EEPROM.write(address + 2, two);
 EEPROM.write(address + 3, one);
}

long EEPROMReadlong(long address) {
 //Read the 4 bytes from the eeprom memory.
 long four = EEPROM.read(address);
 long three = EEPROM.read(address + 1);
 long two = EEPROM.read(address + 2);
 long one = EEPROM.read(address + 3);

 //Return the recomposed long by using bitshift.
 return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

Conclusion – Part 1

blog 2

With the display on, the board consumes around 14 mAh, which is relatively a lot. I’ll study how to decrease this. It is possible to get this number lower by putting the Arduino to sleep between refreshing the display, but the most of the power will end up going to the display. There is no escape. But our watch wont be showing the time always. It will have a button to activate the display when we want it to. So let’s make another test, this time putting the display to sleep with the code and measuring the current.

WP_20150920_18_03_18_Pro

Well, that ended up being a lot lower with the display off. With the brown-out enabled it was 315 uA, however, after the last time this post was edited, the current was only 238 uA (down-out disabled). Even now I believe it is still possible to lower that value. All the parts are working, and our watch is still counting the time, but at the lowest power consumption possible. At this state, a watch with a regular (and very common) CR2032 240mAh battery would last amazing 31 (improved) 42 days.

Conclusion – Part 2

IIC Version

I was still trying to figure out if I could lower even more the current. So I e-mailed the guys that produced my Oled module (http://heltec.cn), and again with some help from Dennis, I realized capacitors C3 and C4 could be the reason  the current was still high, because in the SDD1306’s datasheet (Oled microcontroller), it was stated that the display could consume way less than it was on my tests.

Just by desoldering those capacitors, the power consumption went to 11.53 mA when on, and only 1.1 uA when everything was sleep. This is just incredible… again:

At this state, a watch with a regular (and very common) CR2032 240mAh battery would last amazing 31 (improved) 42 days (improved again) 25 years. Ouch!

Now, in order to decrease the current with the display on, I had a conversation with @PumaBare and he said that to change the display contrast I would have to write to the register 0x81. Unfortunately, I found out that the command “u8g.setContrast(value)” was not working. By looking at the Adafruit SSD1306 library I figured that the commands should be:

#define BRIGHTNESS 0x01
#define BRIGHTNESSREG 0x81

First declare the display contrast register and the brightness value you want and then the function you should call:

void setBrightness( ){
    Wire.beginTransmission(0x3c);
    Wire.write(0x00);
    Wire.write(BRIGHTNESSREG);
    Wire.endTransmission();
    Wire.beginTransmission(0x3c);
    Wire.write(0x00);
    Wire.write(BRIGHTNESS);
    Wire.endTransmission();
}

Now every time you want to set the display to the minimum value you can just call your new function. With this implementation the current with the display on went to just 8.08 mA, or 30% lower than before. Very good indeed. Now it is time to add some sensors to our board.

Where to go from here

12 thoughts on “OLED Display and Arduino With Power-Save Mode

  1. Very cool! I love the little intro in the beginning about various types of displays. Also, very interesting to read about the various types of fonts and corresponding power consumption characteristics. Also, thanks for the details on libraries used and the code. I’m trying to get my son interested and your projects will be very useful in that way. Thanks!

    Liked by 1 person

  2. Wow, this is amazingly great. I’ll need to share it with my course mates. Plus, I’ll try and subscribe for more updates. Good job, keep up the good work.

    Liked by 1 person

  3. Really impressive, readable, and interesting post. The coding side of things I could handle, but I wish I had the electronics knowledge (at my school, in the 1980s, it was impossible to study both computing and electronics together – I had to take art instead!)
    Really interesting overview of OLED vs LCD, that explained at lot.
    BTW minor typo: ‘There is no scape’ – I assume it should read ‘escape’
    Great article. I thoroughly admire your skills.

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s