Weather station with Firebeetle/ESPHome

A while ago I bought a box full of Firebeetles, an offer I just couldn’t resist. But I didn’t really have time to do something with them. But then I came across Home Assistant - you can run it on a Raspberry Pi - and the addon ESPHome. At some point I have to write dedicated posts about the Firebeetle and Home Assistant…

Anyway, with ESPHome and HA it’s really easy to quickly spin up a little sensor/control/etc project. Something I had in mind for a long time, was a kind of “weather station” to monitor outside temperature, humidity and pressure. A quick Google suggested the BME280 chip. And I also needed a battery for the Firebeetle. You can directly plug in a lithium battery via a PH 2.0 JST connector.

In ESPHome you create a new device, paste in the config, connect your Firebeetle to the PC and flash it, done. Then just add the visualisation of the sensor readings in HA. That easy!

Well, working out the details of the config took me a while. One big problem of the Firebeetle is the high power consumption. It comes with lots of computing power, built-in Bluetooth, built-in Wifi, etc. But that has a price, power usage. So you want the Firebeetle to sleep most time, just wake up quickly, read the sensors, send them to HA, sleep again. And suddenly your config becomes quite a bit more complex (explanations below):

substitutions:
  my_ip: 192.168.168.160
  my_gw: 192.168.168.1
  my_sn: 255.255.255.0
  mqtt_host: 192.168.168.100

esphome:
  name: weather
  friendly_name: Weather
  on_boot:
    - priority: -300
      then:
        - script.execute: read_sensors

esp32:
  board: firebeetle32
  framework:
    type: arduino

# Enable logging
#logger:

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

#ota:
#  password: "xxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true
  manual_ip:
    static_ip: $my_ip
    gateway: $my_gw
    subnet: $my_sn

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Weather Fallback Hotspot"
    password: "xxx"

mqtt:
  broker: $mqtt_host
  username: !secret mqtt_user
  password: !secret mqtt_password
  birth_message:
  will_message:

captive_portal:

deep_sleep:
  id: sleepy

i2c:
  sda: 21
  scl: 22
  scan: true
  id: bus_a

sensor:
  - platform: bme280_i2c
    id: bme_id
    i2c_id: bus_a
    address: 0x76
    temperature:
      name: "W Temperature"
      on_value: 
        - then:
          - lambda: |-
              id(updates)++;
    pressure:
      name: "W Pressure"
      on_value: 
        - then:
          - lambda: |-
              id(updates)++;
    humidity:
      name: "W Humidity"
      on_value: 
        - then:
          - lambda: |-
              id(updates)++;
    update_interval: 60s

  - platform: adc
    id: battvcc_id
    name: "W Battery Vcc"
    pin: 
      number: GPIO34 # A2
      allow_other_uses: true
    accuracy_decimals: 2
    attenuation: 11dB
    filters:
      - multiply: 2.0
    update_interval: never
    on_value: 
      - then:
        - lambda: |-
            id(updates)++;

  - platform: adc
    id: batt_id
    name: "W Battery"
    pin: 
      number: GPIO34 # A2
      allow_other_uses: true
    accuracy_decimals: 0
    attenuation: 11dB
    filters:
      - multiply: 2.0
      - calibrate_linear:
        - 4.20 -> 100
        - 4.06 -> 90
        - 3.98 -> 80
        - 3.92 -> 70
        - 3.87 -> 60
        - 3.82 -> 50
        - 3.79 -> 40
        - 3.77 -> 30
        - 3.74 -> 20
        - 3.68 -> 10
        - 3.45 -> 5
        - 3.00 -> 0
      - clamp:
          min_value: 0
          max_value: 100
    unit_of_measurement: '%'
    update_interval: never
    on_value: 
      - then:
        - lambda: |-
            id(updates)++;

globals:
  - id: updates
    type: int
    restore_value: no
    initial_value: '0'
script:
  - id: read_sensors
    then:
      - lambda: |-
          id(updates) = 0; 
      - component.update: battvcc_id
      - component.update: batt_id
      - component.update: bme_id
      - wait_until:
          lambda: |-
            return (id(updates) >= 5);
      - deep_sleep.enter:
          id: sleepy
          sleep_duration: 60min

The main points:

  • Use fixed IP addresses, not DHCP (I just fished out an old Wifi router and set up a dedicated LAN for HA).
  • Disable logging.
  • Disable the HA API. That means you cannot update your Firebeetle ‘over-the-air’, and HA can’t read your sensor values. But we won’t need that.
  • The sensors are read by a ‘script’ - in above example that happens once every 60min - then sleep.
  • The consequence of this is, that your Firebeetle will show up as ‘not available’ and it’s sensor readings as ‘None’ (except the 2 or 3 sec out of the 3600 sec when it actually is active).
  • For that reason, use MQTT. Get the HA MQTT integration and see the mqtt bit in the config.

Some notes about the script:

It has an internal counter, which increments for each sensor value read (these are triggered byt component.update). After all 5 values are read (temperature, humidity and pressure from BME280 chip, and as little bonus the battery voltage and charge via adc (internally from pin A2)) it will send the Firebeetle into deep sleep for another 60min.

One thing which really tripped me, was this: I set up a few more Firebeetles with DHT22 sensors in the house to get temperature and humidity of different rooms. So I had a few sensors with name “Temperature” on different Firebeetles. Oh, the MQTT integration doesn’t like that! The sensor name needs to be unique! I don’t know why. On the MQTT level this is not a problem at all, but the HA MQTT integration can’t handle it. Hence note the ‘W’ in:

    temperature:
      name: "W Temperature"