How to use the Heltec OLED display on the ESP32

Heltec is a Chinese company that makes all kinds of SoC (system on a chip) devices. Their ESP32-based SoCs appear to be solid quality, and they offer a few variants, including a LoRa WLAN chip for long range connectivity on a narrow band.

I bought a handful of the Heltec OLED WiFi kit chips so as to have a little display without the hassle of attaching a two-line LCD on the device, to see what’s happening in the system. I have a previous piece on how to use the LCD displays, in case you are interested (it features the Arduino but it works just as well on ESP32).

The installation of the Heltec brand chips is straightforward, because after installing the general support for ESP32, you can add Heltec as a library to the Arduino IDE. This generates the file Heltec.h into the Libraries folder, and that brings you all the functionality you need.

The code you see in this blog post is available on GitHub.

OLED features

OLED stands for Organic LED, and it is some sort of biomechanical sorcery or magick that brings up imaged on the 0.96″ screen. It is bluish in color. The resolution is 128 x 96 pixels, so this isn’t exactly HD, but for the use I have for this it is absolutely adequate. You can display strings or draw lines, boxes, circles, or images, if you work the images into the right size, and then export them into a file format called XBM, which is a text file actually. This is a nice tutorial on using XBM. Essentially your image pixels are turned into zeros and ones, put into an array, and displayed by the Heltec functionality as images. The images in XBM are contained in a header file called images.h.

Here’s a short video on how the OLED works.

OLED with custom images, booting

The Haaga-Helia logo on this boot sequence is created by taking the actual logo, scaling it down to 128 x 96 pixels, and then exporting it to a format like this:

In XBM code, it looks like this:

define hhlogo_width 128
define hhlogo_height 64
const unsigned char hhlogo_bits[] PROGMEM = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

[I am cutting a bunch of these lines here, because as you can see, it merely defines 0xFF of the pixel is off and something else like 0x3C for pixels that are on.]

0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, };

When these bits are rendered in the device, you see the picture as in the image above.

The device is actually rather handy in drawing not only pixel images, but lines, boxes, and curves too. When you install the library, you get some sample files, and one of them shows all these features, namely SSD1306DrawingDemo.

The draw demo program

Let’s look at how the device draws things. In the Setup function, the device must be enabled:

Heltec.begin(true /DisplayEnable Enable/, false /LoRa Disable/, true /Serial Enable/);
Heltec.display->setContrast(255);

After that, it can be asked to do all kinds of operations, all of which must be followed by

Heltec.display->display();

In effect, every operation is written into a buffer an when this line is issued, it actually displays the values. It was unclear to me at first that this display operation must be repeated often, but when you figure out that this is the actual show command, it’s easy enough. Its counterpart is the clear operation:

 Heltec.display->clear();

This does not for some reason erase every pixel every time, so sometimes you need to draw a black box in the entire screen to make sure everything goes away. This code draws an alternating black / white set of rectangles:

void fillRect(void) {
uint8_t color = 1;
for (int16_t i = 0; i < DISPLAY_HEIGHT / 2; i += 3) { 
// alternate colors
Heltec.display->setColor((color % 2 == 0) ? BLACK : WHITE); 
Heltec.display->fillRect(i, i, DISPLAY_WIDTH - i * 2, DISPLAY_HEIGHT - i * 2);
Heltec.display->display();
delay(10);
color++;}
// Reset back to WHITE
Heltec.display->setColor(WHITE);
}

So, in essence, it is very easy to work with this display. The main commands you will use are

Heltec.display->drawLine(start-x, start-y, end-x, end-y);
Heltec.display->drawRect(topcorner-x, topcorner-y, 
bottomcorner-x, bottomcorner-y); //open rectangle
Heltec.display->fillRect(topcorner-x, topcorner-y,
bottomcorner-x, bottomcorner-y); //filled rectangle
Heltec.display->drawCircle(center-x, center-y, radius) //circle

Heltec.display->setTextAlignment(TEXT_ALIGN_LEFT);
Heltec.display->setFont(ArialMT_Plain_10);
Heltec.display->drawString(0, 0, "Hello world"); //this is of course one of the main methods, writing text on the screen

Heltec.display->setColor(WHITE); //color to use

This set of commands will take you far. The video below shows an analog clock in operation. It draws a circle, and then inside it, three likes with the same origin and differing lengths:

float xsec = arc_second * sin(analogSec * 6 * PI / 180); // Second needle TIP position, arc=needle length
float ysec = arc_second * cos(analogSec * 6 * PI / 180);
float xmin = arc_minute * sin(analogMin * 6 * PI / 180); // Minute needle TIP position, arc=needle length
float ymin = arc_minute * cos(analogMin * 6 * PI / 180);
float xhour = arc_hour * sin(hourDegrees * PI / 180); // Hour needle TIP position, arc=needle length
float yhour = arc_hour * cos(hourDegrees * PI / 180);
Heltec.display->drawCircle(x_origo, y_origo, 31);
Heltec.display->drawLine(x_origo, y_origo, x_origo + xsec, y_origo - ysec); //Second hand
Heltec.display->drawLine(x_origo, y_origo, x_origo + xmin, y_origo - ymin); //Minute hand
Heltec.display->drawLine(x_origo, y_origo, x_origo + xhour, y_origo - yhour); //Hour hand
Heltec.display->display();

You can learn more of the use of Heltec in the ESP32 UDP – Android application posts, of which a three part blog entry is being written and will be published as soon as I get them done.

Analog clock on Heltec

This analog/digital clock file, in case you want to try it out, is indeed on Github. To get the analog clock to change into a digital one, ground Pin 18. I hope this post helps you get started with the clever little OLED ESP32.

Loading

0 thoughts on “How to use the Heltec OLED display on the ESP32”

  1. Hi
    When i complie you clock code i get the error
    Arduino: 1.8.9 (Windows 10), Board: “WiFi Kit 32, Disabled, 240MHz (WiFi/BT), 921600, None”

    Build options changed, rebuilding all
    wificlock:4:20: error: images.h: No such file or directory

    compilation terminated.

    Multiple libraries were found for “WiFi.h”
    Used: C:\Users\david\AppData\Local\Arduino15\packages\Heltec-esp32\hardware\esp32\0.0.5\libraries\WiFi
    Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
    exit status 1
    images.h: No such file or directory

    This report would have more information with
    “Show verbose output during compilation”
    option enabled in File -> Preferences.

    Can you tell me what library is missing as i have tried a lot of oled one with no success

    Thanks

    1. Hi! What you are missing is the image file for the logos. I am giving you a copy of my own file, which you will be able to use to build, and when you make your own XBM file, you can copy that file’s content into my file’s data part.

      This file goes into the same folder as the INO file.

      Get the file from https://www.dropbox.com/s/s3r2xij8439agaz/images.h?dl=0

  2. Please re-review the clear-screen (fillRect) function sample. The example provided, isn’t a complete function, nor is the “issetColor“ a known function.

    void fillRect(void) {
    uint8_t color = 1;
    for (int16_t i=0; isetColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors
    Heltec.display->fillRect(i, i, DISPLAY_WIDTH – i2, DISPLAY_HEIGHT – i2);
    Heltec.display->display();
    delay(10);
    color++;
    }
    // Reset back to WHITE
    Heltec.display->setColor(WHITE);
    }

    1. Hi! Thank you very much for spotting this. The proper code is
      void fillRect(void) {
      uint8_t color = 1;
      for (int16_t i = 0; i < DISPLAY_HEIGHT / 2; i += 3) { Heltec.display->setColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors
      Heltec.display->fillRect(i, i, DISPLAY_WIDTH - i * 2, DISPLAY_HEIGHT - i * 2);
      Heltec.display->display();
      delay(10);
      color++;
      }
      // Reset back to WHITE
      Heltec.display->setColor(WHITE);

  3. Hello my friend, awesome tutorial, easy to grasp, modify, and expand.
    +1 thing I found in tinkering, is if accessible in final product, to change switch pin from 18 to 0, so as to use the on board prg button.

    Thank again.

    1. Hi, thanks for your kind comment! Iam not sure what you mean with pin 0?

      1. First thanks for the tutorial,
        and also thanks to CJ for the PIN 0 = PRG button info, very handy specially for demo purpose where you dont want to solder wires and buttons.

        Also want to show another useful method:
        Heltec.display->setPixel(x, y); // sets a pixel at given x, y
        and there is clearPixel(x,y), but i haven’t used yet.

        Example:
        void makeNoise() {
        Heltec.display->clear();
        for (int16_t y=0; y<DISPLAY_HEIGHT; y+=1) {
        for (int16_t x=0; xsetPixel(x, y);
        }
        }
        Heltec.display->display();
        }

        1. Hi! THanks for the comment and this new method too, must give it a look.

  4. I’d love to know how to adjust the included settings for my time zone and/or DST!

    1. Hi, glad you liked the post.

      In the code, which is on GitHub, there are these lines:

      // Define NTP Client to get time
      const char* ntpServer = “pool.ntp.org”;
      const long gmtOffset_sec = 2 x 3600;
      const int daylightOffset_sec = 3600;

      The gmtOffset_sec is for the time zone, which is 2 hours more than GMT here in Finland, hence 2 x 3600. All you need to do is figure out your time zone in seconds and deduct it if to the west of Greenwich, and add if to the east.

      If you want to be really fancy, you can see the date also in NTP and use that information to figure out whether it is Daylight Saving Time still. But that is left for the reader as an exercise.

    2. Have a look at the wonderful library, eztime. It has everything you need, including timezones and DST management.

  5. Hi Heikki,
    I’m hoping you can help. I am new to the field of Arduino and creating files. What I am hoping to do is I’m sure, simple, but getting the information seems quite difficult!
    What I want to do is transmit telemetry data via WiFi from an RC transmitter to an ESP32 board (with OLED) and pass it on to a tracking station. Basically the video at the start of this page shows exactly what I am after with respect to screen information, but unfortunately I could not find any ‘how-to’s’ on creating this on this page.
    Could you please point me to a correct location to learn this please?
    Thanks in advance!

    1. Hi Mark,

      I will of course help if I can. The code that runs this little app in on GitHub and the link to that is at the bottom of the page.

      If you want to collect data and then pass it on to a serve on the Internet, I have a couple of such projects on https://github.com/HeikkiHietala?tab=repositories. It would help if you elaborated a little on that project of yours, but the examples on GitHub are rather easy to adapt to any purpose. Have a look at these and come back to discuss any further needs.

      You could start with this: https://www.sabulo.com/sb/arduino/humidity-and-temperature-data-on-iot/ which reads temp and humidity data and updates a web file every five minutes.

      1. Hi Heikki,
        Thanks so much for the quick reply!
        OK, so the purpose of this board is to receive telemetry information from an RC aircraft and send it on to an automatic antenna tracking device. I need the device to be updated with GPS information from the aircraft, so it knows where to point. In terms of data flow:
        – GPS telemetry is sent back from the plane to the transmitter.
        – The transmitter has a WiFi module that can broadcast this information.
        – The ESP32 will receive this information via WiFi and pass it on to the tracking device via a wired connection (direct).
        I’ll have a look at the file you provided. Again, many thanks. I have an image that may illustrate my needs, but also, this video shows exactly what I am after, but this person is using a bluetooth board with no display:
        https://youtu.be/CQvbUByPu6E
        Kind regards,
        Mark

        1. Sure thing. I am not as sure of what you want to achieve, and an image would help, but I’ll stand by to assist in any way I can later on.

          Do you have a WLAN box up in the aircraft, and an ESP32 on the ground, or vice versa?

          1. Thanks once more.
            The plane receiver has a feature that allows it to send back telemetry/information to the transmitter (called MAVlink). The ESP32 is on the ground receiving the WiFi from the transmitter module.
            How can I send you an image? Did you manage to watch the video at all? Essentially I just want to substitute the HC-05 in the video with my ESP32 but include a readout on the OLED screen.
            Kind regards,
            Mark

  6. Thanks once more.
    The plane receiver has a feature that allows it to send back telemetry/information to the transmitter (called MAVlink). The ESP32 is on the ground receiving the WiFi from the transmitter module.
    How can I send you an image? Did you manage to watch the video at all? Essentially I just want to substitute the HC-05 in the video with my ESP32 but include a readout on the OLED screen.
    Kind regards,
    Mark

  7. I normally dont leave comments, but this is a well written tutorial I appreciate you sharing.

    1. Hi! Glad to hear you liked it, I hope it helps you along with the Heltec ESP32.

  8. Hello, I just ran across your forum here and absolutely enjoyed it. Your willingness to lend help to others is commendable and I will be coming back. My project is still in it’s infancy and is going through lots of changes, but my question is about the “ESP32 Lora” and its overall transmission capabilities. I’m creating an autonomous UAV that will have GPS navigation and full gyro ability. I’m open to any suggestions if you have any.

    1. Hi Patrick, and thank you! I have not much on the LoRa system, but you probably have already learned of Rui Santos’ magnificent site randomnerdtutorials.com which has a lot of info on ESP32 and LoRa.

  9. HI,
    Using ARduino IDE and the ESP board with display like shown for clock.
    Program compiles, uploads and says it is doing a hard boot
    I had to set board to Heltec WiFi Lora 32 to stop display compile errors.

    The display shows it is executing the prior program and shows :
    OLED Initial done
    Haaga-Helia
    3D + Robo Lab
    Connecting to (Wifi)
    Serial monitor shows it saying it is connected to WiFI
    So Board is functinal.

    I have tried holding down reset button and no effect (is my timing off?)
    Suggestions please.

    1. Hi Nick,

      I am not seeing your problem. If the program compiles, goes through hard reset, and shows the boot sequence graphics, what is the issue? Does it not start to show the clock?

  10. Hi, thank you for your tutorial.
    I have been trying all sorts of code for the ESP32 WiFi kit 32 v3 (in my case) with some success. Then I found your tutorial and tried to get it working. I get a blank, black screen and the following text in the monitor.
    ESP-ROM:esp32s3-20210327
    Build:Mar 27 2021
    rst:0x1 (POWERON),boot:0x8 (SPI_FAST_FLASH_BOOT)
    SPIWP:0xee
    mode:DIO, clock div:1
    load:0x3fce3818,len:0x508
    load:0x403c9700,len:0x4
    load:0x403c9704,len:0xad0
    load:0x403cc700,len:0x29e4
    entry 0x403c9880
    Serial initial done
    you can see OLED printed OLED initial done!
    However, the OLED display remains black. Yes, the image .h file was added.
    Because I am at the bottom of the learning curve, I cannot see a way of getting this to work.
    The only observation I can make is that your post is 4 years old and Heltec have greatly modified their offering which might also include the library files. I say this because a simple text and graphics sketch produced by them runs perfectly. The difference is in the libraries used and the calls to it.
    #include “HT_SSD1306Wire.h” // legacy include: `#include “SSD1306.h”`
    #include “HT_DisplayUi.h”
    #include “images.h”

    void drawFrame2(ScreenDisplay *display, DisplayUiState* state, int16_t x, int16_t y) {
    // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file
    // Besides the default fonts there will be a program to convert TrueType fonts into this format
    display->setTextAlignment(TEXT_ALIGN_LEFT);
    display->setFont(ArialMT_Plain_10);
    display->drawString(0 + x, 10 + y, “Arial 10”);

    display->setFont(ArialMT_Plain_16);
    display->drawString(0 + x, 20 + y, “Arial 16”);

    display->setFont(ArialMT_Plain_24);
    display->drawString(0 + x, 34 + y, “Arial 24”);

    As you see, no Heltec… in sight.
    I would appreciate your opinion so I can move on with learning to use this device.
    Thank you again for your tutorial and time.

    1. Hi Alan,

      I’ll have to return to this when the next academic year starts and I will return to the Lab. Sorry for the delay.

Leave a Reply

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

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.