Water Level Sensor does not work with ESPHome?

I am trying to use the “Grove Water Level Sensor” with an ESP8266 (and tried ESP32) and ESPHome and this doesn’t seem to work. I have connected the SDA/SDL to D1/D2 (used no resistors and 4k7 ones) on my ESP8266 and the code detects the address “0x77” during the I2C scan. This means the sensor is detected.

What level of water i try, i always get 0% or 5% in my output. For 30-35% i see the following bytes read:
[09:51:10][V][custom:033]: low 8 sections value = 222.0.0.0.0.0.249.0
[09:51:10][V][custom:035]: high 12 sections value = 249.249.249.249.249.248.0.0.0.0.0.0
[09:51:10][V][custom:049]: touch_val = 16193
[09:51:10][V][custom:057]: water level = 5

I use the following code in ESPHome (mostly copy-paste from the example):

 #include "esphome.h"

 // https://wiki.seeedstudio.com/Grove-Water-Level-Sensor/
 unsigned char low_data[8] = {0};
 unsigned char high_data[12] = {0};

 int sensorvalue_min = 250;
 int sensorvalue_max = 255;

 #define NO_TOUCH            0xFE
 #define THRESHOLD           100
 #define ATTINY1_HIGH_ADDR   0x78
 #define ATTINY2_LOW_ADDR    0x77

 class WaterLevelSensor : public PollingComponent, public Sensor {
 public:
  WaterLevelSensor() : PollingComponent(15000) {}

  void setup() override {
    // Initialize the device here. Usually Wire.begin() will be called in here,
    // though that call is unnecessary if you have an 'i2c:' entry in your config

    ESP_LOGV("custom", "in-setup");
    Wire.begin();
  }
  void update() override {
    uint32_t touch_val = 0;
    uint8_t trig_section = 0;

    getLow8SectionValue();
    getHigh12SectionValue();

    ESP_LOGV("custom", "low 8 sections value = %d.%d.%d.%d.%d.%d.%d.%d", low_data[0], low_data[1], low_data[2], low_data[3], low_dat                                                                                                         a[4], low_data[5], low_data[6], low_data[7]);

    ESP_LOGV("custom", "high 12 sections value = %d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d", high_data[0], high_data[1], high_data[2], hig                                                                                                         h_data[3], high_data[4], high_data[5], high_data[6], high_data[7], high_data[8], high_data[9], high_data[10], high_data[11]);

    for (int i = 0 ; i < 8; i++) {
      if (low_data[i] > THRESHOLD) {
        touch_val |= 1 << i;
      }
    }

    for (int i = 0 ; i < 12; i++) {
      if (high_data[i] > THRESHOLD) {
        touch_val |= (uint32_t)1 << (8 + i);
      }
    }

    ESP_LOGV("custom", "touch_val = %d", touch_val);

    while (touch_val & 0x01)
    {
      trig_section++;
      touch_val >>= 1;
    }

    ESP_LOGV("custom", "water level = %d", trig_section * 5);
    publish_state(trig_section * 5);
  }

  void getLow8SectionValue(void)
  {
    memset(low_data, 0, sizeof(low_data));
    Wire.requestFrom(ATTINY2_LOW_ADDR, 8);

    while (8 != Wire.available());

    for (int i = 0; i < 8 ; i++) {
      low_data[i] = Wire.read(); // receive a byte as character
    }
    delay(10);
  }

  void getHigh12SectionValue(void)
  {
    memset(high_data, 0, sizeof(high_data));
    Wire.requestFrom(ATTINY1_HIGH_ADDR, 12);

    while (12 != Wire.available());

    for (int i = 0; i < 12; i++) {
      high_data[i] = Wire.read();
    }
    delay(10);
  }

 };

ESPHome ? what’s that ?

The easy way of using ESP8266/ESP32:

I haven’t tested it on the ESP platform, and it’s theoretically fine if the connection is correct.

In theory it should, but I tried all possible ways … but the water level sensor just doesn’t work reliable. ESPHome is using Arduino under the hood (so the software should be good).

I believe I connected the it correctly:
VCC -> 3.3V
GND -> GND
D1 -> D0 (ESP8266) - with 4.7k resistor connected to the VCC
D2 -> D1 (ESP8266) - with 4.7k resistor connected to the VCC
Using the following code:

Please let me know if I wired it correctly … If not, I will try again otherwise the water sensors can go in the garbage bin :frowning: It is a pity, the Grove sensor had huge potential.

I’m not familiar with ESP8266, but I think you can check the electrical signals on the logic analyzer.

Thanks for the reply, only I don’t have an analyzer to check it.

It is a pity, this makes the Grove platform completely useless for me, because it cannot easily integrate with home automation (like Home Assistant). I thought it had potential (even it is slightly more expensive then other Chinese parts). Sorry i had to waste100+ dollar to figure this out :frowning:

#include “esphome.h”
#include <Wire.h>

// Grove - Water Level Sensor - Seeed Wiki
unsigned char low_data[8] = {0};
unsigned char high_data[12] = {0};

int sensorvalue_min = 250;
int sensorvalue_max = 255;

#define NO_TOUCH 0xFE
#define THRESHOLD 100
#define ATTINY1_HIGH_ADDR 0x78
#define ATTINY2_LOW_ADDR 0x77

class WaterLevelSensor : public PollingComponent, public Sensor {
public:
// constructor
WaterLevelSensor() : PollingComponent(15000) {}

float get_setup_priority() const override { return esphome::setup_priority::HARDWARE; }

void setup() override {
// Initialize the device here. Usually Wire.begin() will be called in here,
// though that call is unnecessary if you have an ‘i2c:’ entry in your config

ESP_LOGV("custom", "in-setup");
Wire.begin();

}
void update() override {
uint32_t touch_val = 0;
uint8_t trig_section = 0;

getLow8SectionValue();
getHigh12SectionValue();

ESP_LOGV("custom", "low 8 sections value = %d.%d.%d.%d.%d.%d.%d.%d", low_data[0], low_data[1], low_data[2], low_data[3], low_dat                                                                                                         a[4], low_data[5], low_data[6], low_data[7]);

ESP_LOGV("custom", "high 12 sections value = %d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d", high_data[0], high_data[1], high_data[2], hig                                                                                                         h_data[3], high_data[4], high_data[5], high_data[6], high_data[7], high_data[8], high_data[9], high_data[10], high_data[11]);

for (int i = 0 ; i < 8; i++) {
  if (low_data[i] > THRESHOLD) {
    touch_val |= 1 << i;
  }
}

for (int i = 0 ; i < 12; i++) {
  if (high_data[i] > THRESHOLD) {
    touch_val |= (uint32_t)1 << (8 + i);
  }
}

ESP_LOGV("custom", "touch_val = %d", touch_val);

while (touch_val & 0x01)
{
  trig_section++;
  touch_val >>= 1;
}

ESP_LOGV("custom", "water level = %d", trig_section * 5);
publish_state(trig_section * 5);

}

void getLow8SectionValue(void)
{
memset(low_data, 0, sizeof(low_data));
Wire.requestFrom(ATTINY2_LOW_ADDR, 8);

while (8 != Wire.available());

for (int i = 0; i < 8 ; i++) {
  low_data[i] = Wire.read(); // receive a byte as character
}
delay(10);

}

void getHigh12SectionValue(void)
{
memset(high_data, 0, sizeof(high_data));
Wire.requestFrom(ATTINY1_HIGH_ADDR, 12);

while (12 != Wire.available());

for (int i = 0; i < 12; i++) {
  high_data[i] = Wire.read();
}
delay(10);

}

};

@Bengisoz i see you are using ESPhome, do you also have a sample of the YAML file?

This is what you need to add to yaml file.

I2C For Sensor

i2c:

  • id: bus_a

    sda: 21

    scl: 22

    scan: true

End of I2C For Sensor

All Sensors

sensor:

Start of Custom Sensor

  • platform: custom

    lambda: |-

    auto my_sensor = new WaterLevelSensor();

    App.register_component(my_sensor);

    return {my_sensor};

    sensors:

    name: “Water Level Sensor”

    unit_of_measurement: “%”

    accuracy_decimals: 1