Weather station modified with an ESP32

Here is my weather station project. This is a cheap wireless weather station I modified with an ESP32 & programmed with the wonderful ESPHome.
I choose to completely remove the original electronics because I wanted to receive data from the sensors at a shorter interval and I can’t be bothered decoding the wireless signal sent.

This is the cheap weather station I purchased. I will not be using the battery powered LCD display.

Weaknesses of this weather station is construction quality.
The metal stand is not stainless steel, nor galvanised or aluminium.
The plastic sensors are likely to breakdown in sunlight UV (as I have seen on another unit).

I painted the sensor units with outdoor rated white spray paint.

The sensors;

Anemometer (Wind speed)
This is a wind cup anemometer and the number of rotations per time period can be used to calculate wind speed.
I found this unit has a magnetic reed switch and a magnet. Two pulses are given per full rotation of the wind cups.

Bearing direction (Wind vane)
This uses 4 magnetic reed switches, for North, East, South & West.
When pointed North, the North reed switch closes and connects a resistor in parallel to the sensor’s wires.
Each direction has a different resistance per direction.
Directions between, e.g. North East, are the resistance value of two resistors in parallel.

I can’t remember off the top of my head what the resistor values were. I connected the sensor across a voltage divider and used an ADC sensor. Value ranges of the ADC correlated to a direction.

Rain gauge
This is a tipping bucket rain gauge. Rain water falls into a funnel and feels a small bucket. When the bucket fills with water it tips over under gravity and activates a magnetic reed switch. The count of bucket tips is the measured rain.

This rain gauge is 0.3mm per bucket tip.

Temperature + Humidity
I removed the original temperature sensor and utilised a DHT22 sensor.

esphome:
  name: weather-station-esp32
  friendly_name: weather_station_esp32

esp32:
  board: esp32dev
  framework:
    type: arduino

#Buch of stuff here.

#Pinouts:
  #DHT22 - 18/D18
  #Anemometer - 32/D32
  #Wind direction - 34/D34
  #Rain gauge - 33/D33

button:
  - platform: template
    name: "Reset total rain"
    id: stationraintotal_reset
    icon: "mdi:rotate-left"
    on_press:
      - pulse_counter.set_total_pulses:
          id: stationrain
          value: !lambda 'return 0;'


sensor:
  # DHT temp + humidity sensor
  - platform: dht
    pin: GPIO18
    model: DHT22
    temperature:
      name: "Station Temperature"
      id: stationtemperature
      filters:
        filter_out: nan
    humidity:
      name: "Station Humidity"
      id: stationhumidity
      filters:
        filter_out: nan
    update_interval: 120s
    

  - platform: absolute_humidity
    name: Station Absolute Humidity
    temperature: stationtemperature
    humidity: stationhumidity
    filters:
      filter_out: nan

  # Wind anemometer
  # 2 Pules per rotation
  - platform: pulse_counter
    name: Station Wind Speed
    id: stationwindspeed
    pin:
      number: GPIO32
      inverted: true
      mode:
        input: true
        pullup: true
    update_interval: 10s
    filters:
      - lambda: if (x < 0.001) return x; else return 1.761 / (1 + (x / 2 * 0.02010619298) ) + 3.013 * (x / 2 * 0.02010619298);
      - sliding_window_moving_average:
          window_size: 12
          send_every: 6
    unit_of_measurement: 'Km/h'
    device_class: wind_speed
    on_value:
      then:
        - component.update: stationknots

  #Km/h to knots
  - platform: template
    name: Station wind speed knots
    id: stationknots
    lambda: |-
      return id(stationwindspeed).state * 0.53996;
    unit_of_measurement: 'knt'

  
  # Feels like temperature
  - platform: template
    name: "Feels Like Temperature"
    id: stationfeelslike
    update_interval: 120s
    unit_of_measurement: '°C'
    filters:
      filter_out: nan
    lambda: |-
      float T = id(stationtemperature).state;  // Replace with your temperature sensor
      float RH = id(stationhumidity).state;  // Replace with your humidity sensor
      float WS = id(stationwindspeed).state;  // Replace with your wind speed sensor
      float HI;
      T = T * 9/5 + 32;  // Convert temperature from Celsius to Fahrenheit for the formula
      if (T <= 40.0) {
        HI = 35.74 + 0.6215 * T - 35.75 * pow(WS, 0.16) + 0.4275 * T * pow(WS, 0.16);
      } else {
        HI = -42.379 + 2.04901523*T + 10.14333127*RH - 0.22475541*T*RH - 0.00683783*T*T - 0.05481717*RH*RH + 0.00122874*T*T*RH + 0.00085282*T*RH*RH - 0.00000199*T*T*RH*RH;
        if (RH < 13 && T >= 80 && T <= 112) {
          float adjust = ((13-RH)/4) * sqrt((17-abs(T-95))/17);
          HI -= adjust;
        } else if (RH > 85 && T >= 80 && T <= 87) {
          float adjust = ((RH-85)/10) * ((87-T)/5);
          HI += adjust;
        } else if (T < 80) {
          HI = 0.5 * (T + 61.0 + ((T-68.0)*1.2) + (RH*0.094));
        }
      }
      return (HI - 32) * 5/9;  // Convert back to Celsius

  
  
  #Rain gauge
  - platform: pulse_counter
    pin:
      number: GPIO33
      inverted: true
      mode:
        pullup: true
        input: true
    unit_of_measurement: 'mm/min'
    name: 'Station Rain'
    id: stationrain
    update_interval: 60s
    accuracy_decimals: 1
    filters:
      - debounce: 0.05s
      - multiply: 0.3  # pulses x 0.3mm
    total:
      unit_of_measurement: 'mm'
      name: 'Station Total Rain'
      accuracy_decimals: 1
      filters:
        - multiply: 0.3

  #Wind direction
  - platform: adc
    name: "Station Wind Dir"
    id: station_winddirection
    internal: true
    pin: 34
    accuracy_decimals: 0
    unit_of_measurement: 'ADC'
    raw: true
    filters:
      - multiply: 1.0
      - median:
          window_size: 6
          send_every: 6
          send_first_at: 1
  
  - platform: template
    name: "Station Wind Direction bearing"
    id: stationwinddirection_int
    unit_of_measurement: '°' 
    lambda: |-
      if (id(station_winddirection).state > 1500 && id(station_winddirection).state <= 1700) {
        return 90.0; //North - offset to west
      }
      else if (id(station_winddirection).state > 650 && id(station_winddirection).state <= 800) {
        return 135.0; //North East
      }
      else if (id(station_winddirection).state > 900 && id(station_winddirection).state <= 1100) {
        return 180.0; //East
      }
      if (id(station_winddirection).state > 800 && id(station_winddirection).state <= 900) {
        return 225.0; //South East
      }
      if (id(station_winddirection).state > 1900 && id(station_winddirection).state <= 2300) {
        return 270.0; //South
      }
      if (id(station_winddirection).state > 1700 && id(station_winddirection).state <= 1900) {
        return 315.0; //South West
      }
      if (id(station_winddirection).state > 2300 && id(station_winddirection).state <= 2500) {
        return 0.0; //West
      }
      if (id(station_winddirection).state > 1100 && id(station_winddirection).state <= 1500)  {
        return 45.0; //North West
      }
      else {
        return 345.0; //Fault
      }
    update_interval: 60s

This is an ESP32 module from AliExpress.
On the circuit board is terminal connectors, a power switch, 12v to 5v buck step down module and an LDR’s pcb. I have an LDR for light levels but never calibrated it (still does alright to schedule garden lights).

The station itself is mounted to my TV antenna on the back of my house. Power cables runs under a roof tile and is connected in a cupboard to a 12v PSU.

1 Like

Some cool features in this project;
I added a “feels like” temperature. This factors the temperature, humidity and wind speed.

# Feels like temperature
  - platform: template
    name: "Feels Like Temperature"
    id: stationfeelslike
    update_interval: 120s
    unit_of_measurement: '°C'
    filters:
      filter_out: nan
    lambda: |-
      float T = id(stationtemperature).state;  // Replace with your temperature sensor
      float RH = id(stationhumidity).state;  // Replace with your humidity sensor
      float WS = id(stationwindspeed).state;  // Replace with your wind speed sensor
      float HI;
      T = T * 9/5 + 32;  // Convert temperature from Celsius to Fahrenheit for the formula
      if (T <= 40.0) {
        HI = 35.74 + 0.6215 * T - 35.75 * pow(WS, 0.16) + 0.4275 * T * pow(WS, 0.16);
      } else {
        HI = -42.379 + 2.04901523*T + 10.14333127*RH - 0.22475541*T*RH - 0.00683783*T*T - 0.05481717*RH*RH + 0.00122874*T*T*RH + 0.00085282*T*RH*RH - 0.00000199*T*T*RH*RH;
        if (RH < 13 && T >= 80 && T <= 112) {
          float adjust = ((13-RH)/4) * sqrt((17-abs(T-95))/17);
          HI -= adjust;
        } else if (RH > 85 && T >= 80 && T <= 87) {
          float adjust = ((RH-85)/10) * ((87-T)/5);
          HI += adjust;
        } else if (T < 80) {
          HI = 0.5 * (T + 61.0 + ((T-68.0)*1.2) + (RH*0.094));
        }
      }
      return (HI - 32) * 5/9;  // Convert back to Celsius

I also log the max, median and minimum of each sensor to a Google spreadsheet at midnight every night.
This is done with an automation and helper sensors.
Google sheets then makes a lovely graph I can use. There’s also other sensors I log daily into Google sheets as you can see by the sheet tabs.
I haven’t found a good way to log wind vane direction yet.

2 Likes

That’s a cool project! Modifying a cheap weather station with an ESP32 and ESPHome to get more frequent sensor data is a smart idea. Removing the original electronics to achieve this shows your dedication to customization.

1 Like

A small addition.
I have figured a way to log the wind vane per day, without needing an hourly measurement (e.g. North at 12pm, East at 1pm).

What I have done is used a history_stats helper to count how many hours the wind vane is aiming in a direction over 24hrs/1 day.

This count is added to a Google sheet at midnight (bottom right graph).

This photo also shows the other sensors graphs since I started long-term logging to Google sheets.
Google sheets also offers ‘publishing’ a graph and embedding it into a webpage which is awesome!