Esphome

6 minute read

ESP32

Image

TFT 1.8” display - ST7735

The ST7735 TFT display is a 1.8″ display with a resolution of 128×160 pixels and can display a wide range of colors ( full 18-bit color, 262,144 shades!). The display uses the SPI protocol for communication and has its own pixel-addressable frame buffer which means it can be used with all kinds of microcontroller and you only need 4 i/o pins. To complement the display, it also comes with an SD card slot on which colored bitmaps can be loaded and easily displayed on the screen.

Other features of the display include:

  • 1.8″ diagonal LCD TFT display
  • 128×160 resolution, 18-bit (262,144) color
  • 4 or 5 wire SPI digital interface
  • Built-in microSD slot – uses 2 more digital lines
  • 5V compatible! Use with 3.3V or 5V logic
  • Onboard 3.3V @ 150mA LDO regulator
  • 2 white LED backlight, a transistor connected so you can PWM dim the backlight
  • 1×10 header for easy breadboarding
  • 4 x 0.9″/2mm mounting holes in corners
  • Overall dimensions: 1.35″ x 2.2″ x 0.25″ (34mm x 56mm x 6.5mm)
  • Current draw is based on LED backlight usage: with full backlight draw is ~50mA

The goal of this tutorial is to demonstrate the abilities of the TFT to display images and text in different colors and some animation.

Sensors

mmwave sensors

2410 can measure both static and moving targets. 2420 can only measure moving and slightly moving targets. This is a comparison diagram (in Chinese). You may use google translate.

Image

2410, 2410B, 2410C, 2410S can all detect static targets, but not 2420. 2420 also dont have BT, dont have light sensor, and dont have OTA.

Top to down:

  • Model:
  • Image:
  • Usage: (all can detect static targets except 2420)
  • Dimension:
  • Voltage:
  • Avg current:
  • Effective distance:
  • Effective angle: (±60 for all)
  • Configuration method: (all UART, use app if has BT)
  • Bluetooth: (only 2410B and C has)
  • Light sensor: (only 2410B and C has)
  • OTA upgrade: (only 2410 and 2420 cannot)
  • Installation method: (Support wall and ceiling mount)

Pinout

  • LED - 3.3v
  • SCK - D13
  • SDA - D11
  • DC - D9
  • Reset - D8
  • CS - D10
  • GND - GND
  • VCC - 5v

https://www.youtube.com/watch?v=boagCpb6DgY

Sample code

esphome:
  name: wind
  # Set inital value for idle timer
  on_boot:
    priority: -100
    then:
      - lambda: |-
          id(idle_time) = id(my_time).now().timestamp;
    
esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

ota:
  password: "8aebd6eb0427929de523046d6773d66e"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Wind Fallback Hotspot"
    password: "sh1mZkRvphaJ"

captive_portal:

# All sensors sourced from HA - Ecowitt weather station sensors
text_sensor:
  - platform: homeassistant
    name: "Current Temp"
    entity_id: sensor.weather_station_temperature
    id: temp

  - platform: homeassistant
    name: "Current Wind Dir"
    entity_id: sensor.wind_direction
    id: wind_dir

  - platform: homeassistant
    name: "Current Wind Speed"
    entity_id: sensor.wind_speed
    id: wind_speed

  - platform: homeassistant
    name: "Named Wind Dir"
    entity_id: sensor.named_wind_direction
    id: named_dir
    
  - platform: homeassistant
    name: "Rain since 12am"
    entity_id: sensor.rain_accumulation
    id: total_rain

  - platform: homeassistant
    name: "Pressure MSL hPa"
    entity_id: sensor.atmospheric_pressure_msl
    id: pressure
    
  - platform: homeassistant
    name: "Wind gust"
    entity_id: sensor.wind_gust
    id: gust

# Stores timestamp of last motion detect    
globals:
  - id: idle_time
    type: long

font:
  - file: "fonts/calibri.ttf"
    id: calibri_20
    size: 20

  - file: "fonts/calibri.ttf"
    id: calibri_25
    size: 25
    
  - file: "fonts/calibri.ttf"
    id: calibri_40
    size: 40
    
time:
  - platform: homeassistant
    id: my_time

    on_time:
      # Every 1 minute check if no presence trigger for 5 min
      - seconds: 0
        minutes: /1
        then:
          - if:
              # more than 5 min with no motion? Exercise display then turn off backlight
              condition:
                lambda: |-
                  return id(my_time).now().timestamp-id(idle_time) > 300;
              then:
                - display.page.show: page2
                - delay: 1s
                - display.page.show: page3
                - delay: 1s
                - display.page.show: page4
                - delay: 1s
                - display.page.show: page5
                - light.turn_off: back_light
                - delay: 1s
                - display.page.show: page1

# PIR on GPIO33
binary_sensor:
  - platform: gpio
    pin: GPIO33
    id: pir
    device_class: motion
    # turn on display on motion detect   
    # store timestamp of last detection event
    on_press:
      then:
        - lambda: |-
            id(idle_time) = id(my_time).now().timestamp;
        - display.page.show: page1
        - light.turn_on: back_light
    on_release:
      then:
        - lambda: |-
            id(idle_time) = id(my_time).now().timestamp;

# backlight pin as PWM GPIO32
output:
  - platform: ledc
    pin: 32
    id: gpio_32_backlight_pwm

# define as light
light:
  - platform: monochromatic
    output: gpio_32_backlight_pwm
    name: "ILI9341 Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON
    
# 2.2" TFT display 320x240, portrait orientation
display:
  - platform: ili9341
    rotation: 0
    model: TFT 2.4
    dc_pin: GPIO21
    cs_pin: GPIO22
    led_pin: GPIO32
    reset_pin: GPIO17
    
    # Page 1 - main display.  Other pages or for anti burn-in
    pages:
      - id: page1
        lambda: |-
          // Colours - used for visiual indication of wind strength
          auto red = Color(255, 0, 0);
          auto green = Color(0, 255, 0);
          auto light_blue = Color(135, 237, 232);
          auto orange = Color(255, 170, 43);
          auto white = Color(255, 255, 255);
          auto purple = Color(97, 15, 219);
          auto grey = Color(100, 135, 135);
          // history arrays for trend display
          static float hist_wind[30];
          static int hist_dir[30];
          static int index=0;
          static int prev_index=29;
          // Convert wind direction to int, speed to float
          int dir = atoi(id(wind_dir).state.c_str());
          float speed = atof(id(wind_speed).state.c_str());
          // Calculate xy position to plot wind indicator
          int x = 120 + (90 * (cos((dir-90)*PI/180)));
          int y = 150 + (90 * (sin((dir-90)*PI/180)));
          // Store in array
          if (hist_wind[prev_index]!=speed || hist_dir[prev_index]!=dir) {
            hist_wind[index] = speed;
            hist_dir[index] = dir;
            index += 1;
            prev_index += 1;
            if (index==30) { index = 0; }
            if (prev_index==30) { prev_index = 0; }
          }
          // Trend maximum for last 30
          float max = hist_wind[0];
          for (size_t i = 0; i < 30; ++i) {
            if (hist_wind[i] > max) {
              max = hist_wind[i];
            }
          }
          // Weather data
          it.strftime(5, 10, id(calibri_25), TextAlign::TOP_LEFT, "%H:%M", id(my_time).now());
          it.printf(235, 10, id(calibri_25), TextAlign::TOP_RIGHT, "%s °C", id(temp).state.c_str());
          it.printf(5, 290, id(calibri_20), TextAlign::BOTTOM_LEFT, "G %s km/h", id(gust).state.c_str());
          it.printf(5, 310, id(calibri_20), TextAlign::BOTTOM_LEFT, "TM %2.1f km/h", max);
          it.printf(235, 290, id(calibri_20), TextAlign::BOTTOM_RIGHT, "%s mm", id(total_rain).state.c_str());
          it.printf(235, 310, id(calibri_20), TextAlign::BOTTOM_RIGHT, "%s hPa", id(pressure).state.c_str());
          // Display trend data
          for (size_t i = 0; i < 30; ++i) {
            if (hist_wind[i]>0) {
              int a = 120 + ((90-(70*hist_wind[i]/max)) * (cos((hist_dir[i]-90)*PI/180)));
              int b = 150 + ((90-(70*hist_wind[i]/max)) * (sin((hist_dir[i]-90)*PI/180)));
              int c = 120 + (90 * (cos((hist_dir[i]-90)*PI/180)));
              int d = 150 + (90 * (sin((hist_dir[i]-90)*PI/180)));
              it.line(a, b, c, d, grey);
            }
          }
          // Compass rose
          it.circle(120, 150, 90);
          it.print(120, 50, id(calibri_20), red, TextAlign::BOTTOM_CENTER, "N");
          it.print(120, 250, id(calibri_20), red, TextAlign::TOP_CENTER, "S");
          it.print(20, 150, id(calibri_20), red, TextAlign::CENTER_RIGHT, "W");
          it.print(220, 150, id(calibri_20), red, TextAlign::CENTER_LEFT, "E");
          // Wind pointer
          if (speed<=11) { it.filled_circle(x, y, 7, light_blue); }
            else {
          if (speed>11 && speed<=30) { it.filled_circle(x, y, 7, green); } 
            else {
          if (speed>30 && speed<=60) { it.filled_circle(x, y, 7, orange); } 
            else {
          if (speed>60) { it.filled_circle(x, y, 7, red); } 
            } } }
          // Wind details
          it.printf(120, 130, id(calibri_25), TextAlign::BOTTOM_CENTER, "%s", id(named_dir).state.c_str());
          if (speed<10) { it.printf(120, 175, id(calibri_40), TextAlign::BOTTOM_CENTER, "%2.1f", speed); }
            else { it.printf(120, 175, id(calibri_40), TextAlign::BOTTOM_CENTER, "%2.0f", round(speed));   
          }
          it.print(120, 175, id(calibri_20), TextAlign::TOP_CENTER, "km/h");
          
      - id: page2
        lambda: |-
          // fill screen red
          auto red = Color(255, 0, 0);
          it.fill(red);
          
      - id: page3
        lambda: |-
          // Fill screen green
          auto green = Color(0, 255, 0);
          it.fill(green);
          
      - id: page4
        lambda: |-
          // Fill screen white
          auto white = Color(255, 255, 255);
          it.fill(white);
          
      - id: page5
        lambda: |-
          it.fill(COLOR_OFF);

Source: https://community.home-assistant.io/t/mini-weather-station-display-esphome-updated-for-3-2-touchscreen-with-bom-forecast/390606

Updated:

Comments