HD-WSD-MI-01 Ultrasonic Anemometer

HD-WSD-MI-01 Ultrasonic Anemometer

I recently bought this ultrasonic anemometer off AliExpress, to replace a Davis wind cup/vane setup where I used ESPHome to read the pulses and the potentiometer resistance. It was relatively cheap and looked reasonable quality compared to others available.

It comes with instructions which are quite good, basically you power it with ~9-12V and use read some modbus registers over RS485.

The unit needs to be mounted on a vertical pole, and seems to be based on a 1.5" BSP tube (ie. 48.3mm scaffold tube). Slightly smaller tubing would work (there is some slack internally), but I wanted it to fit securely so I cut a short piece of scaffold tube and welded it to the 1" steel tubing I am using, leaving space for the cable down the middle. The plastic is fairly thin, so I only didn't tighten the nuts as much as I otherwise would have. Given the unit has a threaded cable connector in the base there's no real chance of it being shaken off the top of the pole.

I try and use ESPHome as much as possible, and this device is a perfect use case for it, so I wrote a configuration file and (after some fiddling)... it worked! The wind speed means the speed is displayed in km/h in Home Assistant. The direction is just a number, so you'll need to convert it to a heading separately. There's also a nice HACS compass card.

Here's my ESPHome configuration. I have found this style of RS485 board the most reliable. It handles DE/RE (driver enable/receiver enable) automatically so you just need to feed it the data. Also it has 2x LEDs (TX/RX) so you can very easily see when it's receiving data, which removes an entire layer of problem solving.

uart:
  id: anemometer_uart
  tx_pin: D2
  rx_pin: D1
  baud_rate: 9600
  stop_bits: 1

modbus:
  id: anemometer_modbus
  uart_id: anemometer_uart

modbus_controller:
  - id: anemometer_controller
    address: 0xFF
    modbus_id: anemometer_modbus
    update_interval: 1s

sensor:
  - platform: modbus_controller
    modbus_controller_id: anemometer_controller
    name: Wind Speed
    id: wind_speed_ms
    unit_of_measurement: "m/s"
    device_class: wind_speed
    register_type: holding
    address: 0x000C
    value_type: U_WORD
    accuracy_decimals: 1
    filters:
      - multiply: 0.01
      - sliding_window_moving_average:
          window_size: 5
          send_every: 5

  - platform: modbus_controller
    modbus_controller_id: anemometer_controller
    name: Wind Direction
    id: wind_direction
    unit_of_measurement: "°"
    register_type: holding
    address: 0x000D
    value_type: U_WORD
    accuracy_decimals: 0
    filters:
      - multiply: 0.1
      - throttle: 5s
💡
If you view the modbus debug in ESPHome, do be aware that the modbus controller will optimise the requests by combining multiple registers into a single request - so for the config below it will send a one command to retrieve the two registers together rather than two commands for each one individually.

This template sensor will record wind gusts based on the instantaneous wind speeds. I used the general meteorology standards for what a "wind gust" is:

  template:
  - trigger:
      - platform: state
        entity_id: sensor.wind_speed
    sensor:
      - name: "Wind Gust"
        unique_id: "sensor.wind_gust"
        unit_of_measurement: "kph"
        state: >
          {% set current_speed = states('sensor.wind_speed_2') | float %}
          {% set average_speed = states('sensor.wind_speed_avg') | float %}
          {% if current_speed > (average_speed + 16) and current_speed > 30 %}
            {{ current_speed }}
          {% else %}
            0
          {% endif %}
          

You'll need this sensor, which is a 2-minute wind speed average:

- platform: filter
  name: "Wind Speed 2 Minute Average"
  entity_id: sensor.wind_speed_2
  filters:
    - filter: time_simple_moving_average
      window_size: "00:02"
      precision: 2
 
💡
Note that "chatty" sensors will quickly fill up your Home Assistant state logs. Either exclude them if you just want to view live data, use an external database (not the built-in sqlite), or use a throttled+averaged sensor derived from the original, and exclude the original.

Results

Annoyingly, there hasn't been much wind since I installed it, however it is at least giving a speed/direction output vaguely in the range I'd expect. I will update here. I intended to leave the old ones up as a comparison but in the end couldn't be bothered to sort out the cabling. I wish I had now!

Davis Vantage Pro wind sensors

For reference, here's my old ESPHome configuration with the Davis Vantage Pro equipment:

sensor:
# Davis Vantage Pro wind direction/weather vane sensor
  - platform: custom
    lambda: |-
      auto wind_sensor = new WindDirectionSensor();
      App.register_component(wind_sensor);
      return {wind_sensor};
    sensors:
      name: "Wind Direction"
      accuracy_decimals: 0
    
# Davis Vantage Pro tipping-bucket rain sensor
  - platform: pulse_counter
    pin: 
      number: GPIO15
      mode: INPUT_PULLDOWN
    unit_of_measurement: 'mm'
    name: 'Rainfall'
    id: rainfall
    count_mode:
      rising_edge: DISABLE
      falling_edge: INCREMENT
    internal_filter: 13us
    filters:
      - multiply: 0.04233  # 0.254ml / 6 (the update interval fraction)
      - debounce: 2s
    update_interval: 10s

# Davis Vantage Pro wind speed sensor
  - platform: pulse_counter
    pin: 
      number: GPIO19
      mode: INPUT_PULLUP
    unit_of_measurement: 'mph'
    name: 'Wind speed'
    id: wind_speed
    count_mode:
      rising_edge: DISABLE
      falling_edge: INCREMENT
    internal_filter: 13us
    filters:
      - multiply: 0.07440 # 1 pulse per second = 1m/s / 2.23694 (to get mph) / 6 (the update interval fraction)
      - debounce: 100ms
    update_interval: 10s
    accuracy_decimals: 0

And the referenced "WindDirectionSensor.h" component. Be aware that the analogue inputs of the ESP32 are not terribly linear, it would be much better to use an external I2C ADC, or at least calibrate it by setting the vane to various positions and recording the result. Also I'm sure running the 3.3V over a long cable is not a great way to do it either! It did seem to work "good enough" though.

#include "esphome.h"
#include "math.h"

#define WindVanePin (34) // The pin the Davis Vantage direction/vane sensor is connected to

int VaneValue; // raw analog value from wind vane
int Direction; // translated 0 - 360 direction
int LastValue; // last direction value

class WindDirectionSensor : public PollingComponent, public Sensor {
	public:
	
	// constructor
	WindDirectionSensor() : PollingComponent(10000) {}

	void setup() override {
		LastValue = 0;
		Direction = 0;

	}	
  
	void update() override {
		//ESP_LOGD("custom", "update");

		getWindDirection();

		// Only update the display if change greater than 5 degrees.
		if(abs(Direction - LastValue) > 5) {
			LastValue = Direction;
		}


		ESP_LOGD("custom", "Direction %d", Direction );
	
		
		publish_state(Direction);

	}

	// Get Wind Direction as degrees
	void getWindDirection() {

		VaneValue = analogRead(WindVanePin);
		Direction = map(VaneValue, 0, 4096, 0, 359);
		
		if(Direction > 360)
		Direction = Direction - 360;

		if(Direction < 0)
		Direction = Direction + 360;

        ESP_LOGD("custom", "VaneValue %d", VaneValue );
	}

  
};