TTGO ESP32 board with OLED display and Adafruit library

These days, there’s a huge amount of support out there for virtually any device you can buy. But every now and again you need to do a bit of head-scratching. So this post is simply my way of helping out anyone who’s going through the same process I just did.

I bought a couple of cheap ESP32 boards that have 128×64 pixel OLED displays mounted to them. At $10 a pop, I thought they were good value and would make excellent Internet of Things (IoT) thermometers when coupled with the right sensor.

I’ve got experience with ESP8266 boards – and, indeed, have already made IoT thermometers with them. But these boards would be far more compact.

All I needed to do, I thought, is load up the Adafruit_SSD1306 library (available via the IDE’s ‘manage libraries’ function) into the Arduino IDE and I’d be off and running.

But I hit a snag right away. It wouldn’t work. The manufacturer, TTGO, took it upon itself to use non-standard pins for the I2C bus connections.

You can always write your own code to control the display. And there are many libraries out there for the SSD1306 (none of which I got to work). But I really wanted to use the Adafruit library because … well, it’s good.

It turns out this isn’t a problem, but it took quite a lot of googling, and a small amount of experimentation, to find that out. For example, even on Adafruit’s own tutorial on using the library, it says that you can’t change the SDA and SCL pin assignments. It turns out, that information is out of date – the library has been updated to allow it – but I had to dig down into comments on forums to find this out.

This is what you do…

So here’s what you need to do to use these boards – or, for that matter, any SSD1306 OLED display with non-standard I2C pin assignments – with the Adafruit_SSD1306 library.

I’m basing this on the example code – ssd1306_128x64_i2c – provided with the Adafruit library. I find that library examples like this are always a good starting point in understanding both libraries and the devices they support.

You need to make five changes:

  • Change the display reset pin to 16 (the example default is 4).
  • Declare your own TwoWire instance.
  • Pass that instance as a parameter in the instantiation of the Adafruit_SSD1306 object.
  • Begin your TwoWire instance with the SDA and SCL pins used by the TTGO board.
  • Change the address of the display.

Let’s go through those in detail.

The easy one first

First, the simplest. In the Adafruit library example code, it has pin 4 set as the reset pin for the display. We need to change that to 16.

[EDITED 24/03/2019 in response to Philipp’s comments below] We’ll also need to define the screen width and height in pixels.

#define OLED_RESET 16 // Reset pin - was 4
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

An instance of your own

Normally, the Adafruit_SSD1306 instance is created by passing Wire (by reference) as a parameter. The default code looks like this:


But that’s not going to give us any chance to change the pins. So, instead of passing &Wire as a parameter, let’s create our own I2C object and pass that instead.

TwoWire twi = TwoWire(1); // our own TwoWire instance on bus 1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &twi, OLED_RESET);


Now we head down to the setup() section. In the Adafruit example, this looks like this:

if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever 

It’s working!

Note the 0x3D parameter. This is the address normally used by these 128×64 OLED displays. The 128×32 versions typically use 0x3C. But, for some reason, the display on these boards, while being 128×64, uses 0x3C. So we’ll need to change that.

But before we do, we need to address something else – those SDA and SCL pin assignments. The TTGO board uses pin 4 for SDA and pin 15 for SCL. If we used the display.begin(…) code above, even with the altered address, it would still be trying to use the default I2C pins. So we need to begin our TwoWire instance with the correct pins before we get to display.begin(…). So the altered code looks like this:

void setup() {
    /* ... some stuff omitted ... */
    twi.begin(4,15);  // Needs to come before display.begin is used
    if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
        Serial.println(F("SSD1306 allocation failed"));
        for(;;); // Don't proceed, loop forever
    /* ... lots of stuff omitted ... */

And we’re done! You can now use the Adafruit library, and the Adafruit GFX library with all its lovely fonts.

[UPDATE – later that day] And now it’s working beautifully with the Adafruit GFX fonts, which make all the difference.

It’s connected to the home wifi, getting time and date from my IoT server (the PiDP), and making regular temperature reports to the server. Except that the temperatures are currently fake because there’s no sensor attached. So that’s the next decision – which temperature sensor to use.

[UPDATE TO THE UPDATE] I had to take this post down after adding the above update. I’d originally created the post in WordPress’ new block editor – which is all very whizzy but actually a pain in the ass.

For a start, it wouldn’t let me type an ampersand without converting it to the relevant HTML entity, ‘&amp;’. That sounds like it’s a good thing. But in program listings, sometimes you want an ampersand to just be an ampersand, for cut & paste purposes. Even working in the ‘code’ mode, and in text defined as preformatted (using the <pre> tags). every time I saved the post, plain ampersands were converted to HTML entities. Gah!

So I reverted to the ‘classic’ editor. But, when I added the update sections above (two pars and a picture), it added them four times to the post. And it moved one of the original paragraphs (and not even the last one) to the bottom. Why?

I tried switching back to the block editor, but things just started going downhill. It was now telling me that there were ‘problems’ with many of the pars. And it added more copies of the additional section.

So back to the classic editor and a few minutes stripping out all the formatting tags the block editor had added to the post.

This is classic feature bloat. What was a perfectly fine editor has been replaced with something not nearly as clever as it thinks it is. And buggy as hell. So much for progress.

13 thoughts on “TTGO ESP32 board with OLED display and Adafruit library

  1. Philipp

    My name is Philipp. I have a YouTube Channel where sometimes I make Videos about Arduino. I found your Tutorial but I still can’t get my TTGO to work with Adafruit.
    The part:

    TwoWire twi = TwoWire(1); // our own TwoWire instance on bus 1
    Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &twi, OLED_RESET);

    I think I have to do edit something more than just copy that into my code right? You you this might be self evident :-).

    Would be nice to hear from you. Maybe you could take a short view on my code.


    1. Machina Post author

      Have you defined OLED_RESET as shown in the code? Also, have you defined the screen parameters?

      #define SCREEN_WIDTH 128 // OLED display width, in pixels
      #define SCREEN_HEIGHT 64 // OLED display height, in pixels

      I neglected to include those lines in the post.

      1. Philipp

        Wow thank you, I didn’t expect an answer from you. I would like to link your Site in my next video for great tutorials (My YouTube Channel for 3D printing:
        I did so now, but the first error i get is:

        error: no matching function for call to ‘Adafruit_SSD1306::Adafruit_SSD1306(int, int, TwoWire*, int)’
        Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &twi, OLED_RESET);

        Here is my full code:


        #define OLED_RESET 16
        //Adafruit_SSD1306 display(OLED_RESET);
        #define SCREEN_WIDTH 128 // OLED display width, in pixels
        #define SCREEN_HEIGHT 64 // OLED display height, in pixels
        TwoWire twi = TwoWire(1); // our own TwoWire instance on bus 1
        Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &twi, OLED_RESET);

        #define NUMFLAKES 10
        #define XPOS 0
        #define YPOS 1
        #define DELTAY 2

        #define LOGO16_GLCD_HEIGHT 16
        #define LOGO16_GLCD_WIDTH 16
        static const unsigned char PROGMEM logo16_glcd_bmp[] =
        { B00000000, B11000000,
        B00000001, B11000000,
        B00000001, B11000000,
        B00000011, B11100000,
        B11110011, B11100000,
        B11111110, B11111000,
        B01111110, B11111111,
        B00110011, B10011111,
        B00011111, B11111100,
        B00001101, B01110000,
        B00011011, B10100000,
        B00111111, B11100000,
        B00111111, B11110000,
        B01111100, B11110000,
        B01110000, B01110000,
        B00000000, B00110000 };

        #if (SSD1306_LCDHEIGHT != 64)
        #error(“Height incorrect, please fix Adafruit_SSD1306.h!”);

        void setup() {

        twi.begin(4,15); // Needs to come before display.begin is used
        if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
        Serial.println(F(“SSD1306 allocation failed”));
        for(;;); // Don’t proceed, loop forever

        // by default, we’ll generate the high voltage from the 3.3v line internally! (neat!)
        display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128×64)
        // init done

        // Show image buffer on the display hardware.
        // Since the buffer is intialized with an Adafruit splashscreen
        // internally, this will display the splashscreen.


        1. Machina Post author

          The four #include lines have turned up blank – maybe because WordPress has treated anything between angle brackets as HTML code! But I’m assuming you are including: Adafruit_SSD1306.h

          1. Philipp Hubert

            Hey, Yes sorry
            included is:

            wordpress didn’t copy that.
            Do I have to change some code in another file?

            Maybe you could send me the full example code so that I can see if I am missing something.

          2. Machina Post author

            Okay, I’ve uploaded what I think are the absolute essentials here:

            This repo won’t stay online for long, so grab it while you can! I do have a fully functioning sketch, but it has a lot of additional stuff that I don’t think is relevant to your problem. It also has a lot of crud that needs cleaning out. But if I get time to do that, I’ll upload it…

            Hope this helps.

  2. Philipp Hubert

    Thanks again. I downloaded and tested it, but I get the same error:

    TGO_base:16: error: no matching function for call to ‘Adafruit_SSD1306::Adafruit_SSD1306(int, int, TwoWire*, int)’

    I am missing something but I don’t know what to change.
    I would like to donate you some $ for your help if we can fix this.

    1. Machina Post author

      Are you sure you have the right board selected under Tools? I’m using ‘ESP32 Dev Module’.

      By the way, the original code you posted has no main loop, and you’re calling the display.begin() method twice – once in the setup() function (which is correct) and then again later. Don’t think that’s the issue, though.

  3. John Rudge

    I came across this very useful article and thanks. However after trying it unsuccessfully, I found that I my display used 4 for SCL and 5 for SDA. Changing to


    made all work ok.


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.