Blog

  • HOWTO: login via ssh into home assistant’s esphome container

    I work a lot on esphome configs, I’m often doing this via ssh.
    I got annoyed that I always have to ssh to home assistant, search for the container’s name, login to that, switch to /config/esphome.

    So here’s a script that does all of that, the home assistant host is called “ha” in my network:

    #!/bin/bash
    HOMEASSISTANT="ha"
    ESPHOME_DOCKER_CONTAINER=$(ssh $HOMEASSISTANT 'docker container list|grep esphome'|awk '{print $10}' -)
    ssh -t $HOMEASSISTANT "docker exec -it $ESPHOME_DOCKER_CONTAINER bash -c 'cd /config/esphome && exec bash'"                                                                                                                                                                                                                                                                                                                                                                                                                                                                     

    As esphome tends to hang up from time to time in my setup, I wrote a script to restart the esphome container too:

    #!/bin/bash
    HOMEASSISTANT="ha"
    ESPHOME_DOCKER_CONTAINER=$(ssh $HOMEASSISTANT 'docker container list|grep esphome'|awk '{print $10}' -)
    ssh -t $HOMEASSISTANT "docker container restart $ESPHOME_DOCKER_CONTAINER"
  • A beelogger on ESP32 basis, part 7: Test setup: The scale part 2

    I built four bee-hive scales according to the great instructions of the Beelogger project.

    As the HX711-breakout-boards are not suitable for setups with 3.3V, I’m trying it with a NAU7802-breakout-board now.
    It doesn’t have problems when being driven with 3.3V and should be more precise than the HX711.
    As an additional feature it has an internal temperature sensor which could help compensating the temperature drift, sadly its esphome-component doesn’t support it (yet).
    According to the documentation it has an external calibration mode that may only be used without load, so it cannot be used for our use case.
    It also has an internal calibration mechanism that can be used with load, it somehow uses the internal temperature sensors’ values, sadly the datasheet doesn’t say anything about how it really works.
    The result of the internal calibration is getting read back by the esphome-component as it just sets the offset and the gain.

    The breakout-board I bought only exposes one ADC channel, Adafruit has an updated version since June 23rd, 2025 that exposes both channels, but sadly I couldn’t find any way to get it in Europe yet.
    It’s also not supported by the esphome-component (yet).


    I connected it like this:

    The code for the test setup:

    i2c:
      sda: GPIO4
      scl: GPIO5
      frequency: 400khz
      scan: true
    
    sensor:
      - platform: nau7802
        name: "NAU7802 Weight"
        gain: 128
        ldo_voltage: "3.0V"
        update_interval: ${update_interval}
        filters:
         - calibrate_linear:
              datapoints:
              # measuring at 25 °C
               -  64429 -> 0
               -  166362 -> 4.58    # super with empty frames + tension belt
               -  392628 -> 14.58   # super with empty frames + tension belt + 10kg
               ...
    button:
      - platform: template
        name: "Toggle Internal Calibration"
        on_press:
          - logger.log: "Internal calibration toggled"
          - nau7802.calibrate_internal_offset  

    This code exposes the internal calibration via a button. The documentation says the internal calibration should be called on temperature or parameter changes, this is an open TODO as well as compensating the temperature drift.

    Additional new TODOs:

    • Support for the internal temperature sensor for the NAU7802-eshome-component
    • Support for channel B for the NAU7802-esphome-component
    • A proper state machine for the NAU7802-esphome-component (similar to the suggestion to the HX711-component in this pull-request)
  • A beelogger on ESP32 basis, part 6: Test setup: The wind speed sensor (anemometer)

    I bought a wind speed sensor from china, it’s a spare part for the “misol” weather stations, just search for “wind speed misol sensor aliexpress” for the product, it was less than 15€.


    I unscrewed it to see how it works, technically it’s just a hall sensor.

    For my test-setup, I simply cut off the connector (Don’t do this if you want to use it in combination with the wind direction sensor, it’s intended to be plugged in there!) and connected the wires directly to GND and GPIO4 and used the internal pull-up resistor:

    Adding it to esphome is straight forward and easy. I’m using the pulse counter component, it uses the pulse counter peripheral of the ESP32 and works in standby-modes:

    substitutions:
      update_interval: 5s
    
    sensor:
     - platform: pulse_counter
       name: "Wind Speed Counter"
       id: windspeed_pulse_counter
       use_pcnt: true
       count_mode: 
         falling_edge: INCREMENT
         rising_edge: DISABLE
       pin:
         number: GPIO4
         inverted: true
         mode:
           input: true
           pullup: true
       unit_of_measurement: "km/h"
       icon: "mdi:windsock"
       update_interval: ${update_interval}
       filters:
         - multiply: 0.04
         - debounce: 10ms
       total:
         name: "Wind Speed Counter Total Pulses"

    The value “0.04” is 2.4kmh/60s (to convert it from esphome’s pulses/min to km/h). The value 2.4km/h is explained here in the datasheets I found in this wiki that seem to match my hardware:
    datasheet_1
    datasheet_2

  • A beelogger on ESP32 basis, part 5: Test setup: The wind direction sensor

    I bought a wind direction sensor from china, it’s a spare part for the “misol” weather stations, just search for “wind direction sensor misol aliexpress” for the product, it was less than 15€.

    I unscrewed it to understand how it works. The picture of the PCB verifies that the hardware matches the datasheets I found in this wiki:
    datasheet_1
    datasheet_2

    So it’s a few hall sensors that switch resistors connected in parallel. The magnet switches one or two of them at once:

    The resistor values are given in the datasheet:

    Direction (degrees)Resistance (ohms)
    033K
    22.56.57K
    458.2K
    67.5891
    901K
    112.5688
    1352.2K
    157.51.41K
    1803.9K
    202.53.14K
    22516K
    247.514.12K
    270120K
    292.542.12K
    31564.9K
    337.521.88K


    Every second entry in this table is a combination of the entries below and above when both switches are triggered and is defined by the formular for two resistors connected in parallel:

    $$R = \frac{R_1 \cdot R_2}{R_1 + R_2}$$

    So we have to measure the resistance of the sensor via the ADC. Therefore a voltage divider is needed. A voltage divider looks like this:

    R2 is the variable resistor that is the sensor. R1 has to be chosen correctly so that it matches the range of R2, the value of Vin and the desired measuring range on Vout.

    According to the datasheets we have to measure resistances for R2 that can be between 688 Ω and 120 kΩ. The ADC of the ESP32S3 accepts up to 3100 mV with ADC_ATTEN_DB_11 (that’s Vout), Vin is 3.3V from the ESP32.

    Now the value for R1 has to be calculated for the biggest possible resistor, so we have R2=120 kΩ, Vin=3.3V and Vout3.1V:

    The formular for the voltage divider is:

    $$V_{\text{out}} = V_{\text{in}} \cdot \frac{R_2}{R_1 + R_2}$$

    So, for our values of Vin, Vout and R2 we get:

    $$R_1 = R_2 \cdot (\frac{V_{\text{in}}}{V_{\text{out}}} – 1) = 120kΩ \cdot (\frac{3.3V}{3.1V} – 1) ≈ 7.74kΩ$$

    The next fitting standard resistor is 8.2kΩ, let’s calculate Vout for it:

    $$V_{\text{out}} = V_{\text{in}} \cdot \frac{R_2}{R_1 + R_2} = 3.3V \cdot \frac{120kΩ}{8.2kΩ + 120kΩ} ≈ 3089mV$$

    That’s a good value: with it we get 3.089V on Vout for 120kΩ which is about 99.6% of the maximal voltage of 3.1V the ADC of the ESP323 can handle.
    So we can get up to 3.089V of the reference voltage of 3.3V, that’s 93.6% of the theoretical range. The ADC has 12 bits, that means we have a theoretical measuring range of 212=4096 values, 93.6% of it are ≈3834, so we have a resolution of round about 120kΩ/3834 ≈ 31.3Ω.
    That’s sufficient, the minimal difference between the resistors we want to measure is more than 100Ω.

    So we can wire the sensor up now via a properly chosen voltage divider:

    Integrating it in esphome is comfortable:

    substitutions:
      update_interval: 5s
    
    text_sensor:
      - platform: template
        name: "Wind direction"
        icon: 'mdi:windsock'
        id: wind_dir
    
    sensor:
      - platform: adc
        id: source_sensor
        pin: GPIO32
        name: ADC
        attenuation: 11db
        internal: true
        update_interval: ${update_interval}
        accuracy_decimals: 1
    
      - platform: resistance
        sensor: source_sensor
        id: resistance_sensor
        configuration: DOWNSTREAM
        resistor: 8.2kOhm
        internal: true
        name: Resistance Sensor
        reference_voltage: 3.3V
        accuracy_decimals: 1
        update_interval: ${update_interval}
    
        on_value:
          then:
            - lambda: |-
                struct WindEntry {
                  int lower;
                  int upper;
                  const char* direction;
                  float heading;
                };
    
                static const WindEntry wind_table[] = {
                  {  400,    789, "East-South-East",  112.5},  // 688 Ω
                  {  789,    945, "East-North-East",   67.5},  // 891 Ω
                  {  945,   1205, "East",              90.0},  // 1 kΩ
                  { 1205,   1805, "South-South-East", 157.5},  // 1.41 kΩ
                  { 1805,   2670, "South-East",       135.0},  // 2.2 kΩ
                  { 2670,   3520, "South-South-West", 202.5},  // 3.14 kΩ
                  { 3520,   5235, "South",            180.0},  // 3.9 kΩ
                  { 5235,   7385, "North-North-East",  22.5},  // 6.57 kΩ
                  { 7385,  11160, "North-East",        45.0},  // 8.2 kΩ
                  {11160,  15060, "West-South-West",  247.5},  // 14.12 kΩ
                  {15060,  18940, "South-West",       225.0},  // 16 kΩ
                  {18940,  27440, "North-North-West", 337.5},  // 21.88 kΩ
                  {27440,  37560, "North",              0.0},  // 33 kΩ
                  {37560,  53510, "West-North-West",  292.5},  // 42.12 kΩ
                  {53510,  92450, "North-West",       315.0},  // 64.9 kΩ
                  {92450, 150000, "West",             270.0},  // 120 kΩ
                };
    
                const int resistance = id(resistance_sensor).state;
    
                for (const auto& entry : wind_table) {
                  if (resistance >= entry.lower && resistance < entry.upper) {
                    id(wind_dir).publish_state(entry.direction);
                    id(wind_heading).publish_state(entry.heading);
                    break;
                  }
                }
    
      - platform: template
        name: "Wind Heading"
        id: wind_heading
        unit_of_measurement: "°"
  • A beelogger on ESP32 basis, part 4: Test setup: The rain sensor

    I bought a rain gauge from china, it’s a spare part for the “misol” weather stations, just search for “rain gauge sensor aliexpress” for the product, it was less than 15€. Technically it’s just a hall sensor.

    For my test-setup, I simply cut off the connector and connected the wires directly to GND and GPIO4 and used the internal pull-up resistor:

    Adding it to esphome is straight forward and easy. I’m using the pulse counter component, it uses the pulse counter peripheral of the ESP32 and works in standby-modes:

    substitutions:
      update_interval: 5s
    
    sensors:
     - platform: pulse_counter
        name: "Rainfall Pulse Counter"
        id: rainfall_pulse_counter
        use_pcnt: true
        count_mode: 
          falling_edge: INCREMENT
          rising_edge: DISABLE
        pin:
          number: GPIO4
          inverted: true
          mode:
            input: true
            pullup: true
        unit_of_measurement: "mm"
        icon: "mdi:water"
        update_interval: ${update_interval}
        filters:
          # I'm unsure what value is correct, I'll have to measure it myself
          - multiply: 0.2794
          # - multiply: 0.367
          - debounce: 10ms
        total:
          name: "Rainfall Total Pulses"
    
      - platform: template
        name: "Hourly Rainfall"
        id: hourly_rainfall
        unit_of_measurement: "mm"
        icon: "mdi:weather-rainy"
        update_interval: never
    
      - platform: template
        name: "Daily Rainfall"
        id: daily_rainfall
        unit_of_measurement: "mm"
        icon: "mdi:weather-pouring"
        update_interval: never
    
      - platform: template
        name: "Rainfall Accumulator"
        id: rainfall_accumulator
        unit_of_measurement: "mm"
        icon: "mdi:counter"
        update_interval: never
        internal: true  # Hide from Home Assistant
    
    interval:
      # Every minute: add current pulse count to accumulator
      - interval: 60s
        then:
          - lambda: |-
              float current = id(rainfall_pulse_counter).state;
              id(rainfall_accumulator).publish_state(id(rainfall_accumulator).state + current);
    
      # Every hour: publish hourly total and reset accumulator
      - interval: 1h
        then:
          - lambda: |-
              float total = id(rainfall_accumulator).state;
              id(hourly_rainfall).publish_state(total);
              id(rainfall_accumulator).publish_state(0);
    
      # Every day at midnight: publish daily total and reset accumulator
    time:
      - platform: homeassistant
        on_time:
          - seconds: 0
            minutes: 0
            hours: 0
            then:
              - lambda: |-
                  float total = id(rainfall_accumulator).state;
                  id(daily_rainfall).publish_state(total);
                  id(rainfall_accumulator).publish_state(0);

    The value “0.2794” is explained here in the datasheets I found in this wiki that seem to match my hardware:
    datasheet_1
    datasheet_2


    The value “0.367” is explained here:
    https://community.home-assistant.io/t/how-to-measure-integration-of-rain-pulse-counter-into-daily-value/136709

    I don’t know yet which one is correct, I’ll do some measurements.


  • A beelogger on ESP32 basis, part 3: Test setup: The scale

    I built four bee-hive scales according to the great instructions of the Beelogger project.

    Remarks:

    • I used the “2 holes per side”-variant of the Load Cell Sensor (CZL601 instead of the CZL601-AC), it works fine too.

    In my test-setup I connected it like this:

    I see two possible solutions for this problem:

    • Exchanging some resistors on the hx711-breakout-board
      • Turns out this is not the way to go, there are at least 4 different versions of the board:
        • blue PCB:
          exchanging the resistors is doable, but needs experience
        • green PCB:
          exchanging the resistors is hard, special equipment needed
        • purple PCB:
          exchanging the resistors is practically impossible.
        • red (the recommend version, it’s shielded) PCB:
          exchanging the resistors is practically impossible.
    • Use another breakout board that doesn’t cause that much problems, I ordered a NAU7802-breakout-board for testing.
      • This is the way the go:
        • It’s connected via I2C, this makes the wiring more easy
        • it works out-of-the-box with 3.3V


    For calibrating, I used weight-lifting weights.

    The testing-code for the HX711 :

    substitutions:
      update_interval: 5s
    
    sensor:
      - platform: hx711
        name: "HX711 Weight"
        dout_pin: GPIO4
        clk_pin: GPIO5
        gain: 128
        update_interval: ${update_interval}
        filters:
          - calibrate_linear:
              datapoints:
              # measuring at 25 °C
              -  77915  -> 0
              -  167520 -> 4.08    # empty super + tension belt
              -  205782 -> 5.88    # super with empty frames + tension belt
              -  308103 -> 9.08    # super with empty frames + tension belt + 5kg
              -  413973 -> 14.08   # super with empty frames + tension belt + 10kg
              ...
              - 1554032 -> 69.08   # super with empty frames + tension belt + 65kg
              - 1655332 -> 74.08   # super with empty frames + tension belt + 70kg
          - clamp:
              min_value: 0
              ignore_out_of_range: true

    Compensating the temperature drift of the scale is still an open TODO.

  • A beelogger on ESP32 basis, part 2: Test setup: The basic sensors

    I’m still unsure which sensors to use in production, so I started experimenting:

    I connected a bmp085, a waterproof bh1750, a Si7021 and a bme280 via I2C. Two DS18B20s that will be built in in the lid of the hive and into its center are connected via 1-wire.



    In my test-setup, I connected it like this:


    The code I used for the sensors:

    substitutions:
      update_interval: 5s
      
    i2c:
      sda: GPIO35
      scl: GPIO36
      scan: true
    
    one_wire:
      - platform: gpio
        pin: GPIO04
    
    sensor:
      - platform: htu21d
        model: SI7021
        temperature:
          name: "SI7021 Temperature"
        humidity:
          name: "SI7021 Humidity"
        heater:
          name: "SI7021 Heater"
        address: 0x40
        update_interval: ${update_interval}
      - platform: bmp085
        temperature:
          name: "BMP085 Temperature"
        pressure:
          name: "BMP085 Pressure"
        address: 0x77
        update_interval: ${update_interval}
      - platform: bme280_i2c
        temperature:
          name: "BME280 Temperature"
        pressure:
          name: "BME280 Pressure"
        humidity:
          name: "BME280 Humidity"
        address: 0x76
        update_interval: ${update_interval}
      - platform: dallas_temp
        name: "DS18B20 Temperature 1"
        address: 0xcb000000513fb228
        update_interval: ${update_interval}
      - platform: dallas_temp
        name: "DS18B20 Temperature 2"
        address: 0x6f000000bcc72128
        update_interval: ${update_interval}
      - platform: bh1750
        name: "BH1750 Illuminance"
        address: 0x23
        update_interval: ${update_interval}
  • A beelogger on ESP32 basis, part 1: The idea

    The Beelogger project is really great.

    But: Since my house is automated with homeassistant, esphome, a lot of ESP32s and my bee-hives are in WIFI-range, I decided to rebuild the beelogger-functionality on esphome basis.

    Additional arguments for realizing it with an ESP32 instead of an Arduino or STM32 might be:

    • more CPU power
    • more RAM & flash (up to 8 MB RAM & 16 MB flash)
    • integrated WIFI
    • probably less power consumption when using deep sleep modes
    • A DSP for audio analysis
    • more ADCs with higher resolution (depending on the ESP32 variant used)
    • a built-in RTC
    • the comfort of esphome:
      • OTA updates
      • a built-in automated connection to homeassistant
      • a webinterface on the device
      • no complex sketches to maintain, just a .yaml-config

    Desicion: I’m going to use an ESP32-S3-N16R8 and / or the ESP32-S3-Zero

    The ESP32-S3-N16R8 provides:

    • a dual-core 32-bit microprocessor with 240 MHz
    • 8MB of additional PSRAM and 16MB flash,
    • 2.4 GHz Wi-Fi (IEEE 802.11b/g/n) and Bluetooth® 5 (LE)
    • An hardware RTC
    • Two general-purpose SPI ports
    • Three UARTs
    • Two I2Cs
    • Two I2Ss
    • Pulse counter
    • Two 12-bit SAR ADCs, up to 20 channels
    • Four 54-bit general-purpose timers
    • 52-bit system timer
    • Three watchdog timers
    • DSP-hardware
    • Four power modes designed for typical scenarios: Active, Modem-sleep, Light-sleep, Deep-sleep
    • A lot of GPIOs 🙂

    It’s the currently most powerful ESP32, its hardware should be more than enough for the task 😀
    The Zero variant will be used when there are just a few PINs needed.

    Realizing it will be a major project, but probably also a lot of fun 🙂

    The TODOs I see so far, I’ll realize them in single test projects:

    • get all the desired sensors working properly
    • write an external component for esphome for the audio-analysis
    • write a component for esphome for the bee counter hardware
    • write a component for esphome to connect to the beelogger-server
    • care about the power management
    • logging to SD-card/flash
    • properly wiring it all up at the end, making it electronically fail safe, soldering it onto a prototype-board
    • install everything in proper cases with proper connectors and install it in a bee hive
    • realize additional ideas, they will for sure come up when working on this project 😉