ESP32 with BH1750 is suddenly restarting or turn on all 6 relays

Hello,

I am running a esp32 as standalone device without wifi and HA which it is controlled by a lumen sensor BH1750 and at some intensity light should open or turn off a relay. The problem occurs now is that once reach all 6 relays on and light intensity drop under a certain threshold, the relays remains on instead turning off if out of their operating range. Cable length used for sensor is RJ45 at 10m long (blue/green for SDA/SCL, orange/brown for 5V/GND). ESP32 is mounted in a ESP32 socket an RJ45 cables connected to screws, well tightened.

Previous problems was that it was restarting often due no wifi, but I read that need to deactivated (I have just commented wifi module) and somehow this went fixed. Also sometimes esp32 enter in safe mode and it restarts 10 times which may trigger relays and blow my devices pcb.

A poor solution is to restart device itself at 20 minutes, but every relay is connected to a device which may got damaged when restart occurs 3-4 times an hour, but may be a code problem, an electrical connection, esp32 itself or a bit of everything.

Below I have attached entire code which runs on_value_range, and previously tested using scripts, but unsuccessful as well.

  name: lumen-lights-3
  friendly_name: Lumen_lights_3
  name_sensor: lumen_3_sensor


esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.12.0
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: '2.3'
  
  on_boot:
    then:
      - switch.turn_off: relay1
      - switch.turn_off: relay2
      - switch.turn_off: relay3
      - switch.turn_off: relay4
      - switch.turn_off: relay5
      - switch.turn_off: relay6
      - delay: 10s
      - wait_until: 
          condition:
              lambda: return id(${name_sensor}).state > 5;

esp32:
  board: esp32dev
  framework:
    type: esp-idf
    #type: arduino
    version: recommended

# Enable logging
logger:

# Enable Home Assistant API
# api:
#   encryption:
#     key: ""

# # Allow Over-The-Air updates
# ota:
# - platform: esphome
#   password: ""

# # Allow provisioning Wi-Fi via serial
# improv_serial:

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

#   # Enable fallback hotspot (captive portal) in case wifi connection fails
#   ap:
#     ssid: !secret hotspot_ssid
#     password: !secret hotspot_password
# # In combination with the `ap` this allows the user
# # to provision wifi credentials to the device via WiFi AP.
# captive_portal:

# dashboard_import:
#   package_import_url: github://esphome/example-configs/esphome-web/esp32.yaml@main
#   import_full_config: true


# # To have a "next url" for improv serial
# web_server:
#   port: 80
#   auth:
#     username: admin
#     password: admin

i2c: 
  - id: bus_a       #bh1750
    sda: GPIO21
    scl: GPIO22
    scan: true
    frequency: 400kHz

sensor:
  # - platform: wifi_signal
  #   name: "${name_sensor} WiFi Signal"
  #   id: ${name_sensor}_wifi
  #   update_interval: 15s
  #   filters:
  #     - sliding_window_moving_average:
  #         window_size: 10
  #         send_every: 10
  #         send_first_at: 4
  #   on_value:
  #     if:
  #       condition:
  #         not:
  #           wifi.connected:
  #       then:
  #         - delay: 120s
  #         - logger.log: WiFi is not connected!
  #         - wifi.disable:
  
  - platform: bh1750
    i2c_id: bus_a
    name: ${friendly_name}
    address: 0x23
    update_interval: 1s
    id: ${name_sensor}
    accuracy_decimals: 0
    filters:
      - skip_initial: 3
      - median:
          window_size: 9
          send_every: 5
          send_first_at: 3

      # - delta: 0.1
      - debounce: 500ms

      ##- throttle: 500ms
      ##- heartbeat: 2s


    on_value_range:
      - below: 9999.9
        then:
        - switch.turn_off: relay1
        - switch.turn_off: relay2
        - switch.turn_off: relay3
        - switch.turn_off: relay4
        - switch.turn_off: relay5
        - switch.turn_off: relay6

      - below: 19999.9
        above: 10000.0
        then:
          - delay: 0.5s
          - switch.turn_on: relay1
          - switch.turn_off: relay2
          - switch.turn_off: relay3
          - switch.turn_off: relay4
          - switch.turn_off: relay5
          - switch.turn_off: relay6

      - below: 39999.9
        above: 20000.0
        then:
          - delay: 0.5s
          - switch.turn_on: relay1
          - delay: 0.5s
          - switch.turn_on: relay2
          - switch.turn_off: relay3
          - switch.turn_off: relay4
          - switch.turn_off: relay5
          - switch.turn_off: relay6

      - below: 69999.9
        above: 40000
        then:
          - delay: 0.5s
          - switch.turn_on: relay1
          - delay: 0.5s
          - switch.turn_on: relay2
          - delay: 0.5s
          - switch.turn_on: relay3
          - switch.turn_off: relay4
          - switch.turn_off: relay5
          - switch.turn_off: relay6

      - below: 89999.9
        above: 70000.0
        then:
          - delay: 0.5s
          - switch.turn_on: relay1
          - delay: 0.5s
          - switch.turn_on: relay2
          - delay: 0.5s
          - switch.turn_on: relay3
          - delay: 0.5s
          - switch.turn_on: relay4
          - switch.turn_off: relay5
          - switch.turn_off: relay6

      - below: 109999.9
        above: 90000.0
        then:
          - delay: 0.5s
          - switch.turn_on: relay1
          - delay: 0.5s
          - switch.turn_on: relay2
          - delay: 0.5s
          - switch.turn_on: relay3
          - delay: 0.5s
          - switch.turn_on: relay4
          - delay: 0.5s
          - switch.turn_on: relay5
          - switch.turn_off: relay6
      - above: 110000.0
        then:
          - delay: 0.5s
          - switch.turn_on: relay1
          - delay: 0.5s
          - switch.turn_on: relay2
          - delay: 0.5s
          - switch.turn_on: relay3
          - delay: 0.5s
          - switch.turn_on: relay4
          - delay: 0.5s
          - switch.turn_on: relay5
          - delay: 0.5s
          - switch.turn_on: relay6
      


# script:
#   - id: relay_all_off
#     then:
#       - switch.turn_off: relay1
#       - switch.turn_off: relay2
#       - switch.turn_off: relay3
#       - switch.turn_off: relay4
#       - switch.turn_off: relay5
#       - switch.turn_off: relay6

#   - id: ssr_1
#     then:
#       - switch.turn_off: relay2
#       - switch.turn_off: relay3
#       - switch.turn_off: relay4
#       - switch.turn_off: relay5
#       - switch.turn_off: relay6
#       - delay: 0.5s
#       - switch.turn_on: relay1


#   - id: ssr_2
#     then:
#       - switch.turn_off: relay3
#       - switch.turn_off: relay4
#       - switch.turn_off: relay5
#       - switch.turn_off: relay6
#       - delay: 0.5s
#       - switch.turn_on: relay1
#       - delay: 0.5s
#       - switch.turn_on: relay2

#   - id: ssr_3
#     then:
#       - switch.turn_off: relay4
#       - switch.turn_off: relay5
#       - switch.turn_off: relay6
#       - delay: 0.5s
#       - switch.turn_on: relay1
#       - delay: 0.5s
#       - switch.turn_on: relay2
#       - delay: 0.5s
#       - switch.turn_on: relay3

#   - id: ssr_4
#     then:
#       - switch.turn_off: relay5
#       - switch.turn_off: relay6
#       - delay: 0.5s
#       - switch.turn_on: relay1
#       - delay: 0.5s
#       - switch.turn_on: relay2
#       - delay: 0.5s
#       - switch.turn_on: relay3
#       - delay: 0.5s
#       - switch.turn_on: relay4

#   - id: ssr_5
#     then:
#       - switch.turn_off: relay6
#       - delay: 0.5s
#       - switch.turn_on: relay1
#       - delay: 0.5s
#       - switch.turn_on: relay2
#       - delay: 0.5s
#       - switch.turn_on: relay3
#       - delay: 0.5s
#       - switch.turn_on: relay4
#       - delay: 0.5s
#       - switch.turn_on: relay5


#   - id: ssr_6
#     then:
#       - delay: 0.5s
#       - switch.turn_on: relay1
#       - delay: 0.5s
#       - switch.turn_on: relay2
#       - delay: 0.5s
#       - switch.turn_on: relay3
#       - delay: 0.5s
#       - switch.turn_on: relay4
#       - delay: 0.5s
#       - switch.turn_on: relay5
#       - delay: 0.5s
#       - switch.turn_on: relay6


# 8 relay outputs, exposed as switches in Home Assistant
switch:
  - platform: gpio
    pin: 
      number: GPIO32
      inverted: True
    name: Relay1
    id: relay1
    restore_mode: RESTORE_DEFAULT_OFF

  - platform: gpio
    pin: 
      number: GPIO14
      inverted: True
    name: Relay2
    id: relay2
    restore_mode: RESTORE_DEFAULT_OFF
    
  - platform: gpio
    pin: 
      number: GPIO27
      inverted: True
    name: Relay3
    id: relay3
    restore_mode: RESTORE_DEFAULT_OFF

  - platform: gpio
    pin: 
      number: GPIO26
      inverted: True
    name: Relay4
    id: relay4
    restore_mode: RESTORE_DEFAULT_OFF

  - platform: gpio
    pin: 
      number: GPIO25
      inverted: True
    name: Relay5
    id: relay5
    restore_mode: RESTORE_DEFAULT_OFF

  - platform: gpio
    pin: 
      number: GPIO33
      inverted: True
    name: Relay6
    id: relay6
    restore_mode: RESTORE_DEFAULT_OFF

  ## - platform: gpio
  ##   pin: 
  ##     number: GPIO19
  ##     inverted: True
  ##   name: Relay7
  ##   id: relay7
  ##   restore_mode: RESTORE_DEFAULT_OFF

  ## - platform: gpio
  ##   pin: 
  ##     number: GPIO18
  ##     inverted: True
  ##   name: Relay8
  ##   id: relay8
  ##   restore_mode: RESTORE_DEFAULT_OFF

  # - platform: template
  #   name: Logic controller
  #   optimistic: True
  #   lambda: |-
  #     if (id(${name_sensor}).state < 9999.9) {
  #       id(relay_all_off).execute();
  #       return true;
  #     } 
  #     else if (id(${name_sensor}).state > 10000.0 && id(${name_sensor}).state < 19999.9) {
  #       id(ssr_1).execute();
  #       return true;
  #     }
  #     else if (id(${name_sensor}).state > 20000.0 && id(${name_sensor}).state < 39999.9) {
  #       id(ssr_2).execute();
  #       return true;
  #     }
  #     else if (id(${name_sensor}).state > 40000.0 && id(${name_sensor}).state < 69999.9) {
  #       id(ssr_3).execute();
  #       return true;
  #     }
  #     else if (id(${name_sensor}).state > 70000.0 && id(${name_sensor}).state < 89999.9) {
  #       id(ssr_4).execute();
  #       return true;
  #     }
  #     else if (id(${name_sensor}).state > 90000.0 && id(${name_sensor}).state < 109999.9) {
  #       id(ssr_5).execute();
  #       return true;
  #     }
  #     else if (id(${name_sensor}).state > 110000.0) {
  #       id(ssr_6).execute();
  #       return true;
  #     }
  #     else {
  #       id(relay_all_off).execute();
  #       return false;
  #     }

Can you post your logs? Specifically for when your sensor reading drops below 10000.

What relays? 5V relay module configured incorrectly perhaps?
Post a link or image.

That’s way too long for I2C to work reliably. You should use active terminator.

listen to @Karosm
i2c stands for inter-integrated circuit communications. I know of no PC boards that are measured in meters (unless they are significantly less than 1).

Running it at 400kHz is guaranteed to make it not work.

  1. Move your esp32 close to your sensor. I don’t trust i2c more than a meter or so. Others have been successful with longer distances.
  2. I have heard people use CAT 6 cable with some success: GitHub - tronikos/esphome-magnetometer-water-gas-meter: Using ESP8266 or ESP32 and QMC5883L or HMC5883L, a triple-axis magnetometer, to read your water meter or gas meter (note the 50kHz i2c speed, for your sensor you could drop it even lower, since it doesn’t need much data).
  3. You can use long wires on relays and/or the things they control. I have a few hundred meters of wire to my well pump and it works very well. The wire was expensive, because it is heavy gauge to handle the current.
  4. Not sure what you are doing with your relays, but I suspect you might run into some problems if the light level changes quickly and you are in the middle of the long sequence you have in some places. I would probably structure that differently.
  5. If you explain what you are trying to do with the YAML you have (what problem you are trying to solve with the solution that currently is not working) you might get some better ways to achieve it.

I do not have any logs as esp32 runs in a place without wifi and it will never have wifi as need to act as a standalone device.

How it works:

  1. The device cannot stay near sensor, it need to stay inside while sensor should stay on top of the roof. The esp32 action relays and each relay action a SSR with AC 220V. I do this because each SSR may run up to 3kW each, so relays cannot be used due overvoltage, overcurent, socket is too small and so on.

The problems:

  1. I encounter an issue when sensor read for e.g. 120.000 lumens, turn on 6 relays, but if decrease to 75.000 lumens due some clouds, it should have only 4 relays, but sometimes remains on 6 of them. Either sensor signal is bad and still it is set to 120k lumens either some esp32 bugs.

  2. The device is plugged in the morning and unplugged in the evening when no sun, so next morning (sometimes) the esp32 enters in safe mode and restart 10 times, then run ok, but in this time may turn on/off relays and potentially damage devices (I will explain below).

  3. Sensor not reading data. Well, I had run before i2c with default frequency then 10K and increased to 100k, 300k and now 400k. I will test it with different values. I have used same type of pair in RJ45 as different length of pair may delay signal, so in this case I do not think it is about wrong pairs. Sensor works with 3V-5V so I have used 5V instead.

Things I have improved:

  1. Why do I use relays and not something else? I am aware that every relay may consume up to 70-80mA per relay, but 6 relays can take 500mA + esp32 + sensor, so esp32 is loaded via usb with 5V 2A as it may be enough.
  2. For filters sensor I have decreased window_size from 10 to 9, changed from avg to median, using debounce.

Possible solutions:

  1. @neel-m: do you reckon to use other pins than 21 and 22? TX and RX but how sensor will recognize it?
  2. @Karosm - said “You should use active terminator.”, but I need some more info about it as I understand now that 10m cable is not realiable in reading correct data from sensor.

Thank you.

Do you have a link for that?

Also, use for example ethernet cable. Both SDA and SCL paired with GND conductor (twisted pairs).

1 Like

Please don’t modify your previous posts, it’s making the whole topic chaotic!

I got some misspelling and I have reorganized.

Correcting misspelling is totally ok.
You modified the post 4 times, lot of misspelling… :grinning:

Sorry for doing the edits, but I realized that some sentences were before others, I made categories and so on. For future long posts I will write text in sublime then paste it in here.

To have any real hope of figuring out what is really going will likely require logs. You do not need Wi-Fi to get logs. You can get them via UART over the USB connection to the esp.

The logs will help you figure out if the problem is i2c communication (likely), the complicated relay code (possibly), both (quite likely), or something no one is even thinking of yet (possible).

10m is a VERY long distance for i2c especially at 400kHz. The datasheet for the sensor you are using doesn’t list a minimum frequency for i2c. I would reduce it to 1kHz. You only need a few bytes, so speed is not so important.

1 Like

Can you go so low with esphome?

probably not. The lower speed might or might not work better, since the transitions will still not be clean edges. It is a generally bad idea to run i2c for long distances, but people do things that other people consider bad every day. It’s not a problem until it is one.

And most likely at certain point it is…

1 Like

Thank you for your solution. I will try it next days to add 1khz if it will be working.

Previously I was thinking that i2c communication might be the problem in long distances as I have tested the device with 20cm cables and having wifi on with logs in HA and run for days with no issues; logs were showing on/off.

I have checked the price of active terminator and it seems to be pricey for now.
I rather think to add esp32 close to light sensor and control relays through RJ45, 6 for signal and 2 as VCC/GND. But any updates I will have to do that means to go on top of the roof :)) (not so reliable).

About the code, indeed may look repetitive and/or messy, but I am thinking to rewrite it in micropython and control the flow slightly differently that I have understood that ‘on_value_range’ works.

why? what will that help?

I looked closer at your code. It is not trivial to follow, but it does look like my original concern is not an issue. You sample the lux sensor every second, but only update every 5s, so your on_update action can only happen every 5s and your longest action is about 3s. That should be okay.

I believe your issue is caused by the sensor failing to update. Logging would tell you this for sure.

Just tested: yes, it will, although manual says 10kHz minimum. But, i guess those values are just examples…
I have some sensors on similar cable length and i use 10kHz; i tried default 50kHz and it also works, but “just in case” i put it on 10kHz. I didn’t try higher frequencies, more or less because i suspect that it won’t work (reliably).
Note however that power supply is very important. I’ve had my sensors on 3.3V at first and they tend to drop occasionally, so i put them on 5V and now it’s ok. But i have sensors which have 5–>3.3V regulator on-board, so i can choose which voltage to use. If your sensors don’t have it i suggest selecting 5V and then manually add voltage regulator at point where sensors are.

Or, at least add an electrolytic capacitor at sensor side - around 470uF will do fine, although bigger doesn’t hurt. It won’t rise voltage if it’s too low, but it will prevent voltage drop in spikes when sensors draw more current.

1 Like