Output data from one ESP32 to two SH1106 oled displays

Hello everybody!
I’m trying to output sensor data to two SH1106 oled displays connected to the same ESP32. I’m not a codemaster and after try&error for a few days with being stuck on the same place I thought about bothering somebody else with my missing skills :upside_down_face: .

So this is the code that works so far:

font:
  - file: 'slkscr.ttf'
    id: font1
    size: 8

  - file: 'BebasNeue-Regular.ttf'
    id: font2
    size: 65

  - file: 'arial.ttf'
    id: font3
    size: 15


i2c:
  sda: GPIO21
  scl: GPIO22
  scan: false


display:
  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3C
    lambda: |-
      // Print time in HH:MM format
      it.strftime(64, -10, id(font2), TextAlign::TOP_CENTER, "%H:%M", id(esptime).now());
      }

It displays the time (same content) on both connected displays, so far what I expect. I read that I should use “0x3D” to address an additional display on the i2c-bus and tried to extend my code as following:

display:
  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3C
    id: blue_display
    lambda: |-
      // Print time in HH:MM format
      it.blue_display.strftime(64, -10, id(font2), TextAlign::TOP_CENTER, "%H:%M", id(esptime).now());
      }

  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3D
    id: white_display
    lambda: |-
      // Print LivingRoom temperature (from homeassistant sensor)
      if (id(livingroom_temperature).has_state()) {
        it.white_display.printf(1, 64, id(font3), TextAlign::BASELINE_LEFT , "%.1f°", id(livingroom_temperature).state);
      }

      // Print LivingRoom humidity (from homeassistant sensor)
      if (id(livingroom_humidity).has_state()) {
        it.white_display.printf(127, 64, id(font3), TextAlign::BASELINE_RIGHT , "%.1f%%", id(livingroom_humidity).state);
      }

I tried using “id” to declare two different displays.
To be honest I don’t understand “it.strftime(” completely. I guess “it” is some kind of variable that I tried to replace or extend with my id’s I gave to the displays but the compiler keeps complaining about my id’s:

Compiling .pioenvs/muffy/src/main.cpp.o
Compiling .pioenvs/muffy/lib07d/ESPAsyncWebServer-esphome/WebAuthentication.cpp.o
/config/esphome/muffy.yaml: In lambda function:
**/config/esphome/muffy.yaml:101:10: error: 'class esphome::display::Display' has no member named 'blue_display'; did you mean 'Display'?**
       it.blue_display.strftime(64, -10, id(font2), TextAlign::TOP_CENTER, "%H:%M", id(esptime).now());
          ^~~~~~~~~~~~
          Display
/config/esphome/muffy.yaml: In function 'void setup()':
/config/esphome/muffy.yaml:102:8: error: expected ')' before '}' token
       }
        ^
        )
 
         
**src/main.cpp:2260:27: note: to match this '('**
**   blue_display->set_writer([=](display::Display & it) -> void {**
**                           ^**
**/config/esphome/muffy.yaml: At global scope:**
**/config/esphome/muffy.yaml:103:4: error: expected unqualified-id before ')' token**
** **
**    ^**
**/config/esphome/muffy.yaml:104:3: error: 'blue_display' does not name a type**
**   - platform: ssd1306_i2c**
**   ^~~~~~~~~~~~**
**/config/esphome/muffy.yaml:105:3: error: 'blue_display' does not name a type**
**     model: "SH1106 128x64"**
**   ^ ~~~~~~~~~~**
**/config/esphome/muffy.yaml:131:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:132:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:133:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:134:3: error: 'App' does not name a type**
**/config/esphome/muffy.yaml:135:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:136:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:137:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:138:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:139:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:140:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:141:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:142:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:143:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:144:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:118:4: error: expected unqualified-id before ')' token**
**/config/esphome/muffy.yaml:119:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:120:3: error: 'white_display' does not name a type**
**/config/esphome/muffy.yaml:128:3: error: 'App' does not name a type**
**/config/esphome/muffy.yaml:129:1: error: expected declaration before '}' token**
**Compiling .pioenvs/muffy/lib07d/ESPAsyncWebServer-esphome/WebHandlers.cpp.o**
***** [.pioenvs/muffy/src/main.cpp.o] Error 1**

I found a guy that used a second i2c bus because of the same i2c address on both displays. This could work in my case but I would consider it as a workaround. I would like to understand how to write the correct syntax.
Could somebody please give me a hint how to address the second display?
Thank you very much for your time and help!

What displays are you using ??
Do they support changing the I2C address ??
If so, have you done this and confirmed the display is now on 0x3D

it is the display object for which it is a lambda. So in both lambda’s you just use it. For example:

display:
  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3C
    id: blue_display
    lambda: |-
      it.print(0, 10, id(font3), "Hello Blue display!");

  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3D
    id: white_display
    lambda: |-
      it.print(0, 10, id(font3), "Hello White display!");

But you need to change in hardware the address of the white display. It will not automatically listen to a different address.

Hey Timo - thanks a lot, this helps, at least in theory :slight_smile:
I have a new topic now as I have to find out if and how I can change the i2c address. Maybe I finally go for the workaround using a second bus although I recognized that this would require two more wires. Or… maybe somewhere is another oled with a different i2c address flying around.

Ok, I found that on the white display I could somehow change the I2C address. But I still cannot compile my code successfully, probably because of a syntax-error. Can someone please explain me what I did wrong/forgot?

display:
  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3C
    id: blue_display
    lambda: |-
      // Print time in HH:MM format
      it.strftime(64, -10, id(font2), TextAlign::TOP_CENTER, "%H:%M", id(esptime).now());
      }

  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x78
    id: white_display
    lambda: |-
      // Print LivingRoom temperature (from homeassistant sensor)
      if (id(livingroom_temperature).has_state()) {
        it.printf(1, 64, id(font3), TextAlign::BASELINE_LEFT , "%.1f°", id(livingroom_temperature).state);
      }

      // Print LivingRoom humidity (from homeassistant sensor)
      if (id(livingroom_humidity).has_state()) {
        it.printf(127, 64, id(font3), TextAlign::BASELINE_RIGHT , "%.1f%%", id(livingroom_humidity).state);
      }

Since I introduced the display id’s the compiler complains about:

HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Dependency Graph
|-- AsyncTCP-esphome @ 2.1.4
|-- WiFi @ 2.0.0
|-- FS @ 2.0.0
|-- Update @ 2.0.0
|-- ESPAsyncWebServer-esphome @ 3.2.2
|-- DNSServer @ 2.0.0
|-- ESPmDNS @ 2.0.0
|-- noise-c @ 0.1.6
|-- Wire @ 2.0.0
Compiling .pioenvs/muffy/src/main.cpp.o
Compiling .pioenvs/muffy/lib07d/ESPAsyncWebServer-esphome/WebServer.cpp.o
/config/esphome/muffy.yaml: In function 'void setup()':
/config/esphome/muffy.yaml:102:8: error: expected ')' before '}' token
       }
        ^
        )
 
         
src/main.cpp:2260:27: note: to match this '('
   blue_display->set_writer([=](display::Display & it) -> void {
                           ^
/config/esphome/muffy.yaml: At global scope:
/config/esphome/muffy.yaml:103:4: error: expected unqualified-id before ')' token
 
    ^
/config/esphome/muffy.yaml:104:3: error: 'blue_display' does not name a type
   - platform: ssd1306_i2c
   ^~~~~~~~~~~~
/config/esphome/muffy.yaml:105:3: error: 'blue_display' does not name a type
     model: "SH1106 128x64"
   ^ ~~~~~~~~~~
/config/esphome/muffy.yaml:131:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:132:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:133:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:134:3: error: 'App' does not name a type
/config/esphome/muffy.yaml:135:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:136:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:137:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:138:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:139:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:140:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:141:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:142:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:143:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:144:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:118:4: error: expected unqualified-id before ')' token
/config/esphome/muffy.yaml:119:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:120:3: error: 'white_display' does not name a type
/config/esphome/muffy.yaml:128:3: error: 'App' does not name a type
/config/esphome/muffy.yaml:129:1: error: expected declaration before '}' token
*** [.pioenvs/muffy/src/main.cpp.o] Error 1
========================= [FAILED] Took 15.03 seconds =========================

Found the bracket and removed it… :see_no_evil: compiles successfully now, will report any progress.

Hi guys,

in case you are trying to connnect this display " MakerFocus 2pcs 1.3 Inch I2C IIC OLED Display Module 128 x 64 Pixel SSD1306" Amazon Link to Display to ESP Home, it took me a while to create a temp and humidity sensor AMS 2302 with that display using ESP32 devkit v4.

Here is the yaml file in case it helps other people.

esphome:
  name: closet-temp
  friendly_name: Closet Temp

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "REDACTED"

ota:
  - platform: esphome
    password: "REDACTED"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Closet-Temp Fallback Hotspot"
    password: "REDACTED"

captive_portal:

# Sensor configuration
sensor:
  - platform: dht
    pin: GPIO4
    model: DHT22
    temperature:
      name: "Closet Temperature"
      id: Closet_temperature
      accuracy_decimals: 1
      filters:
        - calibrate_linear:
            - 0 -> 0
            - 40 -> 40
      on_value:
        then:
          - lambda: |-
              id(update_timer) = 15;
    humidity:
      name: "Closet Humidity"
      id: Closet_humidity
      accuracy_decimals: 1
    update_interval: 15s

i2c:
  sda: GPIO21
  scl: GPIO22
  frequency: 400kHz  # Speed up I2C to reduce display lag
  scan: false

font:
  - file: "gfonts://Roboto"
    id: roboto_large
    size: 52
  - file: "gfonts://Roboto"
    id: roboto_small
    size: 16
  - file: "gfonts://Roboto"
    id: roboto_tiny
    size: 10    

# Global countdown timer
globals:
  - id: update_timer
    type: int
    restore_value: no
    initial_value: '15'

# Countdown interval every 1s
interval:
  - interval: 1s
    then:
      - lambda: |-
          if (id(update_timer) > 0) {
            id(update_timer)--;
          }

# Display
display:
  - platform: ssd1306_i2c
    model: "SH1106 128x64"
    address: 0x3C
    update_interval: 1s  # So countdown is smooth
    lambda: |-
      if (id(Closet_temperature).has_state()) {
        float temp_f = id(Closet_temperature).state * 1.8 + 32.0;
        it.printf(0, 0, id(roboto_large), TextAlign::TOP_LEFT, "%.1f°", temp_f);
      }

      if (id(Closet_humidity).has_state()) {
        it.printf(127, 64, id(roboto_small), TextAlign::BASELINE_RIGHT, "%.1f%%", id(Closet_humidity).state);
      }

      it.printf(0, 64, id(roboto_tiny), TextAlign::BASELINE_LEFT, "%ds", id(update_timer));

Additonal notes:

  • Display powered by 5V of ESP32
  • AMS2302 powered by 3.3V of ESP32
  • GPIO 21 and GPIO 22 for display
  • GPIO 4 for AMS2302
  • 7 wires total + USB power to ESP32
  • update interval was set to 15 seconds for the screen to match the AMS refresh interval (default of 600 seconds was too high for me)
  • timer on display is synchronized with that 15 sec interval allowing you to see when the next measurement will take place

I hope this helps other people as it took a long time to get the display working from black only to scrambled screen, etc. The toughest part was the description implied SSD1306, while in reality it is an SH1106 display, but the good news is that the platform supports both as long as you specify the correct model.

Enjoy!