A Davis Vantage Pro wireless weather station on a green tripod in a grassy field: anemometer on a short arm at the top, black rain collector above the white radiation shield, and a small solar-panel box on one leg

My 20 year old Davis Vantage Pro2 died a decade ago and I thought I would resurrect it to help automate Home assistant tasks based on the weather. I had to replace the super capacitor (used a 20F) and the Temp/Humidity unit. I plugged the console USB into HA and it sort of worked but the transmitter was very patchy even in close proximity – I assume it had also degraded and was only catching about one packet in five.

Close-up of the Davis ISS SIM transmitter board: CR123A battery, blue 2.7 V 20 F supercapacitor and the radio section
Supercap, CR123A and the radio

The plan

The sensors on a Vantage Pro2 are surprisingly simple. The anemometer is a reed switch that closes once a revolution, plus a 20 kΩ potentiometer for the vane. The rain gauge is another reed switch on the tipping bucket. The solar sensor puts out a voltage proportional to sunlight. Only the temperature/humidity board is clever, and that’s the one you don’t want to touch (more below).

So the new setup is an ESP32 in the ISS box counting the two reed switches, an ADS1115 converter reading the vane and the solar sensor, and an Adafruit SHT45 in the radiation shield in place of the Davis temperature board (Giving higher accuracy as well). It reports to Home Assistant over Wi-Fi and runs off 5 V USB, so no radio, no console, no supercap and no CR123A. Every sensor keeps its own Davis cable and RJ11 plug, and the old SIM board stays in the box in case I ever want to go back. Only downside is the power draw is higher than the original ISS but easily dealt with with a small battery and solar panel.

Breadboard first

Before ordering anything I built the whole circuit on a breadboard and tested each sensor in turn. A few things came out of that.

The anemometer – Davis quote 2.25 mph per pulse per second, which works out at 3.621 km/h per Hz. The vane is linear: four 90° steps read 0.244, 0.260, 0.251 and 0.245 of full scale. Reading it as a ratio of the wiper to its own supply cancels out any supply drift. There’s a dead band of a few degrees at north where the wiper leaves the track and the reading wanders, so the firmware clamps it and takes a median.

The rain gauge was the surprise. The reed switch closes twice every time the bucket tips, about 0.1 to 0.4 seconds apart, so my first 100 ms debounce counted every tip twice. A 750 ms lockout fixed it and still copes with rain far heavier than anything real (around 960 mm/h). A closure now has to last at least 20 ms before it counts.

The board

With the breadboard working I drew a carrier board in KiCad. The ESP32 DevKit and the ADS1115 plug into sockets, three RJ11 jacks take the Davis plugs, and the rest is a handful of resistors, capacitors and diodes. Five boards (min order) from JLCPCB cost a few euros plus shipping – proved incredibly easy to do and arrived as promised within 10 days.

Bare carrier PCB v0.1, top side: ESP32 DevKit footprint, ADS1115 sockets, resistor and diode positions, and three RJ11 jacks labelled WIND, RAIN and SUN
QtyPartUse
1ESP32 DevKit V1, 30-pincontroller
1Adafruit ADS1115, STEMMA QT (1085)vane and solar, 16-bit
1Adafruit SHT45 with PTFE membrane (6174), 400 mm STEMMA QT cabletemperature and humidity
3RJ11 6P6C PCB jack, SparkFun PRT-00132WIND, RAIN, SUN
51N5817 Schottky diode4 input clamps, 1 solar supply drop
5 + 21 kΩ + 10 kΩ, ¼ Wseries protection, pull-ups
2 + 210 nF + 100 nF ceramicinput filters, decoupling
Female headers: 1×15 ×2, 1×6 ×2sockets for the ESP32 and ADS1115
1Carrier PCB (Gerbers below)58.4 × 88.9 mm, 2-layer, through-hole

The modules and connectors came to a little over €40 from Kiwi Electronics; the resistors, capacitors and headers came out of a cheap assortment kit.

SensorWireFunctionESP32 side
Wind (6410)blackspeed reed, 1 pulse per revolutionD27: 10 k pull-up, 1 k series, 10 nF, two 1N5817 clamps
redcommonGND
greenvane wiperADS1115 A0 via 1 k
yellowvane supplyADS1115 A+, sensed on A1 via 1 k
Rainred, green+yellowreed switchD26: same input circuit as wind, red to GND
Solar (6450)yellowsupply, 3 V ±10 %3V3 through a 1N5817: 3.14 V at 0.67 mA
green1.67 mV per W/m²ADS1115 A2 via 1 k
red, blackgroundGND
SHT45STEMMA QT cable into the ADS1115I²C 0x44D21 SDA, D22 SCL, shared with the ADS1115 at 0x48
Assembled carrier board v0.1 with the ESP32 DevKit plugged in, resistors, diodes and capacitors fitted, three RJ11 jacks, and the Adafruit SHT45 sensor on its cable
Built and running: ESP32 DevKit, and the SHT45 on its cable. One error with the ADC board that I had was the headers were on backwards so it got put on the backside of the PCB

Gotchas

  • Don’t wire the Davis temperature/humidity board (7346.070) up as I²C. It reportedly uses Sensirion’s old SHT1x “Sensibus” protocol, and at least one person has cooked theirs trying. The SHT45 costs about €15 and has a better accuracy spec anyway. I have put mine in the housing underneath the rain gauge and it seems to work fine. The Davis replacement is over 70 Euro.
  • The ADS1115’s A+ and A− pins are supply outputs, not inputs. The vane’s supply comes from A+, so the rain gauge in the wind jack would short the supply on every tip. The jacks are labelled, but unplug the power before swapping plugs.

Building it

  1. Resistors, diodes and capacitors first (diode band to the square pad), then the sockets, using the modules as jigs, then the jacks.
  2. On the bare board, check 3V3 to GND isn’t shorted.
  3. Flash ESPHome over USB and add the device in Home Assistant. Temperature and wind direction should show values; if not, check I²C (SHT45 at 0x44, ADS1115 at 0x48).
  4. Power off, plug in the sensors, power on.

The firmware

The ESPHome config below has the same sensor logic I tested on the bench, plus the Home Assistant connection, a rain total that survives restarts, and a north offset for the vane. It compiles on ESPHome 2026.9.1. Set north_offset to the bearing the anemometer arm points to (0 if it points north, as Davis recommend).

# Davis Vantage Pro2 ISS read directly by an ESP32 (no SIM board, no radio)
#
# Carrier PCB: ESP32 DevKit V1 (30-pin), Adafruit ADS1115 (1085) at 0x48, Adafruit SHT45 at 0x44.
# Pins: D27 wind speed, D26 rain, D21 SDA, D22 SCL. ADS1115: A0 vane wiper, A1 vane supply, A2 solar.
# Settings proven on the bench (Oct 2026): wind 1 ms pulse filter; rain 20 ms minimum closure plus a
# 750 ms lockout (the rain reed closes twice per tip, 0.11-0.36 s apart).
substitutions:
device_name: davis-iss
friendly_name: Davis ISS
# Bearing the anemometer arm points to (0 if it points north, as Davis recommends).
north_offset: "0"
rain_lockout_ms: "750"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
on_boot:
priority: -100
then:
- lambda: |-
id(rain_total).publish_state(id(rain_tip_count) * 0.2f);
id(rain_rate).publish_state(0);
esp32:
board: esp32dev
framework:
type: esp-idf
logger:
level: INFO
api:
encryption:
key: !secret api_encryption_key
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: ${friendly_name} Fallback
password: !secret fallback_ap_password
captive_portal:
i2c:
sda: GPIO21
scl: GPIO22
frequency: 100kHz
ads1115:
- address: 0x48
id: ads
globals:
# Rain tips since first flash; survives restarts.
- id: rain_tip_count
type: uint32_t
restore_value: true
initial_value: "0"
- id: rain_last_tip_ms
type: uint32_t
initial_value: "0"
- id: rain_closures_seen
type: float
initial_value: "0"
sensor:
# ---- Wind speed: reed switch, one pulse per revolution ----
- platform: pulse_meter
id: wind_speed
name: Wind speed
pin:
number: GPIO27
inverted: true
mode:
input: true
pullup: true
# Closed and open for at least 1 ms to count: ignores bounce. ~1.6 ms closure at 200 mph.
internal_filter: 1ms
internal_filter_mode: PULSE
timeout: 10s
unit_of_measurement: km/h
device_class: wind_speed
state_class: measurement
accuracy_decimals: 1
# Davis: 2.25 mph per Hz = 3.621 km/h per Hz. pulse_meter reports pulses per minute.
filters:
- multiply: 0.060352
# ---- Rain: reed switch, 0.2 mm per tip ----
# The pulse meter counts closures of at least 20 ms (timed in the interrupt); the lambda then drops
# any closure within the lockout of the last counted tip.
- platform: pulse_meter
id: rain_closures
internal: true
pin:
number: GPIO26
inverted: true
mode:
input: true
pullup: true
internal_filter: 20ms
internal_filter_mode: PULSE
timeout: 15min
total:
id: rain_closures_total
internal: true
on_value:
- lambda: |-
float seen = id(rain_closures_seen);
id(rain_closures_seen) = x;
if (x <= seen) return;
uint32_t now = millis();
uint32_t since = now - id(rain_last_tip_ms);
if (id(rain_last_tip_ms) != 0 && since < ${rain_lockout_ms}) return;
if (id(rain_last_tip_ms) != 0)
id(rain_rate).publish_state(0.2f * 3600000.0f / since); // Davis-style rate
id(rain_tip_count) += 1;
id(rain_last_tip_ms) = now;
id(rain_total).publish_state(id(rain_tip_count) * 0.2f);
- platform: template
id: rain_total
name: Rain total
unit_of_measurement: mm
device_class: precipitation
state_class: total_increasing
accuracy_decimals: 1
update_interval: never
- platform: template
id: rain_rate
name: Rain rate
unit_of_measurement: mm/h
device_class: precipitation_intensity
state_class: measurement
accuracy_decimals: 1
update_interval: never
# ---- Wind direction: vane pot read as a ratio (A0 wiper / A1 supply), so supply drift cancels ----
- platform: ads1115
ads1115_id: ads
id: vane_wiper
multiplexer: A0_GND
gain: 4.096
update_interval: never
internal: true
- platform: ads1115
ads1115_id: ads
id: vane_supply
multiplexer: A1_GND
gain: 4.096
update_interval: never
internal: true
- platform: template
id: wind_direction
name: Wind direction
unit_of_measurement: "°"
state_class: measurement
accuracy_decimals: 0
update_interval: never
lambda: |-
float s = id(vane_supply).state, w = id(vane_wiper).state;
if (std::isnan(s) || std::isnan(w) || s < 1.0f) return NAN;
return std::min(std::max(w / s, 0.0f), 1.0f);
# The pot has a few-degree gap at north where the wiper floats and reads noisy: a median of 3
# removes the odd outlier.
filters:
- median:
window_size: 3
send_every: 1
- lambda: return fmodf(x * 360.0f + ${north_offset} + 360.0f, 360.0f);
# ---- Solar radiation: Davis 6450, 1.67 mV per W/m², supply 3V3 through a 1N5817 (~3.1 V) ----
- platform: ads1115
ads1115_id: ads
id: solar_volts
multiplexer: A2_GND
gain: 4.096
update_interval: 10s
internal: true
- platform: copy
source_id: solar_volts
name: Solar radiation
unit_of_measurement: W/m²
device_class: irradiance
state_class: measurement
accuracy_decimals: 0
filters:
- lambda: return std::max(x / 0.00167f, 0.0f);
- sliding_window_moving_average:
window_size: 6
send_every: 6
# ---- Temperature and humidity: SHT45 in the Davis radiation shield ----
- platform: sht4x
address: 0x44
precision: High
update_interval: 30s
temperature:
name: Temperature
accuracy_decimals: 1
humidity:
name: Humidity
accuracy_decimals: 1
- platform: wifi_signal
name: Wi-Fi signal
update_interval: 60s
- platform: uptime
name: Uptime
interval:
# Read the vane's two channels back to back, then work out the direction.
- interval: 2s
then:
- component.update: vane_wiper
- component.update: vane_supply
- component.update: wind_direction
# Rain rate falls back to 0 after 15 min without a tip.
- interval: 60s
then:
- lambda: |-
if (id(rain_last_tip_ms) != 0 && millis() - id(rain_last_tip_ms) > 15UL * 60UL * 1000UL &&
id(rain_rate).state != 0.0f)
id(rain_rate).publish_state(0);

Where it’s at

The board is built and all four sensors have been tested on it on the bench; It’s reporting into Home Assistant consistently via the native ESP32 integration. Much slicker and more controllable than the old USB integration. The Console is in the cupboard.

Gerbers (v0.2) – 2-layer, 1.6 mm FR-4, any fab.

Acknowledgements

This stands on other people’s work. Thanks to:

Header photo: “Davis Instruments weather station” by Cjp24, via Wikimedia Commons, licensed under CC BY-SA 4.0. Unmodified.

Leave a Reply