Electronic Windvane

This project documents how to make a windvane to sense the wind direction. It was designed for an autonomous sailboat but could be used in other applications. The structure is mostly 3D printed and uses a gopro mount to attach to the hull. The main sensor is a magnetic encoder that outputs an analog voltage in the 0-5V range. For my application I used a voltage divider to bring that down to 0-2.5V because my autopilot is based on 3.3V logic. I also have an arduino program to output the vane angle as NMEA 0183 serial data; I don’t use this on my boat but it is a standard digital interface that might be helpful for others.

Encoder and Earlier Attempts

The heart of this sensor is the magnetic encoder model P3022. You can find it on amazon and other places, but Marlin P. Jones has good deals so I get them there. It is very low-friction and has a nice analog output so it’s super easy to use. I wish there was a 3.3V version, the 5V output is too much for most modern microcontrollers, but the output power is sufficient to drive a voltage divider without significant loading error as far as I can tell.

angle sensor

I spent some time designing a vane around a less-integrated sensor, the AS5600 (affiliate link). This is a nice sensor, and much cheaper than the P3022, but you need to integrate your own magnet and bearings. I found that to 3D print an accurate axle, it needed to be pretty big, which required a large, heavy bearing. My designs using a 1/8 inch (3mm) shaft and small bearing were too wobbly to be reliable. I think a brass shaft would have worked, or maybe a design with 2 bearings, but unless you need to make a large quantity I think the simplicity of the integrated sensor pays for itself.

AS5600 Prototypes

Vane Design

I drew up a design for the vane to 3D print. I had a few ideas to implement:

  • Covers that overlap to keep water from coming in (unless you spray upwards)
  • Base is a standard GoPro mount because these are easy to find and stick on, and makes the vane easily removable for transport.
  • I used a fiberglass tube from Tap Plastics for the strut, it’s much lighter than PVC pipe or something 3D printed.
  • In order to keep the balance weight low, I made the vane’s tail from a foam take-out container rather than printing it.
  • I printed 1/4-20 threads into the end of the vane to add an adjustable balance weight. Balancing the vane is important to keep the angle from changing if the vane is tilted. The threads printed okay but I had to clean them out, and I also had to cut the vane tip shorter because the nut was too heavy otherwise.
  • I printed it in PETG for the good hot day tolerance compared to PLA
Vane Parts Animation


Parts & Materials List

Build Steps

Wire up the sensor. Make sure the wires are long enough! Don’t put a connector on the ends of the wires until you’ve fished them through the strut.

Mount the sensor into the cup. I wrapped a few wraps of electrical tape around the sensor to make it a tight fit in the cup. I added a little bit of E6000 to the top to keep it from spinning but didn’t use so much glue that it won’t come apart if needed. Ideally the “dead zone” between 0-5V should line up with head-to-wind on your sailboat since you won’t be sailing that way anyway. In practice the dead zone is pretty small so it will probably work fine with an offset added in the software.

Parts showing tape and wires on sensor

Test fit the cap. I had to drill it out a little so it wouldn’t drag on the bearing. I didn’t glue the cap on, it seems pretty tight, but I would recommend glue before any offshore sailing just to be more watertight.

Cap on cup, and vane

Cut the strut to the right length. Your choice! Drill a hole in the bottom of the strut for the wire to exit. The hole should be above the base piece or you will have to drill that too. I angled the hole down so the wire comes out without a sharp bend (drill perpendicular, then put a hand drill in and rotate the bit).

Run the wires out the hole and glue the cup and base to the strut with some E6000 or epoxy. I used E6000 to seal around the wires and a zip tie for strain relief. Make sure that the sensor is level before gluing and tape the pieces down so they won’t spin while the glue dries!

Sensor and cup in place, test fitting wires

Cut a tail for the vane. Bigger is better as long as it won’t bump into anything when it rotates. I used foam from a takeout container but a meat tray or something similar could work too– lighter is better. Sand the edges and any texture off so it looks nice. Glue to the vane with 5 minute epoxy- E6000 will dissolve foam so do not use it here!

foam for vane tail
Recycled foam for the tail
Wait for glue to dry: Cup and base glued with E6000, Vane tail glued with 5 minute epoxy

Mount the vane on the sensor, it should press fit but use glue as needed– if it spins the angles will be wrong! Balance the sensor so that it won’t swing when tilted using a 1/4-20 nut or whatever is handy. I had to clean up the threads with a thread-cutter first but maybe your printer works better than mine. I also had to cut/glue the vane shaft to make it shorter to put the threads in the right place with the nut I used. Then I cut it too short (because I forgot about the weight of the plastic removed) and had to add a washer. Maybe I should put threads on the whole front of the vane to give more adjustment range…

Ready for the final step

Voltage Divider

A common circuit to reduce voltages is the voltage divider. I used two 10KΩ resistors to cut the output in half. That is more reduction than needed for a 3.3V circuit. I could not find any specification on the max current the sensor can supply, so I used large resistors to be sure it would be okay, even though this could cause a loading error when attached to the ADC. Seems fine though. Maybe something like 6kΩ and 4kΩ (or 3kΩ and 2kΩ?) arranged to give a gain of 0.6 would be better for resolution and loading considerations.

Voltage divider wired in place

NMEA 0183 Output

Prototype sensor connected to Arduino which is connected to OpenCPN

I wrote an arduino code to take the analog input and output a serial NMEA windvane sentence (SDMVW). It’s a modification of my speed/depth sensor code project, simplified by eliminating the calibration routine over serial (constants are hard-coded) and obviously sending the new sentence. This sentence also could send wind speed but there’s no data for that so far.

I’ve tested the code on arduino nano (old style) and uno, but it should work on anything with serial and analog-digital conversion if you update the parameters (and the ADC resolution if it’s not 10 bit). Note that since the sensor is 5V you should use it on a 5V arduino unless you include a voltage divider to reduce the sensor voltage. If you do use a 5V arduino you will need to convert the TTL serial voltage (voltage divider or logic shifter) to work with most 3.3V flight controllers (Pixhawk etc.) The USB-serial found on Arduinos makes this work really well for PC or Raspberry Pi connections.

// This code is a NMEA 0183 wind direction instrument
// Mike Holden mholden@csum.edu
// began 3/17/10 as speed/depth nmea sender https://www.holdentechnology.com/2017/09/02/nmea-0183-speedometer/
// Changes:
//  05/02/10 added factory reset to program eeprom initially (instead of commenting in/out stuff)
//           put checksum in eeprom to tell if it's been programmed.
//           fixed up calibration routine to work better
//  05/17/10 Added fix so missing pulses make speed go to zero.
//  05/24/12 updated for arduino 1.0
// hacked 3/2020 for wind sensor
//  01/03/2020 cleaned up for wind sensor

// Hardware:
int LEDPin = 13; // there should be an LED on 13 already

long baud;
int deg, degtenths;
float wind_dxn;
float print_time = 0;
char nmeastring[50] = "$VWVHW,,T,,M,0.00,N,0.00,K*";
int jj;
char checksum=0xAA;

// Popular Adjustments:
#define BAUD 4800   // serial port baud rate
#define PRINTDT 10 // ms between serial output (set to 1000 for 1 Hz output)
#define MAXVOLTS 3.3  // max voltage expected from vane (5 or less)
#define MINVOLTS 0.0  // min voltage expected from vane (0 or more)
#define DEGOFF -160.0 // Zero point for vane adjust
// given a voltage V, the angle is determined by:
// This equation is then wrapped to 0-360 degrees

// The setup() method runs once, when the sketch starts
void setup()   {                
  // initialize the digital pin as an output:
  pinMode(LEDPin, OUTPUT);     

//  dt_char= 20000/(float)baud; // ms between output characters; minimum ~10/baud*1000
  Serial.println("  www.holdentechnology.com");
  Serial.println("  meh...surement system model W");

  digitalWrite(LEDPin, HIGH);   // set the LED on
  delay(1000);                  // wait for a second
  digitalWrite(LEDPin, LOW);    // set the LED off
  delay(1000);                  // wait for a second

// the loop() method runs over and over again,
// as long as the Arduino has power
void loop()                     
    //MWV - Wind Speed and Angle
    //        1   2 3   4 5
    //        |   | |   | |
    // $--MWV,x.x,a,x.x,a,a*hh<CR><LF>
    //Field Number:
    //1.Wind Angle, 0 to 359 degrees
    //2. Reference, R = Relative, T = True
    //3. Wind Speed
    //4. Wind Speed Units, K/M/
    //5. Status, A = Data Valid, V = Invalid
    //6. Checksum
    //Example: $WIMWV,214.8,R,0.1,K,A*28
    // make NMEA speed string

    // read voltage as 10 bit int (0-1023), convert to 0-360 degrees
    // See parameter defs above
    wind_dxn = ((float)analogRead(A0))*5.0/1023.0; // volts input
    wind_dxn = (wind_dxn - MINVOLTS)*360.0/(MAXVOLTS-MINVOLTS);
    wind_dxn += DEGOFF; // correct offset
    if (wind_dxn < 0) wind_dxn+=360.0; // correct wrap
    if (wind_dxn > 360) wind_dxn-=360.0; // correct wrap
//    wind_dxn = 180*cos(float(millis())*1.0/10000.0)+180; //HACK!!! fake data

    deg = (int) wind_dxn; // gets around lack of floating point in arduino's sprintf
    degtenths = wind_dxn*100 - deg*100;


    // checksum math (see NMEA0183 references)
    checksum = 0;
    while (nmeastring[jj+1]!=0)
      checksum ^= nmeastring[jj];
  // Print NMEA sentence.
  // periodically send the string + checksum
   if (millis() > print_time)
      print_time = (float)millis() + PRINTDT;
      //Serial.println("$GPRMC,200258,A,3754.4088,N,12223.0474,W,4.9,266.5,300712,15.1,E,D*3F"); // HACK!!! for testing
      if (checksum < 0x10) Serial.print('0');

} // end of loopCode language: Arduino (arduino)
Not quite ready to sail, but closer!

3 thoughts on “Electronic Windvane

  1. Hollevoet Thierry says:

    Hello, first of all, I am a mechanic, electronics are a hobby of mine, I am from Belgium and I have build a wind direction vane with the AS5600. Test with a AS5600 test program works 100%.
    But here it comes. My knowledge of arduino is not so good. In Your article there is something about a prototype with AS5600. So maybe there is a sketch for arduino with the AS5600. Is there a possibility to send me the sketch? I find nothing on the internet and my project is stranded over the software. If you respond, I will send you fotos os the mechanical side of my project.
    Please excuse me for my poor English .
    Your sincerely

    • Mike Holden says:

      Hi! Sorry I missed this comment! I think I used the PWM output of the AS5600 so in the arduino the pulseIn function did the work for me.

      int sensorValue = pulseIn(7,HIGH);

Leave a Reply

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