splitbrain.org

electronic brain surgery since 2001

LED Matrix with ESPHome & HomeAssistant

When I'm in the workshop, I usually wear my hearing protection all the time. That's good for my ears, but it drives Kaddi nuts when she wants to talk to me. Because I will neither hear her shout, nor will I notice my phone buzzing in my pockets…

So of course I had to find a solution. I needed some kind of visual alarm and maybe a way to get a short message.

Electronics

I decided to use a flashing light for the alarm and a LED pixel matrix for the message part. Connect both to an ESP32, add a few more components and voila…

Most of the stuff I already had. I just needed to buy the light and the LED Matrix.

Wiring it all up was relatively straight forward. I connected the all the ground and Vcc connections on the back of the matrix with some beefier speaker wires, so that all LEDs would get enough juice.

Matrix connections

To power the ESP32 I had planned to simply provide 5V to the Vin pin. Just like I usually do with my ESP8266s. However it just didn't want to work. After a lot of googling, I finally found the answer: on some boards the WiFi won't be enabled when powering from Vin. Only when powered via USB, the WiFi will work. What a pain.

So I cut up one of my many microUSB cables and soldered to that. Problem solved.

Everything else was simple. The MOSFET is used to turn on and off the 12V flash light. Since this uses up the only 3.3v pin, the arcade button is using the internal pull up and is wired to ground and a GPIO.

Component Test

Software

The idea is to have a way to set a text in HomeAssistant and that will make the box flash and scroll the message on the display. To do so a text input helper was needed first in HomeAssistant.

input_text:
  led_matrix_text:
    name: Message
    initial: ""
    icon: "mdi:chat"

Next an ESPHome config is needed. The code published by Richard Nauber got me quickly started, but I made a couple of adjustments.

led_matrix.yaml
# LED Matrix

substitutions:
  devicename: esp32-03
  xscrollpadding: "4" # in pix

esphome:
  name: $devicename
  platform: ESP32
  board: esp32dev
 
# Enable logging
logger:

# Enable Home Assistant API
api:

web_server:
  port: 80

ota:
  password: !secret ota_password

wifi:
  ssid: "W00t"
  password: !secret wifi_password
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${devicename} Fallback Hotspot"
    password: !secret ap_password

captive_portal:

########## Setup #######################
 

script:
  - id: start
    mode: restart
    then:
      - switch.turn_on: led_matrix_flash
      - delay: 60s
      - script.execute: stop
  - id: stop
    then:
      - script.stop: start
      - switch.turn_off: led_matrix_flash
      - homeassistant.service:
          service: input_text.set_value
          data:
            value: ""
            entity_id: input_text.led_matrix_text

text_sensor:
  - platform: homeassistant
    name: "Matrix Text"
    entity_id: input_text.led_matrix_text
    id: led_matrix_text
    internal: true
    on_value:
      - if:
          condition:
            text_sensor.state:
              id: led_matrix_text
              state: ""
          else:
            - script.execute: start

font:
  - id: tinyfont
    file: "ttf/lexis.ttf"
    size: 8
    glyphs: '''äöüß!"%()+,-_.:*=°?~#0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz'
 
 
light:
  - platform: fastled_clockless
    chipset: WS2812B
    pin: GPIO4
    num_leds: 256
    rgb_order: GRB
    name: "led_matrix"
    id: led_matrix_light
    default_transition_length: 0s
    color_correct: [50%, 50%, 50%]
    internal: true
    restore_mode: ALWAYS_ON
 
display:
  - platform: addressable_light
    id: led_matrix_display
    addressable_light_id: led_matrix_light
    width: 32
    height: 8
    pixel_mapper: |-
      if (x % 2 == 0) {
        return (x * 8) + y;
      }
      return (x * 8) + (7 - y);
    rotation: 0°
    update_interval: 200ms
    lambda: |-
          static int16_t xpos = it.get_width();
          const char * text = id(led_matrix_text).state.c_str();
 
          auto color = Color(0, 0, 255);
          int x_start, y_start;
          int width, height;
 
          it.get_text_bounds(
            0, 0, text, id(tinyfont), 
            TextAlign::TOP_LEFT,
            &x_start, &y_start, &width, &height
          ); 
 
          if(xpos < -1 * (width + $xscrollpadding)) {
            xpos = it.get_width();
          }
 
          if(width <= it.get_width()) {
            xpos = 0;
          }
 
          it.print(
            xpos,
            0, 
            id(tinyfont),
            color, 
            TextAlign::TOP_LEFT,
            text
          ); 
          xpos--;
 
switch:
  - platform: gpio
    pin: GPIO23
    name: "LED Matrix Flash"
    id: led_matrix_flash
    internal: true
    restore_mode: ALWAYS_OFF
 
binary_sensor:
  - platform: gpio
    pin:
      number: GPIO13
      inverted: true
      mode:
        input: true
        pullup: true
    name: "LED Matrix Confirm"
    id: led_matrix_confirm
    internal: true
    on_press:
      then:
        - script.execute: stop

My start and stop scripts control the flashing light and will stop the whole thing automatically after one minute. The stop script is also triggered by the button, so I can end the madness early.

I adjusted the scrolling code a bit so that words always scroll in from the right instead of weirdly starting at the left. If the message is short enough to be shown in full, no scrolling is done.

I'm using the Dogica font by Rob Mocci. It's an 8×8 pixel font, so it's perfect for the matrix display. To make the font available to ESPHome, I copied the font files to config/esphome/ttf/ via SSH.

Update: the Lexis font by Damian Vila is a 8×6 pixel font that is even better, since characters are less wide.

Hardware

With the software out of the way, it was time to put everything into a nice box.

I 3D printed this grid model off Thingiverse to have a nice separation between the pixels.

Matrix grid

For a diffuser, Kaddi had the great idea to cut up an IKEA Trofast storage container. The bandsaw made quick work of it. Strips of the same plastic were also used to keep the matrix in place later on.

Diffuser build

The front got roughly cut out of plywood on my scroll saw before a final cut and a chamfer was made on the router table. Use all the tools!

Front panel on the scroll saw

In the bottom I added two magnets. A old piece of metal got screwed on top of my homemade air filter and the magnets are used to fix the box up there. The power supply I mounted at the back of the box with some adhesive velcro.

Final product

Tags:
diy, workshop, electronics, homeassistant, esphome
Similar posts:
1)
I'm not sure if I will keep this model. I would prefer something brighter