Skip to content

Instantly share code, notes, and snippets.

@cnorick
Last active December 8, 2023 21:31
Show Gist options
  • Save cnorick/1ad3d61e7e517fb05e1972908b6b607e to your computer and use it in GitHub Desktop.
Save cnorick/1ad3d61e7e517fb05e1972908b6b607e to your computer and use it in GitHub Desktop.
ESPHome -- Scroll LCD Display Lines
substitutions:
lcd_width: '16'
lcd_height: '2'
scroll_speed: 1s
line_1: 'Long line that we want to scroll'
line_2: 'Another long line that does not fit on the screen'
display:
- platform: lcd_gpio
dimensions: ${lcd_width}x${lcd_height}
data_pins:
- D0
- D1
- D2
- D3
enable_pin: D6
rs_pin: D5
id: lcd
update_interval: $scroll_speed
lambda: |-
static int p1 = 0;
static int p2 = 0;
// These lines could instead come from something like a text sensor.
char* line1 = "$line_1";
int length1 = strlen(line1);
if (length1 > $lcd_width) length1 = $lcd_width;
const char* line2 = "$line_2";
int length2 = strlen(line2);
if (length2 > $lcd_width) length2 = $lcd_width;
int spacesLength = length1 >= $lcd_width ? 0 : $lcd_width - length1 + 1;
char spaces[spacesLength];
for (int i = 0; i < spacesLength - 1; i++) spaces[i] = ' ';
spaces[spacesLength - 1] = '\0';
it.printf("%.*s%s%.*s", length1, line1 + p1, spaces, length2, line2 + p2);
p1++;
if(p1 > (int)strlen(line1) - $lcd_width) {
p1 = 0;
}
p2++;
if(p2 > (int)strlen(line2) - $lcd_width) {
p2 = 0;
}
@clutch70
Copy link

For the copy pasters out there in the world like me, it would seem as though an update or something has changed the required syntax for this to work. I kept getting errors at lines 25 and 29 because the compiler wasn't getting the quotes from the substitution so it couldn't tell it was a string. Adding my own quotes e.g. below did the trick.

char* line1 = "$line_1"; //and
const char* line2 = "$line_2";

@cnorick
Copy link
Author

cnorick commented Jan 11, 2023

@clutch70 Thanks for the heads-up. I get my values from text sensors, so I guess that's why I didn't see it break. I updated the gist with your suggestion.

@steveisaguy
Copy link

Thanks for the code @cnorick
I've been hacking at this for a while to get it to show some data from text sensors but am hitting issues with converting to string/int/char etc.
Could you post your code for using a text sensor? Keen to see where I am going wrong.

@cnorick
Copy link
Author

cnorick commented Feb 3, 2023

Hey @steveisaguy, here is my whole configuration where I use the scrolling LCD. I haven't used this device in a while, so there's a chance it will no longer work after some of the new ESPHome updates. Good luck with it!

substitutions:
  lcd_width: '16'
  lcd_height: '2'
  scroll_speed: 1s

esphome:
  name: meeting-status-board
  platform: ESP8266
  board: nodemcuv2

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:
  password: !secret ota_password

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Meeting-Status-Board"
    password: !secret ap_password

captive_portal:

text_sensor:
  - platform: homeassistant
    id: meeting_title
    entity_id: calendar.work
    attribute: message
    name: "Meeting Title"
    disabled_by_default: true
  - platform: homeassistant
    id: meeting_start
    entity_id: calendar.work
    attribute: start_time
    name: "Meeting Start Time"
    disabled_by_default: true
  - platform: homeassistant
    id: meeting_end
    entity_id: calendar.work
    attribute: end_time
    name: "Meeting End Time"
    disabled_by_default: true
  - platform: template
    name: "Current Light"
    lambda: |-
      if (id(in_meeting).state) {
        return {"red"};
      }
      else if (id(meeting_started).state) {
        return {"yellow"};
      }
      else {
        return {"green"};
      }
    update_interval: 5s
    id: current_light
    disabled_by_default: true
    on_value:
      then:
        - switch.turn_off: green_light
        - switch.turn_off: yellow_light
        - switch.turn_off: red_light
        - if:
            condition:
              binary_sensor.is_on: in_meeting
            then:
              - switch.turn_on: red_light
            else:
              - if:
                  condition:
                    binary_sensor.is_on: meeting_started
                  then:
                    - switch.turn_on: yellow_light
                  else:
                    - switch.turn_on: green_light
    
binary_sensor:
  - platform: homeassistant
    id: meeting_started
    entity_id: calendar.work
    name: "Meeting Started"
    disabled_by_default: true
  - platform: homeassistant
    id: in_meeting
    entity_id: binary_sensor.in_meeting
    name: "In Meeting"
    disabled_by_default: true

switch:
  - platform: gpio
    pin: D7
    name: "Green Light"
    internal: true
    id: green_light
  - platform: gpio
    pin: D8
    name: "Yellow Light"
    internal: true
    id: yellow_light
  - platform: gpio
    pin: 9
    name: "Red Light"
    internal: true
    id: red_light

display:
  - platform: lcd_gpio
    dimensions: ${lcd_width}x${lcd_height}
    data_pins:
      - D0
      - D1
      - D2
      - D3
    enable_pin: D6
    rs_pin: D5
    id: lcd
    update_interval: $scroll_speed
    lambda: |-
      static int p1 = 0;
      static int p2 = 0;
      
      char line1[$lcd_width];
      char startString[$lcd_width/2];
      char endString[$lcd_width/2];
      struct tm start;
      memset(&start, 0, sizeof(tm));
      struct tm end;
      memset(&end, 0, sizeof(tm));
      strptime(id(meeting_start).state.c_str(), "%Y-%m-%d %H:%M:%S", &start);
      strftime(startString, sizeof(startString), "%I:%M", &start);
      
      strptime(id(meeting_end).state.c_str(), "%Y-%m-%d %H:%M:%S", &end);
      strftime(endString, sizeof(endString), "%I:%M", &end);
      sprintf(line1, "%s - %s", startString, endString);
      
      int length1 = strlen(line1);
            
      const char* line2 = id(meeting_title).state.c_str();
      int length2 = strlen(line2);
      if (length2 > $lcd_width) length2 = $lcd_width;
      
      int spacesLength = length1 >= $lcd_width ? 0 : $lcd_width - length1 + 1;
      char spaces[spacesLength];
      for (int i = 0; i < spacesLength - 1; i++) spaces[i] = ' ';
      spaces[spacesLength - 1] = '\0';
      
      it.printf("%.*s%s%.*s", length1, line1 + p1, spaces, length2, line2 + p2);

      p1++;
      if(p1 > (int)strlen(line1) - $lcd_width) {
        p1 = 0;
      }
      p2++;
      if(p2 > (int)strlen(line2) - $lcd_width) {
        p2 = 0;
      }

@djansen1987
Copy link

Hey @steveisaguy, here is my whole configuration where I use the scrolling LCD. I haven't used this device in a while, so there's a chance it will no longer work after some of the new ESPHome updates. Good luck with it!

substitutions:
  lcd_width: '16'
  lcd_height: '2'
  scroll_speed: 1s

esphome:
  name: meeting-status-board
  platform: ESP8266
  board: nodemcuv2

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:

ota:
  password: !secret ota_password

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Meeting-Status-Board"
    password: !secret ap_password

captive_portal:

text_sensor:
  - platform: homeassistant
    id: meeting_title
    entity_id: calendar.work
    attribute: message
    name: "Meeting Title"
    disabled_by_default: true
  - platform: homeassistant
    id: meeting_start
    entity_id: calendar.work
    attribute: start_time
    name: "Meeting Start Time"
    disabled_by_default: true
  - platform: homeassistant
    id: meeting_end
    entity_id: calendar.work
    attribute: end_time
    name: "Meeting End Time"
    disabled_by_default: true
  - platform: template
    name: "Current Light"
    lambda: |-
      if (id(in_meeting).state) {
        return {"red"};
      }
      else if (id(meeting_started).state) {
        return {"yellow"};
      }
      else {
        return {"green"};
      }
    update_interval: 5s
    id: current_light
    disabled_by_default: true
    on_value:
      then:
        - switch.turn_off: green_light
        - switch.turn_off: yellow_light
        - switch.turn_off: red_light
        - if:
            condition:
              binary_sensor.is_on: in_meeting
            then:
              - switch.turn_on: red_light
            else:
              - if:
                  condition:
                    binary_sensor.is_on: meeting_started
                  then:
                    - switch.turn_on: yellow_light
                  else:
                    - switch.turn_on: green_light
    
binary_sensor:
  - platform: homeassistant
    id: meeting_started
    entity_id: calendar.work
    name: "Meeting Started"
    disabled_by_default: true
  - platform: homeassistant
    id: in_meeting
    entity_id: binary_sensor.in_meeting
    name: "In Meeting"
    disabled_by_default: true

switch:
  - platform: gpio
    pin: D7
    name: "Green Light"
    internal: true
    id: green_light
  - platform: gpio
    pin: D8
    name: "Yellow Light"
    internal: true
    id: yellow_light
  - platform: gpio
    pin: 9
    name: "Red Light"
    internal: true
    id: red_light

display:
  - platform: lcd_gpio
    dimensions: ${lcd_width}x${lcd_height}
    data_pins:
      - D0
      - D1
      - D2
      - D3
    enable_pin: D6
    rs_pin: D5
    id: lcd
    update_interval: $scroll_speed
    lambda: |-
      static int p1 = 0;
      static int p2 = 0;
      
      char line1[$lcd_width];
      char startString[$lcd_width/2];
      char endString[$lcd_width/2];
      struct tm start;
      memset(&start, 0, sizeof(tm));
      struct tm end;
      memset(&end, 0, sizeof(tm));
      strptime(id(meeting_start).state.c_str(), "%Y-%m-%d %H:%M:%S", &start);
      strftime(startString, sizeof(startString), "%I:%M", &start);
      
      strptime(id(meeting_end).state.c_str(), "%Y-%m-%d %H:%M:%S", &end);
      strftime(endString, sizeof(endString), "%I:%M", &end);
      sprintf(line1, "%s - %s", startString, endString);
      
      int length1 = strlen(line1);
            
      const char* line2 = id(meeting_title).state.c_str();
      int length2 = strlen(line2);
      if (length2 > $lcd_width) length2 = $lcd_width;
      
      int spacesLength = length1 >= $lcd_width ? 0 : $lcd_width - length1 + 1;
      char spaces[spacesLength];
      for (int i = 0; i < spacesLength - 1; i++) spaces[i] = ' ';
      spaces[spacesLength - 1] = '\0';
      
      it.printf("%.*s%s%.*s", length1, line1 + p1, spaces, length2, line2 + p2);

      p1++;
      if(p1 > (int)strlen(line1) - $lcd_width) {
        p1 = 0;
      }
      p2++;
      if(p2 > (int)strlen(line2) - $lcd_width) {
        p2 = 0;
      }

This looks like a cool project, do you have a photo on how the project looks. Seems you used it for a meeting room display

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment