{"id":1779,"date":"2020-04-16T10:21:46","date_gmt":"2020-04-16T10:21:46","guid":{"rendered":"https:\/\/www.sabulo.com\/sb\/?p=1779"},"modified":"2025-10-31T08:31:04","modified_gmt":"2025-10-31T06:31:04","slug":"how-to-use-the-heltec-oled-display-on-the-esp32","status":"publish","type":"post","link":"https:\/\/www.sabulo.com\/sb\/esp32-development-board\/how-to-use-the-heltec-oled-display-on-the-esp32\/","title":{"rendered":"How to use the Heltec OLED display on the ESP32"},"content":{"rendered":"\n<p>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.<\/p>\n\n\n\n<p>I bought a handful of the <a href=\"https:\/\/heltec.org\/project\/wifi-lora-32v2\/\" target=\"_blank\" rel=\"noreferrer noopener\">Heltec OLED WiFi kit chips<\/a> (the link goes to the current V2 version&#8217;s page) so as to have a little display without the hassle of attaching a two-line LCD on the device, to see what&#8217;s happening in the system. <a href=\"https:\/\/www.sabulo.com\/sb\/3d-printing-2\/finding-rotation-speed-using-arduino\/\" target=\"_blank\" rel=\"noreferrer noopener\">I have a previous piece on how to use the LCD displays<\/a>, in case you are interested (it features the Arduino but it works just as well on ESP32).<\/p>\n\n\n\n<p>The installation of the Heltec brand chips is straightforward, because after installing the general support for ESP32, you can add <a rel=\"noreferrer noopener\" href=\"https:\/\/heltec.org\/wifi_kit_install\/\" target=\"_blank\">Heltec as a library <\/a>to the Arduino IDE. This generates the file Heltec.h into the Libraries folder, and that brings you all the functionality you need. <\/p>\n\n\n\n<p>EDIT: THe library used by my version of Heltec Wifi Kit 32 is version 1.1.5. <strong>Your mileage may vary.<\/strong> Be prepared to install highe or lower versions based on your device&#8217;s age. <\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/HeikkiHietala\/ESP32-OLED-NTP-clock\" target=\"_blank\" rel=\"noreferrer noopener\">The code you see in this blog post is available on GitHub.<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">OLED features<\/h3>\n\n\n\n<p>OLED stands for Organic LED, and it is some sort of biomechanical sorcery or magick that brings up imaged on the 0.96&#8243; screen. It is bluish in color. The resolution is 128 x 96 pixels, so this isn&#8217;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 <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.squix.org\/2015\/05\/esp8266-nodemcu-how-to-create-xbm.html\" target=\"_blank\">nice tutorial on using XBM<\/a>. 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. <\/p>\n\n\n\n<p>Here&#8217;s a short video on how the OLED works.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls src=\"https:\/\/www.sabulo.com\/sb\/wp-content\/uploads\/2020\/04\/booting-the-heltec.mp4\"><\/video><figcaption class=\"wp-element-caption\">OLED with custom images, booting<\/figcaption><\/figure>\n\n\n\n<p>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:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"128\" height=\"64\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/uploads\/2020\/04\/hhlogo.png?resize=128%2C64&#038;ssl=1\" alt=\"\" class=\"wp-image-1783\"\/><\/figure>\n<\/div>\n\n\n<p>In XBM code, it looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">define hhlogo_width 128\ndefine hhlogo_height 64\nconst unsigned char hhlogo_bits[] PROGMEM = {\n0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n\n[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.]\n\n0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n0xFF, 0xFF, 0xFF, 0xFF, };<\/pre>\n\n\n\n<p>When these bits are rendered in the device, you see the picture as in the image above. <\/p>\n\n\n\n<p>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. <\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"1080\" style=\"aspect-ratio: 1920 \/ 1080;\" width=\"1920\" controls src=\"https:\/\/www.sabulo.com\/sb\/wp-content\/uploads\/2020\/04\/0001-0794-1.mp4\"><\/video><figcaption class=\"wp-element-caption\">The draw demo program<\/figcaption><\/figure>\n\n\n\n<p>Let&#8217;s look at how the device draws things.  In the Setup function, the device must be enabled:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Heltec.begin(true \/<em>DisplayEnable Enable<\/em>\/, false \/<em>LoRa Disable<\/em>\/, true \/<em>Serial Enable<\/em>\/);\nHeltec.display-&gt;setContrast(255);<\/pre>\n\n\n\n<p>After that, it can be asked to do all kinds of operations, all of which must be followed by <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Heltec.display-&gt;display();<\/pre>\n\n\n\n<p>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&#8217;s easy enough. Its counterpart is the clear operation:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> Heltec.display-&gt;clear();<\/pre>\n\n\n\n<p>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:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">void fillRect(void) {\nuint8_t color = 1;\nfor (int16_t i = 0; i &lt; DISPLAY_HEIGHT \/ 2; i += 3) { \n\/\/ alternate colors\nHeltec.display-&gt;setColor((color % 2 == 0) ? BLACK : WHITE); \nHeltec.display-&gt;fillRect(i, i, DISPLAY_WIDTH - i * 2, DISPLAY_HEIGHT - i * 2);\nHeltec.display-&gt;display();\ndelay(10);\ncolor++;}\n\/\/ Reset back to WHITE\nHeltec.display-&gt;setColor(WHITE);\n}<\/pre>\n\n\n\n<p>So, in essence, it is very easy to work with this display. The main commands you will use are<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Heltec.display-&gt;drawLine(start-x, start-y, end-x, end-y);\nHeltec.display-&gt;drawRect(topcorner-x, topcorner-y, \nbottomcorner-x, bottomcorner-y); \/\/open rectangle\nHeltec.display-&gt;fillRect(topcorner-x, topcorner-y,\nbottomcorner-x, bottomcorner-y); \/\/filled rectangle\nHeltec.display-&gt;drawCircle(center-x, center-y, radius) \/\/circle\n\nHeltec.display-&gt;setTextAlignment(TEXT_ALIGN_LEFT);\nHeltec.display-&gt;setFont(ArialMT_Plain_10);\nHeltec.display-&gt;drawString(0, 0, \"Hello world\"); \/\/this is of course one of the main methods, writing text on the screen\n\nHeltec.display-&gt;setColor(WHITE); \/\/color to use<\/pre>\n\n\n\n<p>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:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">float xsec = arc_second * sin(analogSec * 6 * PI \/ 180); \/\/ Second needle TIP position, arc=needle length<br>float ysec = arc_second * cos(analogSec * 6 * PI \/ 180);<br>float xmin = arc_minute * sin(analogMin * 6 * PI \/ 180); \/\/ Minute needle TIP position, arc=needle length<br>float ymin = arc_minute * cos(analogMin * 6 * PI \/ 180);<br>float xhour = arc_hour * sin(hourDegrees * PI \/ 180); \/\/ Hour needle TIP position, arc=needle length<br>float yhour = arc_hour * cos(hourDegrees * PI \/ 180);<br>Heltec.display-&gt;drawCircle(x_origo, y_origo, 31);<br>Heltec.display-&gt;drawLine(x_origo, y_origo, x_origo + xsec, y_origo - ysec); \/\/Second hand<br>Heltec.display-&gt;drawLine(x_origo, y_origo, x_origo + xmin, y_origo - ymin); \/\/Minute hand<br>Heltec.display-&gt;drawLine(x_origo, y_origo, x_origo + xhour, y_origo - yhour); \/\/Hour hand<br>Heltec.display-&gt;display();<\/pre>\n\n\n\n<p>You can learn more of the use of Heltec in the ESP32 UDP &#8211; Android application posts, of which a three part blog entry is being written and will be published as soon as I get them done.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1280 \/ 720;\" width=\"1280\" controls src=\"https:\/\/www.sabulo.com\/sb\/wp-content\/uploads\/2020\/04\/VID_20200416_104908.mp4\"><\/video><figcaption class=\"wp-element-caption\">Analog clock on Heltec<\/figcaption><\/figure>\n\n\n\n<p>This analog\/digital clock file, in case you want to try it out, <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/HeikkiHietala\/ESP32-OLED-NTP-clock\" target=\"_blank\">is indeed on Github<\/a>. 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.<\/p>\n\n\n\n<p><\/p>\n<div class=\"pvc_clear\"><\/div><p id=\"pvc_stats_1779\" class=\"pvc_stats all  \" data-element-id=\"1779\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/i0.wp.com\/www.sabulo.com\/sb\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif?resize=16%2C16&#038;ssl=1\" border=0 \/><\/p><div class=\"pvc_clear\"><\/div>","protected":false},"excerpt":{"rendered":"<p>Heltec is a Chinese company that makes all kinds of SoC (system on a chip) devices. Their ESP32-based SoCs appear &hellip; <a href=\"https:\/\/www.sabulo.com\/sb\/esp32-development-board\/how-to-use-the-heltec-oled-display-on-the-esp32\/\" class=\"more-link\">More <span class=\"screen-reader-text\">How to use the Heltec OLED display on the ESP32<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_1779\" class=\"pvc_stats all  \" data-element-id=\"1779\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img loading=\"lazy\" decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/www.sabulo.com\/sb\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[64,86,79],"tags":[],"class_list":["post-1779","post","type-post","status-publish","format-standard","hentry","category-esp32-development-board","category-heltec-oled","category-led"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p6vhqE-sH","_links":{"self":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts\/1779","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/comments?post=1779"}],"version-history":[{"count":15,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts\/1779\/revisions"}],"predecessor-version":[{"id":2184,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/posts\/1779\/revisions\/2184"}],"wp:attachment":[{"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/media?parent=1779"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/categories?post=1779"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sabulo.com\/sb\/wp-json\/wp\/v2\/tags?post=1779"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}