NewHaven OLED Display on I2C

I’ve got my NewHaven Character OLED wired up I2C. I I’ve confirmed wiring and voltage (5V) but I can’t get it to even turn on or I see no activity from the OLED… I’ve tried another one I had too… no luck. I’ve modified the sketch here at this link and used their wiring diagram. I’m not sure if I need the 10K Ohm pullup resistors or not.

Newhaven Display Forum Tutorial

anyone have an easier/basic “starter” I2C setup for an OLED Character Display. Any help would be appreciated.

It's always best to explicitly state how you have wired than just

Also the exact type and a link to the datasheets/schematics to hardware devices should be part of the original post.

I found a datasheet here
This display is by default set for MPU-6800 Parallel communication. For serial (SPI not I2C) you'd need to set the jumpers accordingly (see datasheet).

Alternatively I now found this other display which has I2C, but still needs jumpers to select the interface

1 Like

Apologies @ScruffR. I’ll attach my code below which has my pin config for the photon and the display. The datasheet for my display is NHD-0420CW-AG3 I’ve followed the instructions in the datasheet on pg 4-5 and “I think” I’ve interpreted them right … but not totally sure… like /RES… I have that tied high… should it be low and on some posts I’ve seen that you need pull-up resistors on SDA/SCL pins. I’ve seen some folks use 4.7K ohm or 10K ohm tied to 3.3V. Then finally docs say 2.8V ~ 5V… is 3.3V ok or do I need to convert to 2.8V or 5V explicit.

/*
 * from Demo_NHD0420CW-Ax3_I2C.ino
 * 
 * Tutorial sketch for use of character OLED slim display family by Newhaven with Arduino Uno (Brian Beardmore: modifying for Particle Photon), using 
 * only Wire (I2C) library.  Models: NHD0420CW-Ax3, NHD0220CW-Ax3, NHD0216CW-Ax3. Controller: US2066
 * in this example, the display is connected to Photon via I2C interface.
 *
 * Displays on the OLED alternately a 4-line message and a sequence of character "block".
 * This pgm ssumes the use of a 4x20 display; if different, modify the values of the two variables 
 * ROW_N e COLUMN_N.
 * The pgm uses the minimum possible of Photon pins; if you intend to use also /RES line, 
 * the related instructions are already present, it's sufficient to remove the comment markers.
 *
 * The circuit modified by Brian Beardmore for Particle Photon I2C:
 * OLED pin 1 (Vss)          to VSS ground
 * OLED pin 2 (VDD)          to 3.3V
 * OLED pin 3 (REGVDD)       to GND (not using 5V, docs say take to GND)
 * OLED pin 4 (SA0)          to VSS ground should use 0x3C address (to assign I2C address 0x3D, connect to VDD)
 * OLED pin 5 and 6          to VSS ground
 * OLED pin 7 (SCL)          to Photon D1 (SCL); 10K pull-up resistor on OLED pin to 3.3V? Do I need this?
 * OLED pin 8 and 9 (SDAin,SDAout are jumpered) to Photon D0 (SDA); 10K pull-up resistor on each OLED pin 8 and 9 to 3.3V? Do I need this?
 * OLED pin 10 to 15         to VSS ground
 * OLED pin 16 (/RES)        to VDD 3.3V
 * OLED pin 17 (BS0)         to VSS ground  ** I2C config BSO to GND (low)
 * OLED pin 18 (BS1)         to VDD 3.3V    ** I2C condig BS1 to 3.3V (high)
 * OLED pin 19 (BS2)         to Vss ground  ** I2C config Bs2 to GND (low)
 * OLED pin 20 (Vss)         to Vss ground
 *
 * Original example created by Newhaven Display International Inc.
 * Modified and adapted to Arduino Uno 15 Mar 2015 by Pasquale D'Antini
 * Modified 19 May 2015 by Pasquale D'Antini
 * Modified 06 Feb 2017 by Brian Beardmore for Particle Photon
 *
 * This example code is in the public domain.
 */

#include <application.h> // added for Photon support

const byte ROW_N = 4;                 // Number of display rows
const byte COLUMN_N = 20;             // Number of display columns

//const byte RES = 13;                // Arduino's pin assigned to the Reset line (optional, can be always high)

const byte SLAVE2W = 0x3C;            // Display I2C address, in 7-bit form: 0x3C if SA0=LOW, 0x3D if SA0=HIGH

const byte TEXT[4][21] = {"1-Newhaven Display--", 
                          "2-------Test--------", 
                          "3-16/20-Characters--", 
                          "4!@#$%^&*()_+{}[]<>?"};         // Strings to be displayed

byte new_line[4] = {0x80, 0xA0, 0xC0, 0xE0};               // DDRAM address for each line of the display
byte rows = 0x08;                     // Display mode: 1/3 lines or 2/4 lines; default 2/4 (0x08)
byte tx_packet[]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
                                      // Packet to be transmitted (max 20 bytes)
// _______________________________________________________________________________________

void command(byte c)                  // SUBROUTINE: PREPARES THE TRANSMISSION OF A COMMAND
{
   tx_packet[0] = 0x00;               // Control Byte; C0_bit=0, D/C_bit=0 -> following Data Byte contains command
   tx_packet[1] = c;                  // Data Byte: the command to be executed by the display
   send_packet(2);                    // Transmits the two bytes
}
// _______________________________________________________________________________________

void data(byte d)                     // SUBROUTINE: PREPARES THE TRANSMISSION OF A BYTE OF DATA
{
   tx_packet[0] = 0x40;               // Control Byte; C0_bit=0, D/C_bit=1 -> following Data Byte contains data
   tx_packet[1] = d;                  // Data Byte: the character to be displayed
   send_packet(2);                    // Transmits the two bytes
}
// _______________________________________________________________________________________

void send_packet(byte x)              // SUBROUTINE: SEND TO THE DISPLAY THE x BYTES STORED IN tx_packet
{
   byte ix = 0;                       // Bytes index
  
   Wire.beginTransmission(SLAVE2W);   // Begin the transmission via I2C to the display with the given address
   for(ix=0; ix<x; ix++)              // One byte at a time, 
   {
      Wire.write(tx_packet[ix]);      //  queue bytes for transmission
   }
   Wire.endTransmission();            // Transmits the bytes that were queued 
}
// _______________________________________________________________________________________

void output(void)                     // SUBROUTINE: DISPLAYS THE FOUR STRINGS, THEN THE SAME IN REVERSE ORDER
{
   byte r = 0;                        // Row index
   byte c = 0;                        // Column index

   command(0x01);                     // Clears display (and cursor home)
   delay(2);                          // After a clear display, a minimum pause of 1-2 ms is required
   
   for (r=0; r<ROW_N; r++)            // One row at a time,
   {
      command(new_line[r]);           //  moves the cursor to the first column of that line
      for (c=0; c<COLUMN_N; c++)      // One character at a time, 
      {
         data(TEXT[r][c]);            //  displays the correspondig string
      }
   }

   delay(2000);                       // Waits, only for visual effect purpose
   
   for (r=0; r<ROW_N; r++)            // One row at a time,
   {
      command(new_line[r]);           //  moves the cursor to the first column of that line
      for (c=0; c<COLUMN_N; c++)      // One character at a time, 
      {
         data(TEXT[3-r][c]);          //  displays the correspondig string (in reverse order)
      }
   }
}
// _______________________________________________________________________________________

void blocks(void)                     // SUBROUTINE: FILLS THE ENTIRE DISPLAY WITH THE CHARACTER "BLOCK"
{
   byte r = 0;                        // Row index
   byte c = 0;                        // Column index

   command(0x01);                     // Clear display (and cursor home)
   delay(2);                          // After a clear display, a minimum pause of 1-2 ms is required

   for (r=0; r<ROW_N; r++)            // One row at a time,
   {
      command(new_line[r]);           //  moves the cursor to the first column of that line
      for (c=0; c<COLUMN_N; c++)      // One character at a time, 
      {
         data(0xDB);                  //  displays the character 0xDB (block)
         delay(50);                   // Waits, only for visual effect purpose
      }
      delay(500);                     // Waits, only for visual effect purpose
   }
}
// _______________________________________________________________________________________

void setup(void)                      // INITIAL SETUP
{
//   pinMode(RES, OUTPUT);            // Initializes Arduino pin for the Reset line (optional)
//   digitalWrite(RES, HIGH);         // Sets HIGH the Reset line of the display (optional, can be always high)
   delayMicroseconds(200);            // Waits 200 us for stabilization purpose
   Wire.begin();      // Initiate the Wire library and join the I2C bus as a master
   delay(10);         // Waits 10 ms for stabilization purpose
   
   if (ROW_N == 2 || ROW_N == 4)
      rows = 0x08;                    // Display mode: 2/4 lines
   else
      rows = 0x00;                    // Display mode: 1/3 lines
   
   command(0x22 | rows); // Function set: extended command set (RE=1), lines #
   command(0x71);        // Function selection A:
   data(0x5C);           //  enable internal Vdd regulator at 5V I/O mode (def. value) (0x00 for disable, 2.8V I/O)
   command(0x20 | rows); // Function set: fundamental command set (RE=0) (exit from extended command set), lines #
   command(0x08);        // Display ON/OFF control: display off, cursor off, blink off (default values)
   command(0x22 | rows); // Function set: extended command set (RE=1), lines #
   command(0x79);        // OLED characterization: OLED command set enabled (SD=1)
   command(0xD5);        // Set display clock divide ratio/oscillator frequency:
   command(0x70);        //  divide ratio=1, frequency=7 (default values)
   command(0x78);        // OLED characterization: OLED command set disabled (SD=0) (exit from OLED command set)
   
   if (ROW_N > 2)
      command(0x09);  // Extended function set (RE=1): 5-dot font, B/W inverting disabled (def. val.), 3/4 lines
   else
      command(0x08);  // Extended function set (RE=1): 5-dot font, B/W inverting disabled (def. val.), 1/2 lines
   
   command(0x06);        // Entry Mode set - COM/SEG direction: COM0->COM31, SEG99->SEG0 (BDC=1, BDS=0)
   command(0x72);        // Function selection B:
   data(0x0A);           //  ROM/CGRAM selection: ROM C, CGROM=250, CGRAM=6 (ROM=10, OPR=10)
   command(0x79);        // OLED characterization: OLED command set enabled (SD=1)
   command(0xDA);        // Set SEG pins hardware configuration:
   command(0x10);        //  alternative odd/even SEG pin, disable SEG left/right remap (default values)
   command(0xDC);        // Function selection C:
   command(0x00);        //  internal VSL, GPIO input disable
   command(0x81);        // Set contrast control:
   command(0x7F);        //  contrast=127 (default value)
   command(0xD9);        // Set phase length:
   command(0xF1);        //  phase2=15, phase1=1 (default: 0x78)
   command(0xDB);        // Set VCOMH deselect level:
   command(0x40);        //  VCOMH deselect level=1 x Vcc (default: 0x20=0,77 x Vcc)
   command(0x78);        // OLED characterization: OLED command set disabled (SD=0) (exit from OLED command set)
   command(0x20 | rows); // Function set: fundamental command set (RE=0) (exit from extended command set), lines #
   command(0x01);        // Clear display
   delay(2);             // After a clear display, a minimum pause of 1-2 ms is required
   command(0x80);        // Set DDRAM address 0x00 in address counter (cursor home) (default value)
   command(0x0C);        // Display ON/OFF control: display ON, cursor off, blink off
   delay(250);           // Waits 250 ms for stabilization purpose after display on
   
   if (ROW_N == 2)
      new_line[1] = 0xC0;             // DDRAM address for each line of the display (only for 2-line mode)
}
// _______________________________________________________________________________________

void loop(void)                       // MAIN PROGRAM
{  
   output();                          // Execute subroutine "output"
   delay(2000);                       // Waits, only for visual effect purpose
   blocks();                          // Execute subroutine "blocks"
   delay(2000);                       // Waits, only for visual effect purpose
}

I’d definetly try with the 10k pull-ups.

Not sure how the display copes with 3.3V instead of 2.8V, but you could always go for 5V via Vin (also pullups to Vin).

I think /RES can be left N/C since it should have a pull-up on board.
If you want to reset via code, you could use this code with 5V too

const int _res = D6; // any pin other than A3 or DAC (not 5V tolerant)
void setup() {
  pinMode(_res, INPUT); // set for hi-Z (=default)
  ...
}

void resetDisplay() {
  pinMode(_res, OUTPUT);
  digitalWrite(_res, LOW);
  delay(100);
  pinMode(_res, INPUT); // set for hi-Z (=default)
}

You could also use the I2C scanner to first check if the display is found at all.

1 Like

ok, got home tonight and starred at this thing until I’m blue in the face. I can’t see it and I’ve tried my setup on two different Photon’s and two different NHD-0420CW OLED displays. @ScruffR, I got the I2C scanner, thanks for that tip! and it’s showing it cannot find the I2C display anywhere… should find it on 0x3C… (…I also powered Pin4 to find it on 0x3D, no luck) So I think I have a wiring problem or hardware problem… but really 2 displays are bad out of the box or I somehow fried them???.. I’m not thinking so, so starring at the wiring again…and these goofy pullup resistors… thought about moving them closer to the Photon Pins, I may try that.

my version of the I2C Scanner code…

// --------------------------------------
// i2c_scanner
//
// Version 1
//    This program (or code that looks like it)
//    can be found in many places.
//    For example on the Arduino.cc forum.
//    The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
//     Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26  2013
//    V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
//    by Arduino.cc user Krodal.
//    Changes by louarnold removed.
//    Scanning addresses changed from 0...127 to 1...119,
//    according to the i2c scanner by Nick Gammon
//    http://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
//    As version 4, but address scans now to 127.
//    A sensor seems to use address 120.
// Version 6, November 27, 2015.
//    Added waiting for the Leonardo serial communication.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//
 
#include "application.h"
 
 
void setup()
{
  Wire.begin();
 
  Serial.begin(9600);
  while (!Serial);             // Leonardo: wait for serial monitor
  Serial.println("\nI2C Scanner");
}
 
 
void loop()
{
  byte error, address;
  int nDevices;
 
  Serial.println("Scanning...");
 
  nDevices = 0;
  for(address = 1; address < 127; address++ )
  {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
 
    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
 
      nDevices++;
    }
    else if (error==4)
    {
      Serial.print("Unknow error at address 0x");
      if (address<16)
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
 
  delay(5000);           // wait 5 seconds for next scan
}

Hey @bbeardmore, I was having the same problem, so I gave the datasheet more scrutiny and looked closely at the configuration for this example code. It turns out you need to connect pin 16 to either VSS or a software reset pin. In your image, it looks like you have 16 connected to VDD. I connected it to VSS and the displayed kicked on as expected.

1 Like

@aholmes sorry so late on the reply! YES! this was exactly my problem and I fixed t and am off and running now!

@aholmes, @ScruffR and @bbeardmore,

Thank you for this thread I was struggling with getting my display to work and your efforts got me back on track.

I am running the display at 3.3V with no issues using the i2c interface.

Some clarification for the non-EEs among us regarding Pin 16:

  • There is no internal pull up resistor so it cannot be left floating
  • This pin resets the display when it is brought LOW and therefore, I tied it HIGH. I find the Vdd and Vss terminology confusing.

Now to take the example code and make something neater for inclusion in my project. Did anyone end us creating a library for this display?

Thanks,

Chip

2 Likes

Very cool seeing my effort help others. Glad you got things going!

@aholmes abd @bbeardmore,

If it helps, I have made a simple library and published it to the Particle Library system

Here is the Github repo

As I am new to this whole library writing thing so any suggestions would be greatly appreciated!
In particular, I struggled with how not to “hard code” things like the rows, columns and i2c address. I implemented a simple function to replace a row but perhaps more granular functions would be better. Again, open to all your input.

@ScruffR, I know that you often have some good points that can improve my code.

Thanks,

Chip

2 Likes

Maybe you can find some inspiration here. http://developer.wildernesslabs.co/Netduino/Input_Output/Digital/SPI/Writing/

Their library derives from this. http://geekswithblogs.net/kobush/archive/2010/09/05/netmf_liquid_crystal.aspx

All depends on how far you want to abstract things though.

1 Like

:+1:

I'll post my findings/suggestions in that repo as "issue" report.

1 Like

Thank you for taking a look. I really appreciate it. One of my goals for this year is to get better at writing libraries.

I have started to look at addressing your issues and some will take me time to wrap my head around. Still, your pointing me in the right direction is exactly what I need.

I have added a couple more functions based on the examples provided by @aholmes above, namely a command to clear the display and another to let me write a character at a specific location on the screen.

It seems that the library in its current state has been downloaded over 90 times so, I don’t want to screw this up but here is what I think I need to do:

  1. Rename the library as you suggest New-Haven-Disaply-4x20-I2C
  2. Update the library with the local changes I have made
  3. Push these updates to the public library.

I have looked at the Library tutorial - https://docs.particle.io/tutorials/device-os/libraries/
and the CLI commands for “Library”

Commands:
  add      Add a library to the current project.
  create   Create a new library in the specified or current directory
  copy     Copy a library to the current project
  list     List libraries available
  migrate  Migrate a local library from v1 to v2 format
  search   Search available libraries
  upload   Uploads a private version of a library.
  publish  Publish a private library, making it public
  view     View details about a library

But, I don’t see how I am to do this as Update, Rename or Delete are not available commands. Is there something on lifecycle management of libraries you could point me to?

Thanks,

Chip

You cannot rename or delete a once uploaded library as it could already be part of a project which would then break.
However, in order to update a library you'd only need to increment the version number in the library.properties file and re-upload the library.

There in no harm in uploading an extended version with a new name while letting the old library live on (maybe with a note in the comment referring to its successor).

If you have a private library or a library that is not part of any project you can ask for manual removal of the library via a support ticket.

1 Like

@all,

FYI, I have updated this library to be specific to the i2c 4x20- display. This version initializes the display much faster. I got some help from NHD and eliminated some instructions that were not needed.

I have published the library in the Particle library system as: New-Haven-Display-4x20-i2c

You can also see the Github repo here:https://github.com/chipmc/New-Haven-Display

Hope this is helpful.

Chip

1 Like