ESP with 1602 LCD

Hi all,
I’ve recently purchased a small LCD 16x2 (https://www.waveshare.com/wiki/LCD1602_I2C_Module) with the intent of connecting to an esphome device. I have an ESP32 C3 and a Nodemcu 8266 I’ve been testing on.
I can’t get this screen to print text. All I see is boxes on the top row. I know it works as I can get text output on a PI with a test script. In esphome, I can see the address, it connects and doesnt log any other errors.

i2c:
  sda: D1
  scl: D2
  scan: True

display:
  - platform: lcd_pcf8574
    dimensions: 16x2
    address: 0x3E
    id: lcd
    update_interval: 20s
    lambda: |-
      it.print("Test Text");

And from the log:

[14:48:59][C][i2c.arduino:059]: I2C Bus:
[14:48:59][C][i2c.arduino:060]:   SDA Pin: GPIO5
[14:48:59][C][i2c.arduino:061]:   SCL Pin: GPIO4
[14:48:59][C][i2c.arduino:062]:   Frequency: 200000 Hz
[14:48:59][C][i2c.arduino:065]:   Recovery: bus successfully recovered
[14:48:59][I][i2c.arduino:075]: Results from i2c bus scan:
[14:48:59][I][i2c.arduino:081]: Found i2c device at address 0x3E
[14:48:59][C][lcd_pcf8574:024]: PCF8574 LCD Display:
[14:48:59][C][lcd_pcf8574:025]:   Columns: 16, Rows: 2
[14:48:59][C][lcd_pcf8574:026]:   Address: 0x3E
[14:48:59][C][lcd_pcf8574:027]:   Update Interval: 20.0s

I was wondering if anyone may be able to assist with further troubleshooting options?

I have a similar display on one of my projects. Here’s my code. Note, the VL53 is a distnce measuring sensor. Did you forget to load a font?

font:
  - file: 'arialbd.ttf'
    id: fontbold
    size: 36


display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    address: 0x3C
    lambda: |-
      if (id(VL53).has_state()) {
        if (isnan(id(VL53).state)) {
          it.printf(5, 18, id(fontbold), TextAlign::TOP_LEFT, "-------");
        } else {
          it.printf(5, 18, id(fontbold), TextAlign::TOP_LEFT, "%.1fM", id(VL53).state);
        }
      }

Thanks for quick reply. As this is a simple dot matrix style 2 line display, the font option wasn’t an option, but I’ll continue to have a play.

Hi,
I created device which use ultrasound distance measuring to measure level of heating oil in tank and I use such display too.

Here is my code, I can explain it more detailed if you want.
Best regards.

#
# Ultrasound distance measurement
#
# Wiring:
#   + 5V (positive power supply) -> 5V
#   Trig (control side) RX       -> D5
#   Echo (the receiver) TX       -> D6
#   GND (negative)               -> GND
#   Na D4 je status led (na pločici)
#
# https://markus-haack.com/ultrasonic-distance-sensors-esphome/
# https://www.aliexpress.com/item/32665460264.html?spm=a2g0o.order_list.order_list_main.69.608c1802MeRvmI
#
# 2023-12-02:
#   - Initial version
#
# 2023-12-03:
#   - Dodan I2C LCD display 16x2
#   -  Wiring:
#        + 5V (positive power supply) -> 5V
#        scl                          -> D1
#        sda                          -> D2
#        GND (negative)               -> GND
#   - Ispisuje sadržaj input_text.heat_fuel_display_l1 u liniji 1
#   - Ispisuje sadržaj input_text.heat_fuel_display_l2 u liniji 2
#
# 2023-12-04:
#   - Dodano zaboravljeno id(api_was_connected) = 1; u on_boot sekciju
#
# 2023-12-15:
#   - Dodan template sensor "${devname} Raw distance" koji šalje svaku
#     izmjerenu vrijednost distance prije svih filtera
#   - Povećano vrijeme između mjerenja na 10s
#   - Produžen uzv impuls na 80us što doprinosi pouzdanijem odzivu
#
# 2023-12-16:
#   - Isproban prototip na stvarnom tanku i radi
#   - Između D3 i GND dodan pushbutton koji se vidi kao senzor button
#   - te kao akcija: single, double, triple ili hold
#   - Dodan switch LCD Backlight te servisi backlight_on i backlight_off.
#     Svi oni preko scripta display_on i display_off kontroliraju LCD backlight.
#
# 2023-12-17:
#   - Dodana plava led dioda za status
#     S D4 ide 4k7 na anodu, a katoda ide na GND
#   - Dodan Dallas DS18B20 da mjeri temperaturu tanka
#     Spojen s 1,5m telefonske flat žice
#     VCC na 5V, GND na GND, data pin DQ na D7 na ESP8266
#     Pull up otpornik od 4k7 stavljen na stranu senzora
#
# 2023-12-31:
#   - Podešeni parametri ultrasonic senzora i stavljen filter: sliding_window_moving_average
#   - Uređaj je u upotrebi
#
#
# 2024-01-02:
#   - Nakon raznih pokušaja s sliding_window_moving_average vraćen median filter
#
# 2024-01-08:
#   - Za ultrasonic senzor primijenjen cijeli niz filtera koji napokon smiruju priču
#
# 2024-01-09:
#   - Isti set filtera upotrebljen za temperaturu
#

substitutions:
  devname: usdist01

globals:
  - id: api_was_connected
    type: int
    restore_value: no
    initial_value: '0'
  - id: reboot_counter
    type: int
    restore_value: no
    initial_value: '0'
  - id: display_backlight
    type: int
    restore_value: no
    initial_value: '1'
  - id: display_ln2_mode
    type: int
    restore_value: no
    initial_value: '0'

wifi:
  networks:
    - ssid:  "IShome08"
      password: !secret wifi_password
    - ssid:  "IShome04"
      password: !secret wifi_password
    - ssid:  "IShome06"
      password: !secret wifi_password
    - ssid:  "IShome07"
      password: !secret wifi_password
    - ssid:  "IShome05"
      password: !secret wifi_password
  domain: !secret domain

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${devname}
    password: !secret ap_password

captive_portal:

# Do not enable logging
logger:
  #level: DEBUG
  level: INFO
  baud_rate: 0

ota:
  password: !secret ota_password

esphome:
  name: $devname
  platform: ESP8266
  board: d1_mini
  on_boot:
    priority: -110
    then:
      - script.execute: display_off
      - wait_until:
          api.connected:
      - lambda: |-
          id(api_was_connected) = 1;

      - sensor.template.publish:
          id: "${devname}_display_ln2_mode"
          state: !lambda 'return id(display_ln2_mode);'

      - text_sensor.template.publish:
          id: "${devname}_button_action"
          state: !lambda 'return "none";'

      - script.execute: display_on
      - delay: 1000ms
      - homeassistant.service:
          service: script.usdist01_startup_script
          data: {}

api:
  password: !secret api_password
  services:
    - service: backlight_on
      then:
        - script.execute: display_on
    - service: backlight_off
      then:
        - script.execute: display_off
    - service: update_display
      then:
        - component.update: lcd_display
    - service: display_ln2_next
      then:
        - lambda: |-
            id(display_ln2_mode)++;
            if (id(display_ln2_mode) > 1) {
              id(display_ln2_mode) = 0;
            }
        - sensor.template.publish:
            id: "${devname}_display_ln2_mode"
            state: !lambda 'return id(display_ln2_mode);'


dallas:
  - pin: D7
    update_interval: 2s

sensor:
  - platform: uptime
    name: "${devname} uptime"
    id: "${devname}_uptime"
  - platform: wifi_signal
    name: "${devname} WiFi Signal"
    id: "${devname}_wifi_signal"
    update_interval: 60s

  - platform: ultrasonic
    trigger_pin: D5
    echo_pin: D6
    name: "${devname} Distance"
    id: "${devname}_distance"
    device_class: distance
    icon: mdi:arrow-expand-down
    unit_of_measurement: m
    accuracy_decimals: 6
    update_interval: 3s
    #pulse_time: 60us
    pulse_time: 80us
    #pulse_time: 100us
    timeout: 1.8m
    on_raw_value:
      then:
        - sensor.template.publish:
            id: "${devname}_raw_distance"
            state: !lambda 'return x;'
    filters:
      - skip_initial: 3
      - filter_out: NaN
      - median:
          window_size: 9
          send_every: 4
          send_first_at: 3
      - quantile:
          window_size: 7
          send_every: 4
          send_first_at: 3
          quantile: .9
      - sliding_window_moving_average:
          window_size: 7
          send_every: 4
          send_first_at: 3
      #- delta: 0.005
      - delta: 0.002

  - platform: template
    name: "${devname} Raw distance"
    id: "${devname}_raw_distance"
    unit_of_measurement: m
    device_class: distance
    accuracy_decimals: 6
    icon: mdi:arrow-expand-down

  - platform: template
    name: "${devname} Display ln2 mode"
    id: "${devname}_display_ln2_mode"

  - platform: dallas
    #index: 0
    address: 0x8b000000643cf428
    id: "${devname}_tank_temperature"
    name: "${devname} Tank Temperature"
    resolution: 12
    force_update: false
    filters:
      - skip_initial: 3
      - filter_out: NaN
      - filter_out: 85.0
      - filter_out: -127.0
      - median:
          window_size: 9
          send_every: 4
          send_first_at: 3
      - quantile:
          window_size: 7
          send_every: 4
          send_first_at: 3
          quantile: .9
      - sliding_window_moving_average:
          window_size: 7
          send_every: 4
          send_first_at: 3
      - delta: 0.1


text_sensor:
  - platform: homeassistant
    name: "${devname} Display L1"
    entity_id: input_text.heat_fuel_display_l1
    id: "${devname}_display_l1"

  - platform: homeassistant
    name: "${devname} Display L2"
    entity_id: input_text.heat_fuel_display_l2
    id: "${devname}_display_l2"

  # Expose ESPHome version as sensor.
  - platform: version
    name: $devname ESPHome Version
  # Expose WiFi information as sensors.
  - platform: wifi_info
    ip_address:
      name: $devname IP
    ssid:
      name: $devname SSID
    bssid:
      name: $devname BSSID

  - platform: template
    name: "${devname} button action"
    id: "${devname}_button_action"
    icon: "mdi:gesture-tap"

binary_sensor:

  - platform: status
    name: "Statussensor"
    id: statussensor

  - platform: gpio
    pin:
      number: D3
      mode:
        input: true
        pullup: true
    name: "${devname} button"
    id: "${devname}_button"
    filters:
      - invert:
      - delayed_on: 30ms
      - delayed_off: 30ms
    on_multi_click:
      - timing:
          - ON for 40ms to 400ms
          - OFF for at least 330ms
        then:
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "single";'
          - delay: 10ms
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "none";'
      - timing:
          - ON for 40ms to 400ms
          - OFF for 40ms to 300ms
          - ON for 40ms to 400ms
          - OFF for at least 330ms
        then:
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "double";'
          - delay: 10ms
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "none";'
      - timing:
          - ON for 40ms to 400ms
          - OFF for 40ms to 300ms
          - ON for 40ms to 400ms
          - OFF for 40ms to 300ms
          - ON for 40ms to 400ms
          - OFF for at least 50ms
        then:
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "triple";'
          - delay: 10ms
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "none";'
      - timing:
          - ON for at least 2s
        then:
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "hold";'
          - delay: 10ms
          - text_sensor.template.publish:
              id: "${devname}_button_action"
              state: !lambda 'return "none";'

# I want status LED too (red one)
# It will be on D4 pin of D1 mini
status_led:
  pin:
    number: D4
    inverted: true
    mode:
      input: false
      pullup: false
      output: true

switch:
  - platform: restart
    name: "${devname} Restart"
    id: rst_switch

  - platform: template
    name: "${devname} LCD Backlight"
    id: "${devname}_lcd_backlight"
    lambda: |-
      if (id(display_backlight) > 0) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
      - lambda: |-
          id(display_ln2_mode) = 0;
      - sensor.template.publish:
          id: "${devname}_display_ln2_mode"
          state: !lambda 'return id(display_ln2_mode);'
      - script.execute: display_on
      - component.update: lcd_display
    turn_off_action:
      - script.execute: display_off


script:
  - id: display_on
    mode: restart
    then:
      - lambda: |-
          id(lcd_display).backlight();
          id(display_backlight) = 1;

  - id: display_off
    mode: restart
    then:
      - lambda: |-
          id(lcd_display).no_backlight();
          id(display_backlight) = 0;

interval:
  - interval: 20sec
    then:
      - if:
          condition:
            lambda: 'return id(api_was_connected) > 0;'
          then:
            - if:
                condition:
                  not:
                    api.connected:
                then:
                  - if:
                      condition:
                        lambda: 'return id(display_backlight) < 1;'
                      then:
                        - script.execute: display_on
                        - component.update: lcd_display
                  # Reboot after 20 cycles (20 * 20sec = 5min)
                  - if:
                      condition:
                        lambda: 'return id(reboot_counter) > 20;'
                      then:
                        - switch.turn_on: rst_switch
                        - lambda: |-
                            id(reboot_counter) = 0;
                  - lambda: |-
                      id(reboot_counter) += 1;
                  - if:
                      condition:
                        lambda: 'return id(display_backlight) < 1;'
                      then:
                        - script.execute: display_on
                  #-
                else:
                  - lambda: |-
                      id(reboot_counter) = 0;

i2c:
  sda: D2
  scl: D1
  frequency: 200kHz

display:
  - platform: lcd_pcf8574
    update_interval: 10s
    id: lcd_display
    dimensions: 16x2
    address: 0x3f
    lambda: |-
      if (id(statussensor).state) {
          if (id(display_backlight) > 0) {
          it.printf(0,0,"                ");
          it.printf(0,0,id(${devname}_display_l1).state.c_str());
          it.printf(0,1,"                ");
          it.printf(0,1,id(${devname}_display_l2).state.c_str());
        }
      } else {
        if (id(api_was_connected) > 0){
          it.printf(0,0,"                ");
          it.printf(0,1,"                ");
          it.printf(0,0,"Connection lost!");
          it.printf(0,1,"Reconnecting....");
        } else {
          it.printf(0,0,"                ");
          it.printf(0,1,"                ");
          it.printf(0,0,"Starting system");
          it.printf(0,1,"and connecting!");
        }
      }
# EOF

Try setting the position
it.print(0,0, "Test Text");

Many thanks for the posts. Despite much testing, I’m about to give up and find another display. I think it has to do with the controller (AIP31068L), I can’t find any reference to that connected to esphome.
Many thanks for sharing code @IgorZg I run another esp with ultrasonic sensor for water tank volume and like your approach to filtering and averaging out the readings.

1 Like

That is exactly your problem. I just tried to use this exact one with HA and Tasmota, that chip is not supported! It would sometimes register an address or 2 or 3 with i2cscan but the appearance never changed.

1 Like