Controlling an OLED character display with Arduino

Posted on Dec 29, 2011 in Arduino, C++, Electronics, Programming, UberFridge | 8 comments

For my UberFridge project I ordered a 4×20 OLED character display from Newhaven, the NHD-0420DZW-AY5-ND. It is a great display: it is much brighter and much better readable then a normal LCD. At only 25 dollars, it was an easy choice.

The UberFridge OLED character display

The OLED display displays the mode of operation, current temperature, temperature setting and status.

The display was supposed to be HD44780 compatible, but it took me ages to get it to initialize properly. The folks at Newhaven couldn’t even figure out what the correct 4-bit initialization sequence had to be for Arduino. Finally I found a sequence that worked and modified the Arduino LiquidCrystal library to work with the OLED display. I also added functionality to read the busy pin of the display and to read the contents of the display.

I have put the code I use to drive the OLED display online at Google code. You can find it here.

You can put it in your Arduino library directory and include it in your projects. I removed 8-bit mode from the library, changed the initialization sequence, changed the waitBusy() function, added a readChar() function and did some other minor changes.

I will not post the whole code in this article but I will highlight some pieces I think are worth mentioning.

OLED Initialization sequence

The modified initialization sequence looks like this:

  delayMicroseconds(100000);
  write4bits(0x03);
  delayMicroseconds(100000);
  write4bits(0x02);
  delayMicroseconds(10000);
  write4bits(0x02);
  delayMicroseconds(10000);
  write4bits(0x08);
 
  delayMicroseconds(10000);
  command(0x08);        // Display off
  delayMicroseconds(10000);
  command(0x01);        // display clear
  delayMicroseconds(10000);
  command(0x06);        // Entry Mode Set:
  delayMicroseconds(10000);
  command(0x02);        // Home
  delayMicroseconds(10000);
  command(0x0C);        // display on/ cursor on/ cursor blink
  delayMicroseconds(10000);

This is almost the same as the initialization sequence in the latest Newhaven datasheet (they have updated it after I contacted them), but with the addition of the first line. This sets the display back to 8-bit mode before setting it to 4-bit. The initialization sequence in the datasheet starts with power-up. The display starts in 8-bit mode by default, so the sequence works fine then. But when the Arduino is reset without resetting the display, the display is already in 4-bit mode and the initialization sequence puts the display in WTF mode:

weird characters on the oled display

Faulty initialization causes the display to do all kinds of funky things

With the initialization code above the display initializes properly every time. It will only go weird after reprogramming the Arduino sometimes, so you will still have to power cycle it sometimes after uploading new firmware. Very rarely the display will go into a state where only the first and third line are active. If someone knows what might be causing this, please tell me.

WaitBusy() function

I have added the following waitBusy() function to the library which reads the busy pin of the display instead of waiting for 5 ms:

void OLEDFourBit::waitBusy(void) {
  //delayMicroseconds(5000);
  unsigned char busy = 1;
  pinMode(_busy_pin, INPUT);
  digitalWrite(_rs_pin, LOW);
  digitalWrite(_rw_pin, HIGH);
  do{
        digitalWrite(_enable_pin, LOW);
        digitalWrite(_enable_pin, HIGH);
        delayMicroseconds(10);
        busy = digitalRead(_busy_pin);
        digitalWrite(_enable_pin, LOW);
 
        pulseEnable();          // get remaining 4 bits, which are not used.
 
  }while(busy);
 
  pinMode(_busy_pin, OUTPUT);
  digitalWrite(_rw_pin, LOW);
}

This function polls the busy pin of the display instead of always waiting the worst case busy time.

ReadChar() function

In my web interface I have a live version of the real OLED display that is updated every 10 seconds. The display has a function to read out its contents, but it was not implemented in the LiquidCrystal library. The following function returns the character at the current display position:

char OLEDFourBit::readChar(void){
  char value=0x00;
  for (int i = 0; i < 4; i++) {
    pinMode(_data_pins[i], INPUT);
  }
  digitalWrite(_rs_pin, HIGH);
  digitalWrite(_rw_pin, HIGH);
  pulseEnable();
  delayMicroseconds(600);
  for (int i = 0; i < 4; i++) {
    value = value | (digitalRead(_data_pins[i]) << (i+4));
  }
  pulseEnable();
  delayMicroseconds(600);
  for (int i = 0; i < 4; i++) {
    value = value | (digitalRead(_data_pins[i]) << (i));
  }
  return value;
}

I call this function to read the display content into an array, which can be echoed to the serial port when the router requests it. The address space of the display continues from line 0 to line 2, so I read the display contents in the order 0-2-1-3. Setting the cursor at line 2 or 3 caused some characters to read incorrectly on these lines. I have no idea why, but avoiding it works well.

void lcdReadContents  (void){
  lcd.setCursor(0,0);
  for(int i =0;i<20;i++){
      lcdText[0][i] = lcd.readChar();
  }
  for(int i =0;i<20;i++){
      lcdText[2][i] = lcd.readChar();
  }
  lcd.setCursor(0,1);
  for(int i =0;i<20;i++){
      lcdText[1][i] = lcd.readChar();
  }
  for(int i =0;i<20;i++){
      lcdText[3][i] = lcd.readChar();
  }
}

8 Comments

  1. Thanks for the great work! i am in Australia and trying to put all the pieces together, i have been able to procure everything bar this screen fairly cheap. The only places i can buy this screen from are in the US and the cheapest ive seen is $30AUD plus shipping to Australia is another $30AUD…. costs more than all other parts put together. Is there any chance of getting a write up or some info on using a more generic (cheaper) LCD display?

    • It’s not hard too change it for a different screen. The code I use is adapted from the LiquidCrystal Arduino library. I had to change a few things to get this display to cooperate, but using the original library should work with most other screens.

      • Would the code need to be adjusted for a different size screen? I assume i would need a 4×20 LCD screen? As i have never used arduino before would you be able to recommend any common and easy to use screens that would work without many changes. p.s thanks for the quick response.

        • I would use a 4×20 screen, because I am using 4 lines to display data. Any hd44780 compatible LCD will do.

  2. There are a few modifications to the OLEDFourBit library that’ll need to be made it work with Arduino 1.0 For anyone having trouble getting it to compile, the changes I made are noted below.

    OLEDFourBit.h
    change: virtual void write(uint8_t);
    to: virtual size_t write(uint8_t);

    OLEDFourBit.cpp
    change: #include "WProgram.h"
    to: #include "arduino.h"

    change: inline void OLEDFourBit::write(uint8_t value) {
    to: inline size_t OLEDFourBit::write(uint8_t value) {

    • Thanks. I will not update the old UberFridge repository, but I will release the first BrewPi code with these updates.

    • Thanks for the code update, i am now getting a whole bunch of errors from different files, are you able to assist?


      fermentationMonitor.cpp.o: In function `menuChoice(int)':
      C:\Users\medoix\AppData\Local\Temp\build7004707615862087386.tmp/fermentationMonitor.cpp:794: undefined reference to `OLEDFourBit::setCursor(unsigned char, unsigned char)'
      C:\Users\medoix\AppData\Local\Temp\build7004707615862087386.tmp/fermentationMonitor.cpp:804: undefined reference to `OLEDFourBit::setCursor(unsigned char, unsigned char)'

      ** clipped the remainder, just more undefined reference errors **

      • This is a linker error: the header file (OLEDFourBit.h) tells the compiler that those functions exists, but the linker cannot find them. The reference is undefined. Add OLEDFourBit.cpp to your project and the linker should be able to find the references.

Trackbacks/Pingbacks

  1. Hacking an Arduino Library to support a NewHaven OLED with an MCP23008 port expander | BuffaloDac - [...] the standard Arduino LiquidCrystal library to make it compatible with the OLED timing parameters: http://www.elcojacobs.com/controlling-an-oled-character-display-with-arduino/.  I swapped his library…

Leave a Reply

Your email address will not be published. Required fields are marked *