4 channel Relay ESP8266 Wifi board WORKING with esphome

Check the relays installed on your board.
Are they 5VDC or 12VDC items?
If they are 12VDC, that is your issue.
The manufacturer placed the wrong relays on the board.

I’m building a landscape lighting controller for my back yard using this four-relay board. The board came with the Nuvoton chip, so I used the longer UART codes with preambles. The relays are switching from OFF (Normally Open) to ON to pass 12VDC from the landscape lighting transformer to each of four zones. The relay board is powered from a 12VDC-to-5VDC buck convertor module. Wiring is easy, but running the low-voltage wires around the back yard in the summer heat is the most difficult part of this project.

Parts in use:
9x6x3 Extreme Broadband Heavy Duty Weatherproof Enclosure IPE963-LTC
Four-relay ESP8266 Smart Switch Module
12V-to-5V Miniature DC-DC Convertor Power Supply Module
2 x Four-position terminal strips

The larger four-position terminal strips allow attaching multiple low-voltage wires without problem. The plastic box has foam-insulated ports on the bottom for waterproof routing of the low-voltage wires into and out of the box.

Was going to say thank you for this thread, it helped me get up and running in about 20 mins after finding out my new ESP01 flashing USB stick didn’t require anything special and just flashed it straight from ESPHome.
Going to be using it for a garage door relay so needed the momentary press:

  - platform: template
    name: 'Relay 1 Toggle'
    id: relay1tog
    turn_on_action:
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]
    optimistic: true
    on_turn_on:
      - delay: 500ms
      - switch.turn_off: relay1tog

And it works a treat. Many thanks.

1 Like
esphome:
  name: 4ch-relay

esp8266:
  board: esp01_1m


# Enable Home Assistant API
api:

ota:
  password: "*******************************"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "4Ch-Relay Fallback Hotspot"
    password: "************"

captive_portal:

# Enable Web server.
#web_server:
#  port: 80
  
time:
  - platform: homeassistant
    id: homeassistant_time

# Text sensors with general information.
text_sensor:
  # Expose ESPHome version as sensor.
  - platform: version
    name: Relay ESPHome Version
  # Expose WiFi information as sensors.
  - platform: wifi_info
    ip_address:
      name: Relay IP
    ssid:
      name: Relay Connected SSID
    bssid:
      name: Relay Connected BSSID
    mac_address:
      name: Relay Mac Wifi Address
  # Make uptime Human Readable
  - platform: template
    name: Uptime Human Readable
    id: uptime_human
    icon: mdi:clock-start

# Sensors with general information.
sensor:
  # Uptime sensor.
  - platform: uptime
    name: Relay Uptime
    id: uptime_sensor
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();

  # WiFi Signal sensor.
  - platform: wifi_signal
    name: Relay WiFi Signal
    update_interval: 60s

  - platform: adc
    pin: VCC
    name: "VCC Voltage"

# Enable logging
logger:
  baud_rate: 0 #need this to free up UART pins

uart:
  baud_rate: 115200 # speed to STC15L101EW
  tx_pin: GPIO1
  rx_pin: GPIO3

switch:
  - platform: template
    name: 'Relay 1'
    id: relay1
    turn_on_action:
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]
    optimistic: true
  - platform: template
    name: 'Relay 2'
    id: relay2
    turn_on_action:
      - uart.write: [0xA0, 0x02, 0x01, 0xA3]
    turn_off_action:
      - uart.write: [0xA0, 0x02, 0x00, 0xA2]
    optimistic: true
  - platform: template
    name: 'Relay 3'
    id: relay3
    turn_on_action:
      - uart.write: [0xA0, 0x03, 0x01, 0xA4]
    turn_off_action:
      - uart.write: [0xA0, 0x03, 0x00, 0xA3]
    optimistic: true
  - platform: template
    name: 'Relay 4'
    id: relay4
    turn_on_action:
      - uart.write: [0xA0, 0x04, 0x01, 0xA5]
    turn_off_action:
      - uart.write: [0xA0, 0x04, 0x00, 0xA4]
    optimistic: true

After amalgamating the posts and viewing the esphome docs, this is what I use.
Just wondering if the turn on and off actions could be done using the UART switch?
https://esphome.io/components/switch/uart.html?highlight=uart

3 Likes

Hi, did anyone of you manage to sort out if it’s possible to read the actual state of the relays?
Do the ESP receive anything cyclic on the RX pins or are there any register to read data from?

1 Like

I needed to toggle two relays. (one on, one off). I tried a few combinations. This is what ultimately worked. Looks like you need a delay between commands.

    turn_on_action:
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
      - delay: 10ms
      - uart.write: [0xA0, 0x02, 0x00, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x02, 0x01, 0xA3]
      - delay: 10ms
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]

It appears that “0xA0” is the sequence start, then the second byte is the Relay #, the third byte is either 0x00 or 0x01 for off/on respectively; and the last byte is the ‘sequence end’ which includes a checksum.

However, this didn’t work:

- uart.write: [0xA0, 0x01, 0x00, 0xA1, 0xA0, 0x02, 0x01, 0xA3]
- uart.write: [0xA0, 0x01, 0x01, 0xA2, 0xA0, 0x02, 0x00, 0xA2]

and neither did this:

- uart.write: [0xA0, 0x01, 0x00, 0x02, 0x01, 0xA4]
- uart.write: [0xA0, 0x01, 0x01, 0x02, 0x00, 0xA4]
1 Like

Since this post helped me so much, I also want to show you what works for me. When I want to toggle the switches, this code works wonderfully:

switch:
  - platform: template
    name: 'Stufe 1'
    id: relay1
    turn_on_action:
      - delay: 2000ms
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]
    optimistic: true
    on_turn_on:
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 10ms
      - switch.turn_off: relay4

  - platform: template
    name: 'Stufe 2'
    id: relay2
    turn_on_action:
      - delay: 2000ms
      - uart.write: [0xA0, 0x02, 0x01, 0xA3]
    turn_off_action:
      - uart.write: [0xA0, 0x02, 0x00, 0xA2]
    optimistic: true
    on_turn_on:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 10ms
      - switch.turn_off: relay4
    
  - platform: template
    name: 'Stufe 3'
    id: relay3
    turn_on_action:
      - delay: 2000ms
      - uart.write: [0xA0, 0x03, 0x01, 0xA4]
    turn_off_action:
      - uart.write: [0xA0, 0x03, 0x00, 0xA3]
    optimistic: true
    on_turn_on:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay4
      
  - platform: template
    name: 'Stufe 4'
    id: relay4
    turn_on_action:
      - delay: 2000ms
      - uart.write: [0xA0, 0x04, 0x01, 0xA5]
    turn_off_action:
      - uart.write: [0xA0, 0x04, 0x00, 0xA4]
    optimistic: true
    on_turn_on:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay3

Is there a reason you didn’t just include the turning off of the other relays as part of the turn_on_action? I was curious if you had tested that and had reason for doing it the way you did. Also, 2 seconds (2000 ms) is a seemingly long wait time for an impatient person like myself and others so I may or others may try to click again with a delay like that. Was this also tested for a reason I have not yet come across? Thanks for feedback. I put my test code below at 300 ms as that seemed to be what people were using when configuring interlock: settings, but unfortunately that isn’t available for template: types, versus switch:.

CODE SUGGESTIONS BELOW WORK, NOT EXTENSIVELY TESTED, SO I DON’T KNOW IF THERE ARE DRAWBACKS TO THIS. Seems to work well and things are more responsive that I wouldn’t click again. Tested clicking between relays quickly and didn’t seem to affect anything.

switch:
  - platform: template
    name: 'Relay 1'
    id: relay1
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 10ms
      - switch.turn_off: relay4
      - delay: 300ms
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]
    optimistic: true

  - platform: template
    name: 'Relay 2'
    id: relay2
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 10ms
      - switch.turn_off: relay4
      - delay: 300ms
      - uart.write: [0xA0, 0x02, 0x01, 0xA3]
    turn_off_action:
      - uart.write: [0xA0, 0x02, 0x00, 0xA2]
    optimistic: true
    
  - platform: template
    name: 'Relay 3'
    id: relay3
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay4
      - delay: 300ms
      - uart.write: [0xA0, 0x03, 0x01, 0xA4]
    turn_off_action:
      - uart.write: [0xA0, 0x03, 0x00, 0xA3]
    optimistic: true
      
  - platform: template
    name: 'Relay 4'
    id: relay4
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 300ms
      - uart.write: [0xA0, 0x04, 0x01, 0xA5]
    turn_off_action:
      - uart.write: [0xA0, 0x04, 0x00, 0xA4]
    optimistic: true

I anticipate any feedback from others experience or know how as to whether this is a good idea or if there are reasons not to do it this way.

THIS PAGE WAS SUPER HELPFUL FO ME AND I WANTED TO CONTRIBUTE TO OTHERS LOOKING FOR HELP. I couldn’t find complete examples but this page got me to where I needed to be so I am contributing my full example.

Here is my 4 channel relay configuration that I setup for use with a 3 speed pull chain fan conversion. The original fan was Off, Low, Medium, and High and cycled through the stages as you pulled the chain. I wanted control of selecting each individual speed, automations, power, etc. all while still keeping the pull chain functional for others to be able to operate it normally. Using the esp-01s and the attached 4 channel relay and some finagling allowed me to do this!

For transparency, I have decided not to use this and instead go with a D1Mini and a separate 4 channel relay (only need three) that has individual pins to turn on and off the relays and provide status of the relay state since each pin is individual. The configuration below does lose track of what speed the relay is on from the esphome device’s web interface when the browser page is refreshed. Not sure if Home Assistant does also, cause I didn’t check before deciding I needed more control. Might be alright there. Long story short, I couldn’t mess with not knowing what was on and what was off when I am switching three different 120VAC legs into a motor and only one can be on at a time. While I accomplished most things below, I just felt the need for more info and control. I certainly will use this in some form on a project that doesn’t have a big 'ol fan connected to it!

Anyway, I removed the original 3way (4-wire) pull chain and replaced it with a single on/off pull chain as I’ll configure the different speeds in code. I just need to know the state has changed to cycle things. I am using GOPI0 to determine if the state of the pull chain has changed. It doesn’t matter if it is On or Off really, just that it has changed. All well and good except I had a 50% chance of the chain being ON and therefore GPIO0 connected to GND at boot up and if GPIO0 was connected directly through to GND during boot the device goes to boot loader mode and doesn’t startup normal operations. To avoid this issue, I have relay4 to enable/disable the Pull chain. One leg of the pull chain is soldered to GPIO0 then to the NC of relay4 and out the NO (normally open) side of relay4 then soldered to the GND pin. Then in the code I turn relay4 on after boot automatically enabling the pull chain after the device is successfully booted. This ensures during boot that the GPIO0 and GND are not connected until after the program starts.

The turn on action for each of the speed relays first turns off the others prior to turning itself on. The functions like and interlock: but since the template platform doesn’t have that I had to manually do it. In the code below I threw in using the fan: component which I started using on my new iteration. I commented out the old code I used that I had come up with that worked but not as nice. However, I didn’t rebuild this one on the esp to confirm function, but I believe I have added it in right based off my other working model.

substitutions:
  #--- device config
  device_name: "bar-fan"
  device_disp: "Bar Fan"

packages:
  common: !include common/base_config.yaml
  wifi: !include common/base_wifi.yaml

esphome:
  name: ${device_name}
  comment: '${device_disp} controller'
  on_boot:
    priority: -10
    then:
      - switch.turn_on: relay4

esp8266:
  board: esp01_1m

# Enable logging
logger:
  baud_rate: 0 # need this to free up UART pins

uart:
  baud_rate: 115200 # speed to STC15L101EW
  tx_pin: GPIO1
  rx_pin: GPIO3

# globals:
#   id: speed
#   type: int
#   initial_value: "0" # Off: 0; Low: 1; Medium: 2; High: 3
#   restore_value: no
#
# binary_sensor:
#   - platform: gpio
#     pin: GPIO0
#     name: "${device_disp} Pull Chain"
#     internal: true
#     filters:
#       - delayed_on_off: 300ms # was 100ms; thought matching delay time was better
#     on_state:
#       then:
#         - lambda: |-
#             id(speed) = id(speed) + 1;
#             if(id(speed) > 3) {
#               id(speed) = 0;
#             }
#             if(id(speed) == 0) {
#               id(relay1).turn_off();
#               id(relay2).turn_off();
#               id(relay3).turn_off();
#             }
#             if(id(speed) == 1) {
#               id(relay1).turn_on();
#             }
#             if(id(speed) == 2) {
#               id(relay2).turn_on();
#             }
#             if(id(speed) == 3) {
#               id(relay3).turn_on();
#             }

binary_sensor:
  - platform: gpio
    name: "${device_disp} Pull Chain"
    pin: GPIO0
    internal: true
    filters:
      - delayed_on_off: 100ms
    on_state:
      then:
        - fan.cycle_speed: fan1

button:
  #--- Momentary Push Button for for testing "like pull chain"
  - platform: template
    name: "${device_disp} Button"
    internal: true
    on_press:
      then:
        - fan.cycle_speed: fan1

fan:
  - platform: speed
    output: fan_speed
    id: fan1
    name: "${device_disp}"
    icon: mdi:fan
    speed_count: 3

output:
  - platform: template
    type: float
    id: fan_speed
    write_action:
      - lambda: |-
          ESP_LOGD("fan", "Float value: %f", state);
          if(state < 0.3) {
            // Off
            id(relay1).turn_off();
            id(relay2).turn_off();
            id(relay3).turn_off();
          } else if (state >= 0.3 && state <= 0.39) {
            // Low Speed
            id(relay1).turn_on();
          } else if (state >= 0.4 && state <= 0.69) {
            // Medium Speed
            id(relay2).turn_on();
          } else if (state > 0.69) {
            // High Speed
            id(relay3).turn_on();
          }

switch:
  - platform: template
    name: "${device_disp} Relay 1"
    id: relay1
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 300ms
      - uart.write: [0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0xA0, 0x01, 0x00, 0xA1]
    optimistic: true
    # on_turn_on:
    #   - globals.set:
    #       id: speed
    #       value: "1"

  - platform: template
    name: "${device_disp} Relay 2"
    id: relay2
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay3
      - delay: 300ms
      - uart.write: [0xA0, 0x02, 0x01, 0xA3]
    turn_off_action:
      - uart.write: [0xA0, 0x02, 0x00, 0xA2]
    optimistic: true
    # on_turn_on:
    #   - globals.set:
    #       id: speed
    #       value: "2"

  - platform: template
    name: "${device_disp} Relay 3"
    id: relay3
    turn_on_action:
      - delay: 10ms
      - switch.turn_off: relay1
      - delay: 10ms
      - switch.turn_off: relay2
      - delay: 300ms
      - uart.write: [0xA0, 0x03, 0x01, 0xA4]
    turn_off_action:
      - uart.write: [0xA0, 0x03, 0x00, 0xA3]
    optimistic: true
    # on_turn_on:
    #   - globals.set:
    #       id: speed
    #       value: "3"

  - platform: template
    name: "${device_disp} Enable Pull Chain"
    id: relay4
    turn_on_action:
      - uart.write: [0xA0, 0x04, 0x01, 0xA5]
    turn_off_action:
      - uart.write: [0xA0, 0x04, 0x00, 0xA4]
    optimistic: true

1 Like

Just wanting to post this here for anyone looking for the 8-channel relay board from LC Technology. It’s from the original PDF reference file, including a table showing all the GPIO channels assigned for each relay.

Relay 1     GPIO16
Relay 2     GPIO14
Relay 3     GPIO12
Relay 4     GPIO13
Relay 5     GPIO15
Relay 6     GPIO0
Relay 7     GPIO4
Relay 8     GPIO5

Hi, thanks for this thread,
i have the 2 channel relay board that comes with esp-01 and in addition to using the relays, i would like to add 2 sensors, Dallas (i2c per my understanding) and AM2301 (DHT), has anybody managed to connect additional peripherals to the board ?
thank you !

Thank you for this, I bought one of these off AliExpress, and I was able to erase, prepare, and flash ESPHome on it, update your Yaml code and wirelessly update it.

I have control of all four relays now through Home Assistant. I very much appreciate your efforts on this. Thank you!

You seem to have solved my problem, but how did you do it? I am stuck on adding the UART codes for the Nuvoton chip. Don’t seem to get the relays to function or click. Would you like to share the code for that part?

esphome:
  name: back-yard-lights
  platform: ESP8266
  board: esp01_1m
  
#
# Disable UART logging
#
logger:
  baud_rate: 0
  level:     debug
  
#
# Config UART for STC15L101EW serial commands
#
uart:
  baud_rate: 115200
  tx_pin: GPIO1
  rx_pin: GPIO3

#
# Configure all four relays as switches
#
switch:
  - platform: template
    name: 'Relay 1'
    id: zone1
    optimistic:    true
    assumed_state: false
    restore_mode: "RESTORE_DEFAULT_OFF"
    turn_on_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x01, 0x01, 0xA2]
    turn_off_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x01, 0x00, 0xA1]

  - platform: template
    name: 'Relay 2'
    id: zone2
    optimistic:    true
    assumed_state: false
    restore_mode: "RESTORE_DEFAULT_OFF"
    turn_on_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x02, 0x01, 0xA3]
    turn_off_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x02, 0x00, 0xA2]

  - platform: template
    name: 'Relay 3'
    id: zone3
    optimistic:    true
    assumed_state: false
    restore_mode: "RESTORE_DEFAULT_OFF"
    turn_on_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x03, 0x01, 0xA4]
    turn_off_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x03, 0x00, 0xA3]

  - platform: template
    name: 'Relay 4'
    id: zone4
    optimistic:    true
    assumed_state: false
    restore_mode: "RESTORE_DEFAULT_OFF"
    turn_on_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x04, 0x01, 0xA5]
    turn_off_action:
      - uart.write: [0x0D, 0x0A, 0x2B, 0x49, 0x50, 0x44, 0x2C, 0x30, 0x2C, 0x34, 0x3A, 0xA0, 0x04, 0x00, 0xA4]

I can also say, you should check the four relays on your board. Be certain they are using either 3.3VDC or 5VDC coils. I once received a board that had 12VDC coils installed, and it would NEVER fire, simply because there was no 12VDC power supply anywhere on the board.

Thank you for answering so quickly and being so generous!!! Fantastic!!! It did not work for me though (or I have missed something else). The relay works fine with Tasmota and code from LC Technology 12V 4 Channel Relay Board (LC-ESP01-4R-12V) Configuration for Tasmota. I want to use esphome for everything and don’t seem to understand how to initiate the listening state between esp01 and the relaycard, how to translate the Tasmota command: SerialReceived#Data=41542B5253540D0A to yaml in esphome.

Can you share a close-up photograph/image of your board?
It may help identify some other difference, as in the power supply section, or the actual relays.

Also, what power supply are you using to power the board? Is it 5VDC, or 12VDC?

We are using 12V power supply for this one. We also have a couple of 4 ch relays with 5V power, who also needs this initiating code to work in Tasmota. Thank you for trying to help!

You are QUITE welcome. Yes, those are definitely 12VDC relays. Are you supplying 12VDC or 12VAC to the board, and is it responding to ESPHome log requests?


12VDC is what we use.

That’s very good to see. :slight_smile:
And have you tried using the shorter hex byte strings for the relays? The codes I provided include a ‘preamble’ of several bytes that were previously necessary for only some boards. All having to do with the specific version of firmware stored on the Nuvoton microprocessor on the relay board. There are several examples of these shorter hex bytes further up this discussion thread.