Adding an old marine anemometer to HA

I’d like to share how I turned a scrap marine anemometer into a really nice wind sensor for HA !

A friend of mine replaced the anemometer on his boat, a 15 year old Navman WIND 3150. The display unit was broken and the masthead unit with the wind vane was damaged with cables torn out. The thing was on its way to the landfill. Marine anemometers are typically much more sturdy and reliable than your average weather station ones and they’re designed to report instantaneous wind with very little lag. They’re also very expensive. So I thought, hey, I’ll take the wind vane unit and see if I can hack it to make it work with HA. Would be nice to have it on the roof of my house. This would work with many other marine or higher end wind vanes too, they all work on a similar principle.

Reverse engineering the wind vane.

There were 5 cables coming out of the unit (and one was ripped off). Some quick continuity testing made it pretty clear that this wasn’t going to be the usual reed switch thing. There had to be some electronics in the wind vane itself. So time to take it apart.

The PCB on the left is the wind speed sensor, the part on the right the wind direction one. Both use rotating magnets on their respective shafts and hall sensors to detect them. I proceeded to quickly sketch out the schematics for both PCBs, as they were pretty simple.

Wind speed.

Wind speed is measured using the usual three cup setup which rotates two small magnets over a shaft on ball bearings. A single hall sensor is positioned right above the magnets on the back side of the first PCB:

I wasn’t able to find any information about the type of sensor used. The references printed on the chip were cryptic and partially unreadable. So I just hooked up my scope on its output while blowing on the cups to make them turn:

image

Ah ! Nice digital signal with a convenient amplitude for an MCU ! The signal goes high if one of the two magnets is within 45° of either side of the hall sensor. So the signal goes high and low twice per full rotation (two magnets). The easiest way to convert this to wind speed is to count the pulses over a set amount of time. That works pretty well for higher wind speeds, but it’s not very precise for low wind speeds when the cups rotate very slowly. So I decided to measure the pulse width instead and derive the rotation speed from that. The shorter the pulse, the higher the speed. I still have to do some more field testing on how well that works with very short pulses on high winds.

float GetWindSpeed()
{
  unsigned long pw = pulseInLong(2, HIGH, 3000000);

  float v = pw ? 1000000.0 / pw * f_a : 0.0;

  return v;
}

I measure the time (in microseconds) that a pulse is high. That is the time the cups take to do a 90° turn. Now you could use basic math to derive the wind speed from the angular speed if you measure the length of the arms from the shaft to the center of the cups. But that would heavily underestimate the real wind, because it doesn’t account for friction losses in the anemometer mechanics. Typically higher end anemometers specify an anemometer factor (fa) which depends on things like size, shape of the cups, shaft bearing friction, etc. Of course, since this is an old scrap part, I have nil info on that. So I had to calibrate f_a manually, by comparing with another calibrated anemometer. I chose knots as standard unit. You could chose anything or convert it later in a HA template.

I average this speed over 30 seconds before reporting it to HA.

Wind direction

The wind vane assembly uses a single rotating magnet and two hall sensors set 90° apart to determine the direction the wind is coming from:

The two sensor output signals looked like this when I started turning the wind vane around in circles:

image

OK, so these hall sensors are analog ones. The output signal move along a sine pattern depending on how close they are to the N or S pole of the magnet respectively. The two sensor signals have a phase shift due to the 90° positioning to each other. Connecting the two signals on two analog inputs of the MCU, I could calculate the angle of the wind vane using basic trigonometry, with an arctan operation. But there was one problem. The amplitude is not normalized ! You might notice how the amplitude of the two sensors is not exactly the same. And there are cable losses and noise, this is analog stuff after all. So another calibration process was needed. A simple min-max detection for each signal individually, by slowly turning the vane a few times around in a circle. The values won’t change much, so they can be hardcoded in the firmware.

float GetWindAngle()
{
  int p0 = analogRead(A0);
  int p1 = analogRead(A1);

  // Normalize the amplitudes using the calibration data
  float w0 = float(p0 - p0_min) / float(p0_max - p0_min);
  float w1 = float(p1 - p1_min) / float(p1_max - p1_min);

  // Remap into the [-1,1] range
  w0 = min(max(w0 * 2.0 - 1.0, -1.0), 1.0);
  w1 = min(max(w1 * 2.0 - 1.0, -1.0), 1.0);

  // Get the angle in radians
  float w = atan2(w0, w1);

  // Convert to degrees and wrap/clamp to valid range
  w = 360.0 * w / (2.0 * M_PI) + 180.0;

  if ( w >= 360.0 ) w -= 360.0;
  if ( w < 0 ) w = 0;

  return w;
}

I also average the wind direction over 30 seconds before reporting it to HA. This also filters out any small noise on the cables and the ADC.

Wind gusts

Wind doesn’t always stay the same, it fluctuates a lot. When it suddenly increases over a short amount of time, then this is called a gust. There are several ways to measure this, but I used the method recommended by the World Meteorological Organization. That is to take wind speed averages over 3 second periods and report the maximum of these averages within a sliding window of the last 10 minutes.

This needs a wind speed history buffer of 200 samples (200 x 3 second average = 10 minutes). I’m using a circular buffer, similar to the usual sliding average filter. But instead of averaging it, I take the maximum.

struct GustHistory {
  static const int count = 200;
  float     samples[count] = { 0.0 };
  int       index = 0;
};

GustHistory gustHistory;

// ------

// In the acquisition loop:

// Read current wind speed
float v = GetWindSpeed();

// Accumulate for 3 seconds
v_avg += v;
v_cnt++;

// After 3 seconds, add the average to the 10 minute circular buffer and extract the gust
if( millis() - t > 3000UL ) {   // 3 secs

    t = millis();

    // Calculate the average over 3 seconds
    v_avg = v_cnt ? v_avg / v_cnt : 0;

    // Add to the history buffer
    gustHistory.samples[gustHistory.index] = v_avg;
    if( ++gustHistory.index == gustHistory.count ) gustHistory.index = 0;

    // Get the highest 3 second speed over the last 10 minutes, that’s our gust    
    float v_gust = 0;
    for( int a = 0; a < gustHistory.count; a++ ) v_gust = max(gustHistory.samples[a], v_gust);

    // Report it to HA only if it changed (avoid spamming the network)
    if( v_gust != last_gust ) {
      PublishTopic(“wind_gusts”, v_gust);
      last_gust = v_gust;      
    }

    // Prepare for next 3 second acquisition
    v_avg = 0;
    v_cnt = 0;
        
}

Getting the data into HA.

I decided to use MQTT to supply the data (speed, direction, gust) to HA because pretty much everything in my home uses MQTT to communicate with HA. A simple way would be to implement this on an ESP and connect it to HA over wifi. I decided to use an Arduino with an Ethernet shield instead, powered by PoE.

On the software side, I used the PubSubClient MQTT library to build and publish the MQTT topic and payload.

A sample set of MQTT topics looks like this:

hcnode/anemometer/wind_speed/state { "state" : "7.6" }
hcnode/anemometer/wind_gusts/state { "state" : "25.7" }
hcnode/anemometer/wind_direction/state { "state" : "218.2" }

On the HA side, nothing too special:

sensor:

  - platform: mqtt
    name: "Wind speed"
    unit_of_measurement: "kts"
    state_topic: "hcnode/anemometer/wind_speed/state"
    expire_after: 3600
    value_template: "{{ value_json.state | float }}"

  - platform: mqtt
    name: "Wind gusts"
    unit_of_measurement: "kts"
    state_topic: "hcnode/anemometer/wind_gusts/state"
    expire_after: 3600
    value_template: "{{ value_json.state | float }}"

  - platform: mqtt
    name: "Wind direction"
    unit_of_measurement: "°"
    state_topic: "hcnode/anemometer/wind_direction/state"
    expire_after: 3600
    value_template: "{{ value_json.state | float }}"

I’m not using availability topics, but a simple expire delay, as the wind data is send in regular intervals anyway.

image

Or on my phone, using Tileboard. The wind direction is displayed as a little turning compass needle.

I hope this gave you some ideas and inspiration for hacking your own older devices and give them a second life with HA !

11 Likes

I really like what you did here. I have integrated my Davis Vantage Pro 2 into HA and am looking for a better way to display wind direction, speed and gusts. What card did you use for that? You only say “turning compass needle”. I am currently using https://github.com/tomvanswam/compass-card but would really like to add the gusts.

Excellent project. It looks like the Ethernet module you are using is an ENC28J60 based on the red board which directly accepts the Nano. You may find that it is highly unreliable. In my experience (and many others confirmed with a search for “enc28j60 freeze” or similar) they stop responding. Might run for weeks, days or hours. But eventually stops passing network traffic. There are various causes for this if you research and the fixes, at least in my experience, don’t correct the issue.

Hey, thanks ! The dashboard on my phone is not Lovelace, so it’s not a card. I’m using Tileboard, which is a custom Javascript based dashboard for Home Assistant. Tileboard allows you to create your own tiles very easily with JS and CSS. The compass tile is based on Tileboard’s own weather tile with two layered images. The base compass is fixed, the needle image is rotated with the CSS transform rotate function, the angle is supplied by the wind angle sensor value from HA.

I don’t find it unreliable at all. I have several of those running 24/7, some of them for years without a reboot, and never experienced any kind of issue with them.

What library are you using? I’ve never had any reliability with these modules over multiple networks and if there is a software fix for the one I have left (in a controller at the far end of my shop roof crawl space accessible only after a 50 foot belly crawl in insulation) that would be great.

I’m using UIPEthernet. As I mentioned, I never had a single issue with any of those modules, I found them to be very reliable. One of them is connected to the switch over 120m of CAT6, which is out of the Ethernet specifications. Still, it never failed in about two years now. You have to make sure to give it enough power, it draws a bit more than other chipsets (about 180mA when transmitting). It needs its own voltage regulator, the Nano can’t provide enough.

Damn, was hoping there was some magic library out there I wasn’t aware of. The modules I used are the common red modules into which the Nano plugs. They have their own 3 pin 3.3V regulator. Oh well, it means traversing the crawl space with a Nano on a Wiznet module dragging a bin full of soldering stuff behind me.