Kinda Small Displays - Part 1

About: We are passionate programmers and embedded devices developers from Deggendorf, Germany. The AZ-Delivery Vertriebs GmbH, was established in 2016 with the purpose to offer our customers high-quality microelect...

This is first part in series of Instructables "Introducing Displays for Arduino and Raspberry Pi". There will be three parts, where first will be about OLED displays, second will be about LCD displays and third will be about 8x8 LED matrix display and seven segment display.

What is a display? Word “display” means to present or to view, so display is device which presents information to a viewer. There are many types of displays. In history fist displays were like posters, people wrote or painted texts, decrees or paintings. In modern days when we started using electronics devices, first displays were photographs, than old black and white tvs, color tvs etc. Old tvs technology were based on monochrome CRT - Cathode Ray Tube (we won’t get into details because this technology and technologies based on it is not much in use these days). Some displays of that time were based on flip-flap/disc effect.

Then came an LEDs, Light Emitting Diode, we cover LEDs in our last Instructable. These displays are LEDs stacked into arrays, and by turning on or off some LEDs in array made characters, letters or images.

Then came LCD technology (Liquid Crystal Display). These displays have liquid crystals in them which orientation is random, and when we let current flow through it they change orientation into some direction, blocking or reflecting light in that part of display. Most likely that you are now using one based on LCD technology.

In 2000s came OLED and AMOLED displays, based on OLEDs - Organic Light Emitting Diodes, and AMOLEDs- Active-Matrix Organic Light Emitting Diodes.

This is first part in series, and here we will use three displays based on OLED technology:

We will also need:

These displays are small about an inch in diagonal, with high contrast and high resolution. This enables high readability even though they are very small. These displays do not have backlight, only pixels in use are light up, so that display power consumption depends on amount of pixels light up. Each pixel is turned on/off by the driver chip SSD1306. With these displays we can establish communication in two ways I2C or SPI. We AZ-Delivery ship OLED displays only with I2C communication.

OLED itself need 3,3V for working and also driver chip needs 3.3V for communication, but there is onboard 3.3V regulator, so we can use these displays with 5V logic and power supply.

First two OLED displays listed in parts list have 128x64 pixels, (128 horizontally and 64 vertically) and third have 128x32 pixels. No matter which one we use, it is just matter of calling special constructor at the beginning of sketch for special display, everything else is almost the same.

Step 1: I2C

As mentioned, we will use I2C communication protocol to control OLED displays. I2C is IIC - Inter Integrated Circuit. It is a synchronous serial computer bus. Synchronous means that everything happens on specific clock cycle. Serial means that data is sent bit by bit, serialy. This all means that for this communication we only need two wires, one called SCL - Clock Line and SDA - Data Line. Both lines operate in open drain mode, which means that device which uses I2C can drive SDA or SCL line low, but cannot drive it high. That is why we need two pull up resistors with same resistance connected to both lines. For Arduino we don’t need to bother ourselves with them. We just have to connect SDA of Arduino (pin A4) to SDA of display, and SCL of Arduino (pin A5) to SCL of display. And of course to program Arduino to do heavy work of controlling display.

I2C protocol offer several different running speeds, but for now (for this Instructable) we don’t need to bother ourselves with it.

Every device connected to these I2C protocol lines has its own address. This is needed because master device (our microcontroller on Arduino Uno board) has to somehow know with which slave device (in this case, display) it communicate. For every of these OLED displays address is the same, 0x3D, and you have option for resoldering one resistor on board of display for another address 0x3C (this is only possible for 0.96’’ OLED display that we ship). But our recommendation is not to use this. If you wish to use multiple displays with one Arduino Uno, you should use I2C multiplexer device, or bigger display. Using multiple displays as one will be one of our future Instructables.

Step 2: Libraries

We will show you how to use two out of many libraries for these displays. First is u8g2 and second is Adafruit SSD1306 library. It does not matter which one you use both do the job.

First u8g2, to download it go to Arduino IDE, Tools > Manage Libraries. A new window will open, type u8g2 into search box, and click install for this library. And that is it. Now we will go to File > Examples, and scroll all way down, and there will be U8g2 set of example sketches. We will use File > Examples > U8g2 > full_buffer > GraphicsTest but we will modify it so extensively because it is complex and very hard to understand it as it is. In next steps we will rewrite code for testing all functions and scrolling image or text on displays.

Second library is Adafruit SSD1306 and to download it go to Tools > Manage Libraries. When new window opens, type in search box OLED and install this library. When we go to File > Examples and scroll all way down, we will see Adafruit SSD1306 set of 4 sketches, two for display of 128x32 pixels (one for SPI and one for I2C), and two for display of 128x64 pixels (again one for SPI and one for I2C). We won't use this library, but you can test it on our displays:

File > Examples > Adafruit SSD1206 > ssd1306_128x32_i2c for 0.91'' display,

File > Examples > Adafruit SSD1206 > ssd1306_128x64_i2c for 0.96'' or 1.3'' displays.

Step 3: Testing Example

For this step we will use the smallest display - 0.91’’ OLED I2C display. But you can use any of three listed in part list. The only difference is in constructor call on the beginning of the sketch and the height of display in pixels, everything else is the same.

Connect everything like on diagram.

In next sketch we will rewrite functions for drawing frames, boxes, discs, circles, text orientation, lines, triangles, and special characters. In loop function we call all of these functions just for example. Here is the sketch (just uncomment libraries names, because Instructable delete that when posted uncommented):

#include // <Arduino.h>
#include // <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI #include // <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include // <Wire.h> #endif

// Uncomment constructor line for display you are using // First for 0.91’’, second for 0.96’’ and third for 1.3’’ display U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

const char COPYRIGHT_SYMBOL[] = { 0xa9, '\0' };

void u8g2_prepare() { u8g2.setFont(u8g2_font_6x10_tf); u8g2.setFontRefHeightExtendedText(); u8g2.setDrawColor(1); u8g2.setFontPosTop(); u8g2.setFontDirection(0); }

void u8g2_box_frame() { u8g2.drawStr(0, 0, "drawBox"); u8g2.drawBox(5, 10, 20, 10); u8g2.drawStr(60, 0, "drawFrame"); u8g2.drawFrame(65, 10, 20, 10); }

void u8g2_r_frame_box() { u8g2.drawStr(0, 0, "drawRFrame"); u8g2.drawRFrame(5, 10, 40, 15, 3); u8g2.drawStr(70, 0, "drawRBox"); u8g2.drawRBox(70, 10, 25, 15, 3); }

void u8g2_disc_circle() { u8g2.drawStr(0, 0, "drawDisc"); u8g2.drawDisc(10, 18, 9); u8g2.drawDisc(30, 16, 7); u8g2.drawStr(60, 0, "drawCircle"); u8g2.drawCircle(70, 18, 9); u8g2.drawCircle(90, 16, 7); }

void u8g2_string_orientation() { u8g2.setFontDirection(0); u8g2.drawStr(5, 15, "0"); u8g2.setFontDirection(3); u8g2.drawStr(40, 25, "90"); u8g2.setFontDirection(2); u8g2.drawStr(75, 15, "180"); u8g2.setFontDirection(1); u8g2.drawStr(100, 10, "270"); }

void u8g2_line() { u8g2.drawStr( 0, 0, "drawLine"); u8g2.drawLine(7, 10, 40, 32); u8g2.drawLine(14, 10, 60, 32); u8g2.drawLine(28, 10, 80, 32); u8g2.drawLine(35, 10, 100, 32); }

void u8g2_triangle() { u8g2.drawStr( 0, 0, "drawTriangle"); u8g2.drawTriangle(14, 7, 45, 30, 10, 32); }

void u8g2_unicode() { u8g2.drawStr(0, 0, "Unicode"); u8g2.setFont(u8g2_font_unifont_t_symbols); u8g2.setFontPosTop(); u8g2.setFontDirection(0); u8g2.drawUTF8(10, 15, "☀"); u8g2.drawUTF8(30, 15, "☁"); u8g2.drawUTF8(50, 15, "☂"); u8g2.drawUTF8(70, 15, "☔"); u8g2.drawUTF8(95, 15, COPYRIGHT_SYMBOL); //COPYRIGHT SIMBOL u8g2.drawUTF8(115, 15, "\xb0"); // DEGREE SYMBOL }

void setup(void) { u8g2.begin(); u8g2_prepare(); }

void loop(void) { u8g2.clearBuffer(); u8g2_prepare(); u8g2_box_frame(); u8g2.sendBuffer(); delay(1500);

u8g2.clearBuffer(); u8g2_disc_circle(); u8g2.sendBuffer(); delay(1500);

u8g2.clearBuffer(); u8g2_r_frame_box(); u8g2.sendBuffer(); delay(1500); u8g2.clearBuffer(); u8g2_prepare(); u8g2_string_orientation(); u8g2.sendBuffer(); delay(1500); u8g2.clearBuffer(); u8g2_line(); u8g2.sendBuffer(); delay(1500); u8g2.clearBuffer(); u8g2_triangle(); u8g2.sendBuffer(); delay(1500); u8g2.clearBuffer(); u8g2_prepare(); u8g2_unicode(); u8g2.sendBuffer(); delay(1500); }

Now let’s explain functions that we used. First in u8g2_prepare() function, in which we used several u8g2 library functions.

- setFont() function as its name says is for setting font for characters we are using, in this case argument for this function si u8g2_font_6x10_tf but you can choose any you like from here.

- setFontRefHeightExtendedText() this function is for drawing characters, and here is full explanation.

- setDrawColor(), if we use argument - 1 only character will be light up, if we use argument - 0, pixels for every letter will be inverted (meaning that space around character will be light up, but character won’t). And there is third argument - 2, but result does not differ from argument - 0.

- setFontPosTop() has several different functions, setFontPosBaseline(), setFontPosCenter() and setFontPosBottom(), meaning that these functions change position of character in character line.

- setFontDirection() is for changing angle of character line, it has four different argument values, 0 - 0° , 1 - 90° , 2 - 180° , 3 - 270° . We use this in u8g2_string_orientation() function.

To print text on the display we use drawStr() function. It has three arguments, first is X position of text, second is Y position, and third is actual string. Before using this function we should use u8g2_prepare() function in order to set up fonts for displaying text.

To display a box, or a frame, or a circle, or a disc, or a line, or a triangle we use drawBox(), drawFrame(), drawCircle(), drawDisc(), drawLine() and drawTriangle() functions respectively. For all of this functions first argument is X position, and second is Y position of object. Other arguments differ from function to function. For box and frame, third and fourth are width and height, for circle and disc third argument is radius. For line first two arguments are X and Y position of first dot of line, and second two arguments are X and Y position for last dot of the line. Triangle has three dots, so there are 6 arguments, first two for first dot, second two for second dot, and third two for third dot of the triangle.

There is function for displaying box or frame with radius on the corners. drawRFrame() and drawRBox(), both functions has fifth argument for radius.

There is function for displaying unicode characters drawUTF8(). This function has three arguments, first two for X and Y position of character, and third is for the character. There are three ways to display these characters. First is to copy and paste them in sketch. Second is to create symbol variable which is char array with two values, first if hexadecimal unicode number for special character, and second value is escape character, like in our example COPYRIGHT_SYMBOL variable. And third way is to use hexadecimal number in a string like “\xb0” (for degree symbol) in our example.

You can use this for another two displays, but you have to comment constructor for 128x32, and uncomment first constructor with 128x64 for 0.96’’ display, and second constructor with 128x64 for 1.3’’ display, at the beginning of the sketch.

There is also a function for displaying a bitmap image and we will cover this in next step.

Step 4: Displaying X Bitmap Image

For this step we will use 0.96'' OLED - I2C display.

Now you must be wondering what is X bitmap or short .xbm image? It is bitmap image and it is used to store cursor and icon image bitmaps. How can you make one .xbm image. There are many tools, but we will explain one using GIMP program.

First you have to create image, of find some online. Second you must posterize it into 2 colored image (preferably black and white). Than you have to scale it to fit display width or height. Than you have to export it as .xbm. At the end, you have to open exported .xbm file into some text editor (we will use Sublime Text, but you can use notepad or any similar) and copy that text into our sketch. Now that is all nice, so let's go through steps.

We will use this image from our site. We will open it with GIMP.

Because this is already two colored image, we will turn red into black and transparent background to white.

Than go to Image > Scale and scale an image by changing width value to 128 and hight will automatically be scaled. If after this, height value of your image is more that 32, than you have to change height to 32 FIRST, and than width will be automatically changed to less than 128.

After this you have to edit image, to have only pixels with black color for image, and white for background, not with this semi transparent color. Because if you leave it like that, it won't look good on small display.

After that export it as image.xbm by pressing ctrl + shif + E.

Than open .xbm file with text editor.

Change line three like on this image and copy all text from this file into next sketch like this:

#include // <Arduino.h>
#include // <U8g2lib.h

#ifdef U8X8_HAVE_HW_SPI #include // <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include // <Wire.h> #endif

// Uncomment constructor line for display you are using // First for 0.91’’, second for 0.96’’ and third for 1.3’’ display // U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

void u8g2_prepare() { u8g2.setFont(u8g2_font_open_iconic_all_4x_t); u8g2.setFontRefHeightExtendedText(); u8g2.setDrawColor(1); u8g2.setFontPosTop(); u8g2.setFontDirection(0); }

#define image_width 128 #define image_height 21 static const unsigned char image_bits[] U8X8_PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0xfc, 0x7f, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x18, 0x00, 0x00, 0x0c, 0x60, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x0c, 0xc0, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x0c, 0xc0, 0xf0, 0x1f, 0x06, 0x63, 0x80, 0xf1, 0x1f, 0xfc, 0x33, 0xc0, 0x03, 0x18, 0x00, 0x00, 0x0c, 0xc0, 0xf8, 0x3f, 0x06, 0x63, 0xc0, 0xf9, 0x3f, 0xfe, 0x33, 0xc0, 0x03, 0x18, 0x00, 0x00, 0x0c, 0xc0, 0x18, 0x30, 0x06, 0x63, 0xc0, 0x18, 0x30, 0x06, 0x30, 0xc0, 0xff, 0xff, 0xdf, 0xff, 0x0c, 0xc0, 0x18, 0x30, 0x06, 0x63, 0xe0, 0x18, 0x30, 0x06, 0x30, 0xc0, 0xff, 0xff, 0xdf, 0xff, 0x0c, 0xc0, 0x98, 0x3f, 0x06, 0x63, 0x60, 0x98, 0x3f, 0x06, 0x30, 0xc0, 0x03, 0x18, 0x0c, 0x00, 0x0c, 0xc0, 0x98, 0x1f, 0x06, 0x63, 0x70, 0x98, 0x1f, 0x06, 0x30, 0xc0, 0x03, 0x18, 0x06, 0x00, 0x0c, 0xc0, 0x18, 0x00, 0x06, 0x63, 0x38, 0x18, 0x00, 0x06, 0x30, 0xc0, 0x03, 0x18, 0x03, 0x00, 0x0c, 0xe0, 0x18, 0x00, 0x06, 0x63, 0x1c, 0x18, 0x00, 0x06, 0x30, 0xc0, 0x00, 0x80, 0x01, 0x00, 0xfc, 0x7f, 0xf8, 0x07, 0x1e, 0xe3, 0x0f, 0xf8, 0x07, 0x06, 0xf0, 0xcf, 0x00, 0xc0, 0x00, 0x00, 0xfc, 0x3f, 0xf0, 0x07, 0x1c, 0xe3, 0x07, 0xf0, 0x07, 0x06, 0xe0, 0xcf, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f };

void u8g2_bitmap() { u8g2.drawXBMP(0, 22, image_width, image_height, image_bits); }

void setup(void) { u8g2.begin(); u8g2_prepare(); u8g2.clearBuffer(); u8g2_bitmap(); u8g2.sendBuffer(); }

void loop(void) { }

Upload skectch above and on display you could see magic.

Step 5: Scrolling Image on Display

For this step we will use 0.91'' OLED - I2C display again, and sketch from last step.

The three things that we need to change in this sketch.

First, we need to declare new variable uint8_t scroll after constructor, and put in it 128 value (width of display),

uint8_t scroll = 128;

Than change u8g2_bitmap() function to:

void u8g2_bitmap() { u8g2.drawXBMP(scroll, 5, image_width, image_height, image_bits); scroll--; }

And third thing is to move these three lines of code from setup() function into loop() function:

u8g2.clearBuffer();
u8g2_bitmap(); u8g2.sendBuffer();

Upload it into Arduino uno and watch magic happen.

To explain it a little bit. In every loop cucle we change X position in drawXBMP() function by changing scroll variable. We can change direction of animation, by setting scroll variable to 0 at the beginning, and increment it in every cucle scroll++; We also can change animation to scroll in Y direction by changing

u8g2.drawXBMP(scroll, 5, image_width, image_height, image_bits);

to

u8g2.drawXBMP(0, scroll, image_width, image_height, image_bits);

and also limits for scroll variable.

Step 6: Battery Charging Animation

For this step we will use 1.3’’ OLED - I2C display. Here is the sketch:

#include // <Arduino.h>
#include // <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI #include // <SPI.h> #endif #ifdef U8X8_HAVE_HW_I2C #include // <Wire.h> #endif

// Uncomment constructor line for display you are using // U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE);

void u8g2_prepare() {

u8g2.setFont(u8g2_font_open_iconic_all_4x_t); u8g2.setFontRefHeightExtendedText(); u8g2.setDrawColor(1); u8g2.setFontPosTop(); u8g2.setFontDirection(0); }

void empty_battery() { u8g2.drawRFrame(10, 10, 108, 44, 3); u8g2.drawRFrame(11, 11, 106, 42, 3); u8g2.drawRFrame(116, 21, 5, 22, 2); u8g2.drawBox(118, 22, 3, 20); }

void three_empty_battery() { u8g2.drawRFrame(10, 10, 108, 44, 3); u8g2.drawRFrame(11, 11, 106, 42, 3); u8g2.drawRFrame(116, 21, 5, 22, 2); u8g2.drawBox(118, 22, 3, 20);

u8g2.drawBox(16, 14, 30, 36); }

void six_empty_battery() { u8g2.drawRFrame(10, 10, 108, 44, 3); u8g2.drawRFrame(11, 11, 106, 42, 3); u8g2.drawRFrame(116, 21, 5, 22, 2); u8g2.drawBox(118, 22, 3, 20);

u8g2.drawBox(16, 14, 30, 36); u8g2.drawBox(49, 14, 30, 36); }

void full_battery() { u8g2.drawRFrame(10, 10, 108, 44, 3); u8g2.drawRFrame(11, 11, 106, 42, 3); u8g2.drawRFrame(116, 21, 5, 22, 2); u8g2.drawBox(118, 22, 3, 20);

u8g2.drawBox(16, 14, 30, 36); u8g2.drawBox(49, 14, 30, 36); u8g2.drawBox(82, 14, 30, 36); }

void setup(void) { u8g2.begin(); u8g2_prepare(); }

void loop(void) { u8g2.clearBuffer(); empty_battery(); u8g2.sendBuffer();

delay(1000);

u8g2.clearBuffer(); three_empty_battery(); u8g2.sendBuffer();

delay(1500);

u8g2.clearBuffer(); six_empty_battery(); u8g2.sendBuffer();

delay(1500);

u8g2.clearBuffer(); full_battery(); u8g2.sendBuffer(); delay(1500); }

We won't explain this sketch because we already used everything in previous sketches.

Nothing new, just magic in animation ;-)

This is all for this Instructable. If you have any questions, feel free to ask.

You can reach us under info@az-delivery.com at any time and we will be glad to help you, or visit us under www.az-delivery.com

Share

Recommendations

  • Build a Tool Contest

    Build a Tool Contest
  • Paper Contest

    Paper Contest
  • Epilog X Contest

    Epilog X Contest

2 Discussions

0
None
curiosity36

15 days ago

It can't get any more timely than this. I'm currently working on integrating a display into my ESP32 iRadio project. I think everything I need to know is right here. Thank you for writing and posting this very educational Instructable.

3
None
Alex in NZ

19 days ago

This is a real neat introduction. Thank you for writing and sharing it :-)