/ August 3, 2020/ Articles, Home Assistant, Weather/ 8 comments

I have made a Solar Weather Station in a Birdhouse. This is based in this project. I ordered the components and some extra’s to measure lux and rain:

  • The Wemos D1 Mini Pro (had this already)
  • BME280 for Temperature, Humidity, Pressure (AliExpress)
  • BH1750 for Light Luxsensor (AliExpress)
  • SI1145 to measure UV Index, IR Light (raw), Visible Light (raw)
  • FC-37 raindrop sensor with a LM393 converter (AliExpress) – please see edit at the bottom
  • Dew Point calculation sensor
  • Absolute Humidity calculation sensor
  • 5V 250mA Solar panel (AliExpress) – changed it later to a bigger 840mA (AliExpress)
  • TP4056 Charger module (AliExpress) – please see edit at the bottom
  • The 3-Pin PCB switch (had this already)
  • Screw terminal (had this already)
  • 18650 Lithium 3400mAh Battery (AliExpress) – please see edit at the bottom
  • Some header pins (AliExpress)
  • The PCB board at PCBWay (PCBWay)

I didn’t wanted to make it with an Arduino sketch, but want to make it in ESPHome. So is used the following code in ESPHome for the Wemos:

substitutions:
  display_name: SolarWeatherStation

esphome:
  name: solar_weather
  platform: ESP8266
#  board: d1_mini_pro
  board: d1_mini

  includes:
    - SI1145.h
  libraries:
    - "Adafruit SI1145 Library"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_pass

api:

# Enable logging
logger:

mqtt:
  broker: !secret mqtt_broker
  birth_message:
    topic: solarsensor/birthdisable
    payload: disable
  will_message:
    topic: solarsensor/willdisable
    payload: disable

ota:

i2c:
 - id: ic_1
   sda: 4
   scl: 5
   scan: true
   frequency: 100kHz
   
binary_sensor:
  - platform: gpio
    pin: 
      number: D5
      inverted: True
    name: ${display_name} Rain
    device_class: moisture
    filters:
      - delayed_on_off: 10000ms
    
sensor:
  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    update_interval: 25s
    
  - platform: bme280
    address: 0x76
    update_interval: 15s
    temperature:
      name: ${display_name} Temp
      id: bme280_temperature
      oversampling: 4x
      filters:
      - offset: -1.6
    pressure:
     name: ${display_name} Pres
     id: bme280_pressure
     oversampling: 4x
    humidity:
     name: ${display_name} Humi
     id: bme280_humidity
     oversampling: 4x
    
  - platform: bh1750
    name: ${display_name} Lux
    address: 0x23
    update_interval: 15s

  - platform: template
    name: ${display_name} Absolute Humidity
    lambda: |-
      const float mw = 18.01534;    // molar mass of water g/mol
      const float r = 8.31447215;   // Universal gas constant J/mol/K
      return (6.112 * powf(2.718281828, (17.67 * id(bme280_temperature).state) /
        (id(bme280_temperature).state + 243.5)) * id(bme280_humidity).state * mw) /
        ((273.15 + id(bme280_temperature).state) * r); // in grams/m^3
    accuracy_decimals: 2
    update_interval: 25s
    icon: 'mdi:water'
    unit_of_measurement: 'g/m³'
    
  - platform: template
    name: ${display_name} Dew Point
    lambda: return (243.5*(log(id(bme280_humidity).state/100)+((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state)))/(17.67-log(id(bme280_humidity).state/100)-((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state))));
    unit_of_measurement: °C
    update_interval: 25s
    icon: 'mdi:thermometer-alert'
    
  - platform: custom
    lambda: |-
      auto UV_sensor = new SI1145_sensor();
      App.register_component(UV_sensor);
      return {UV_sensor->visible_sensor, UV_sensor->ir_sensor, UV_sensor->uvindex_sensor};

    sensors:
    - name: ${display_name} Visible Light
    - name: ${display_name} IR Light
    - name: ${display_name} UV Index
      unit_of_measurement: uvindex
      accuracy_decimals: 2
      
  - platform: adc
    id: bat_vcc
    pin: A0
    name: ${display_name} Battery
    update_interval: 15s
    filters:
      - multiply: 5.37

  - platform: template
    name: ${display_name} Battery Percentage
    unit_of_measurement: '%'
    update_interval: 15s
    lambda: |-
      return ((id(bat_vcc).state /4.25) * 100.00);
    
deep_sleep:
 run_duration: 35s
 sleep_duration: 5min

The above code does need some tuning. For example the ADC multiply. First of all disable the filter. Than a RealValue between 0-1V will be shown in the ESPHome log. Measure the real battery value with a multiple meter. Then BatteryMeasure / RealValue = Multplier.

Also the Battery Percentage needs some tuning, and the Deep Sleep needs tuning. The 35s is enough to drop some sensor readings and i feel ok with an 5 min sleep.
For the binary rain sensor there is a delayed_on_off so the sensor is not bouncing during the sensor reading.

To read an UV index i did use the SI1145, but the first try wasn’t successfully. I have stacked the sensor on the BH1750 sensor, this is possible because it is a different address.
This is not an integrated sensor so i needed the library for that. I tried it, but i was continously getting 0 values. After all it turned out that a manual library for a I2C and using the integrated I2C is not working together. So i stopped using it for a while. After a while someone posted on github that the I2C frequency wasn’t the same for this custom sensor and the built in I2C of esphome. I changed the code and added:frequency: 100kHz within in the I2C section and all working now.
To get this working you need to upload the SI1145.h and the library code files to your esphome folder. Get the right SI1145.h here and the library here (upload the Adafruit_SI1145.cpp and Adafruit_SI1145.h file)

I also added the Dewpoint calculation sensor and the Absolute Humidity sensor calculator from the BME280.

To make the project more fun, i decided to create it in a bird house. Did some sawing and nailing, drilled some ventilation holes, made roof protection and divided the electronics part from the bird part. And made it waterproof.
The result:

For Home Assistant i used the following code, but you can also use MQTT Discovery to add the sensors.

# Weather Station
  - platform: mqtt
    name: Weather Station Temp
    state_topic: "solar_weather/sensor/solarweatherstation_temp/state"
    unit_of_measurement: "°C"
    force_update: true
    device_class: temperature
  - platform: mqtt
    name: Weather Station Humi
    state_topic: "solar_weather/sensor/solarweatherstation_humi/state"
    unit_of_measurement: "%"
    force_update: true
    device_class: humidity
  - platform: mqtt
    name: Weather Station Absolute Humidity
    state_topic: "solar_weather/sensor/solarweatherstation_absolute_humidity/state"
    icon: mdi:water-percent
    unit_of_measurement: "g/m³"
    force_update: true
  - platform: mqtt
    name: Weather Station Pres
    state_topic: "solar_weather/sensor/solarweatherstation_pres/state"
    unit_of_measurement: "hPa"
    force_update: true
    device_class: pressure
  - platform: mqtt
    name: Weather Station Lux
    state_topic: "solar_weather/sensor/solarweatherstation_lux/state"
    unit_of_measurement: "lux"
    device_class: illuminance
    force_update: true
  - platform: mqtt
    name: Weather Station Dewpoint
    state_topic: "solar_weather/sensor/solarweatherstation_dew_point/state"
    unit_of_measurement: "°C"
    force_update: true
    device_class: temperature
  - platform: mqtt
    name: Weather Station UV Index
    state_topic: "solar_weather/sensor/solarweatherstation_uv_index/state"
    force_update: true
    icon: mdi:wall-sconce-round
  - platform: mqtt
    name: Weather Station Battery
    state_topic: "solar_weather/sensor/solarweatherstation_battery/state"
    unit_of_measurement: "V"
    force_update: true
    device_class: battery
  - platform: mqtt
    name: Weather Station Battery Percentage
    state_topic: "solar_weather/sensor/solarweatherstation_battery_percentage/state"
    unit_of_measurement: "%"
    force_update: true
    device_class: battery
  - platform: mqtt
    name: Weather Station Wifi Signal
    state_topic: "solar_weather/sensor/wifi_signal_sensor/state"
    unit_of_measurement: "dB"
    force_update: true
    device_class: signal_strength

At the end it is looking like this:

Edit: Because the sensors are in a Bird house, the temperature is higher compared to the ouside air. To fix this, you can play a little bit with the offset in esphome as i did. Also the light (lux) values aren’t correct. Therefore i create some ventilation holes in the bird house. You can also make your own Stevenson screen instead of a bird house. For the light intensity i created a hole with some plexiglass, behind the hole i glued the lux and light sensor.

Edit 2021:
Also the rain sensor turns out to be not as accurate, so i removed it. Perhaps in future i will try it again, but in the meantime i use a public sensor for this purpose.
As for the Solor panel, it turnes out it workes great, but is a little to less sometime to overcome the nights when there is very less UV on the day. So i changed it to a bigger 840mAh version, which is 165x165mm and covers almost the whole top of the birdhouse.

Edit 2022:
Athough the solar panel is working great, with the Rain sensor it is not a succes. As a deep sleep is needed for saving battery power, you are simple missing rain drops. I made some improvement in the code, as the new ESPHome 2021.12 and newer has some changes with Adafruit libraries. I also changed the rain sensor to a bucket MS-WH-SP-RG
As this is an digital sensor, you can use it with the pulse_counter and connect it to a GPIO.

At last make sure you have an updated version of SI1145.h (more info), i used the following code:

#include "esphome.h"
#include "Adafruit_SI1145.h"

using namespace esphome;

class SI1145_sensor : public PollingComponent {
 public:

  Adafruit_SI1145 uv;

  Sensor *uvindex_sensor = new Sensor();
  Sensor *ir_sensor = new Sensor();
  Sensor *visible_sensor = new Sensor();
  
  SI1145_sensor() : PollingComponent(60000) { }

  void setup() override {
    uv.begin();
  }

  void update() override {
    float uvindex = uv.readUV();
    uvindex_sensor->publish_state(uvindex / 100.0);

    float irlight = uv.readIR();
    ir_sensor->publish_state(irlight);
    
    float visible = uv.readVisible();
    visible_sensor->publish_state(visible);
  }
};

Also i get rid of the solar panel and find a way to connect it constantly to main power (5v), so no battery needed any more. And also the TP4056 is not needed any more. The code is:

substitutions:
  display_name: SolarWeatherStation

esphome:
  name: solar_weather
  platform: ESP8266
  board: d1_mini

  includes:
    - SI1145.h
  libraries:
    - SPI
    - Wire
    - adafruit/Adafruit BusIO @ 1.9.6
    - "Adafruit SI1145 Library"
    
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_pass

api:

# Enable logging
logger:

mqtt:
  broker: !secret mqtt_broker
  birth_message:
    topic: solarsensor/birthdisable
    payload: disable
  will_message:
    topic: solarsensor/willdisable
    payload: disable

ota:

i2c:
 - id: ic_1
   sda: 4
   scl: 5
   scan: true
   frequency: 100kHz
    
sensor:
  - platform: wifi_signal
    name: WiFi Signal Sensor
    update_interval: 180s

  - platform: pulse_counter
    name: ${display_name} Rain
    pin: 
      number: D4
      mode: INPUT_PULLUP
    unit_of_measurement: 'mm'
    icon: 'mdi:weather-rainy'
    count_mode:
      rising_edge: DISABLE
      falling_edge: INCREMENT
    internal_filter: 50us
    filters:
      - multiply: 0.2794
    
  - platform: bme280
    address: 0x76
    update_interval: 50s
    temperature:
      name: ${display_name} Temp
      id: bme280_temperature
      oversampling: 4x
      filters:
      - offset: -1.6
    pressure:
     name: ${display_name} Pres
     id: bme280_pressure
     oversampling: 4x
    humidity:
     name: ${display_name} Humi
     id: bme280_humidity
     oversampling: 4x
    
  - platform: bh1750
    name: ${display_name} Lux
    address: 0x23
    update_interval: 50s

  - platform: template
    name: ${display_name} Absolute Humidity
    lambda: |-
      const float mw = 18.01534;    // molar mass of water g/mol
      const float r = 8.31447215;   // Universal gas constant J/mol/K
      return (6.112 * powf(2.718281828, (17.67 * id(bme280_temperature).state) /
        (id(bme280_temperature).state + 243.5)) * id(bme280_humidity).state * mw) /
        ((273.15 + id(bme280_temperature).state) * r); // in grams/m^3
    accuracy_decimals: 2
    update_interval: 110s
    icon: 'mdi:water'
    unit_of_measurement: 'g/m³'
    
  - platform: template
    name: ${display_name} Dew Point
    lambda: return (243.5*(log(id(bme280_humidity).state/100)+((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state)))/(17.67-log(id(bme280_humidity).state/100)-((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state))));
    unit_of_measurement: °C
    update_interval: 120s
    icon: 'mdi:thermometer-alert'
    
  - platform: custom
    lambda: |-
      auto UV_sensor = new SI1145_sensor();
      App.register_component(UV_sensor);
      return {UV_sensor->visible_sensor, UV_sensor->ir_sensor, UV_sensor->uvindex_sensor};

    sensors:
    - name: ${display_name} Visible Light
    - name: ${display_name} IR Light
    - name: ${display_name} UV Index
      unit_of_measurement: uvindex
      accuracy_decimals: 2

In this code i updated the interval also. You can remove the battery sensors in HA, as it is not in use anymore.

Share this Post

8 Comments

  1. This looks like a fun project. However, I’d like to suggest something and please don’t take it personal.

    You should read a little bit about how a safe birdhouse should be constructed. There are multiple flaws in your design which make it much easier for predators to harm unaware birds – e.g, walls thickness or presence of this stick just below entering hole (birds don’t need it at all, it just make things easier for predators….). And most importantly, hole should be drilled higher, or whole box should be deeper.

    1. Hi Philip, i appreciatie your comment, but keep in mind, this is more a fun project and for purpose of the weather station.

  2. Pingback: Community Highlights: 11th edition – Home Assistant – truvallu.com

  3. Pingback: Community Highlights: 11th version » Define Smart Home

  4. Hello,

    I get this error when I try to compile the code:

    src/main.cpp: In lambda function:
    src/main.cpp:529:28: error: expected type-specifier before ‘SI1145_sensor’
    auto UV_sensor = new SI1145_sensor();
    ^
    src/main.cpp:531:89: error: could not convert ‘{, , }’ from ” to ‘std::vector’
    return {UV_sensor->visible_sensor, UV_sensor->ir_sensor, UV_sensor->uvindex_sensor};
    ^
    src/main.cpp:532:3: warning: control reaches end of non-void function [-Wreturn-type]
    });
    ^
    *** [/data/solarnimeteostanice/.pioenvs/solarnimeteostanice/src/main.cpp.o] Error 1

  5. Hello,

    I get this error message when I try to compile the code:


    src/main.cpp: In lambda function:
    src/main.cpp:525:28: error: expected type-specifier before 'SI1145_sensor'
    auto UV_sensor = new SI1145_sensor();
    ^
    src/main.cpp:527:89: error: could not convert '{, , }' from '' to 'std::vector'
    return {UV_sensor->visible_sensor, UV_sensor->ir_sensor, UV_sensor->uvindex_sensor};
    ^
    src/main.cpp:528:3: warning: control reaches end of non-void function [-Wreturn-type]
    });
    ^
    *** [/data/solarnimeteostanice/.pioenvs/solarnimeteostanice/src/main.cpp.o] Error 1

    1. Did you load the adafruit files ?

      1. Thank you,
        but everything is fine now.
        For the first time, I didn’t know what to do with the SI1145.h file, what it was supposed to contain. But I already understood that and I have successfully compiled the code.

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
*
*