OpenMarine

Full Version: Write my own wind sensor code
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Hello everyone, i know some of you helped choosing a wind sensor here, and helped me configuring it with @seandepagnier's "weather sensor" here. But i wanted to go one step forward (just for fun a because learning new stuff is cool), and designed the simplest posible pcb with an ESP32, installed SensESP libraries and write some code to read wind speed and direction. I don't think details at this point are relevant, but just know that i'm using an ADS1115 to read wind speed, and that the working voltage is 3.3V. Of course let me know if i should provide further information. Oh, i'm using the same Davis wind sensor

My issue is reading the wind direction. When the windvane is at ~3º, i get 0V, when the windvane is at 360º, i get 3.3V. Ok, so far so good, is what I expected. The problem comes when the vane is between 360º and 3º, because the voltage drop is obviously continous, and you can get ANY reading between 3.3V and 0V in that ~3º range. i.e: if the vane is at 2º, i get the same reading of 314º.

I assume this is known problem, and I assume too this is related to the fact that those wind sensors have a deadband, but how do you people usually deals with this? Should be a hardware or software solution? I can't just ignore values, because i could get ANY value.

I've tried solutions like storing the quadrant of the last X readings, and discard readings that would imply an unusual change of wind direction, but that's not a good solution, at least in a test environment, because if you keep the vane in the dead angle, you'll eventually get false readings after X samples.

Also tried averaging the readings after that, and maybe I should do it before, or maybe I should oversample more, but I'm kinda lost here.

I already know i would have a +-5º deadband, and that's perfectly fine, i just don't know hot to achieve that

So.. how do you usually solve this kind of problems?

Thanks a lot!
Noise

I assume you are using an ADS1115 to read the wind speed and that is a good decision because the ESP32's ADC is very dirty and noisy. It will still have some noise, especially when sending data over wifi, so you'll need to take 10 or so readings and calculate the average.

Dead band

The davis anemometer uses a 20KΩ potentiometer for wind direction, when you enter the "dead zone" the potentiometer does not makes any contact and you get that random readings. To minimize this you have to add a 20KΩ resistor between pin "power" (yellow) and pin "wind direction" (green) in the picture below:

[Image: connect-arduino-to-davis-anemometer.jpg]

Doing this you will almost kill that effect. You can still have some "jump" that you can remove by software. What I do is comparing the last reading to the new one and if it is more than 10º I ignore it.
Generally you aim to align the deadband forward since most boats can't sail directly into the wind.

I have a hall-sensor replacement that is 3d printed with no contact (less friction) but compatible with the same 4 wires for windspeed/direction. The deadband is so much reduced it is difficult to find (<0.5 degree) and the software mostly rejects the bad readings.

Eventually... I am looking at sensors which use pwm output to completely avoid the analog discontinuity of the deadband and so essentially, there will be no deadband.
Thanks both for your comments!

@seandepagnier The deadband is aligned forward, so that part is ok. Also i won't definetly be saling at less than 20º AWA, so that's ok too. It's just annoying to see the wind indicator showing random number when hoisting a the sail, for example. But indeed not a bit deal. The whole question was more about understanding the "whys" and learning the "hows" of this issue, as it must be something frequent.

@sailoog. My first test was with the ESP32 builtin ADC. Indeed the noise was so heavy that even with a fixed windvane I could get readings oscillating a 10% of the whole range. That's why I went for the ADS1115, where readings are very stable.

I've tried the 20KΩ resistor. It did a good job, i didn't know that! I tried to add a second 20KΩ resistor, the readings were even better. So i put a 100KΩ. Is this too much? Can I break anything?

The current algorithm does this:

1) Read ADC every 10ms -> something between 0-17500
2) Divide the reading by 32 (I don't think I need that much precision, so this already smooth the readings). -> something between 0-822
3) Filter out anything above 812 (360º corresponds to that reading) -> something between 0-812
4) Compare the value with the last reading and discard it if it's too far from previous reading
5) Get the average of the last 10 readings -> something between 0-822
6) Store the value
-- The rest happens somewhere else when using SensESP

I still have to test a couple of things, but I think the 100k resistor did the trick.

Thanks again!
(2022-03-07, 05:51 PM)MigGat Wrote: [ -> ]I've tried the 20KΩ resistor. It did a good job, i didn't know that! I tried to add a second 20KΩ resistor, the readings were even better. So i put a 100KΩ. Is this too much? Can I break anything?

I remember testing with a higher resistor and not getting better values but it will not hurt at all. I was using 5V instead of 3.3V and then changing the level of the ADS1115 I2C output to 3.3v before going to the ESP32 input. I did this to make the system also work with the Sean's device and others. You may also try this.

Bona sort!
It may be useful to use a lowpass filter (resistor and capacitor) on the wind direction to reduce noise. This can make things worse during the deadband though.

generally you should read the adc as many samples as you can. I use interrupt driven, or on esp32 perhaps you can use DMA. These are then averaged. So using a few hundred/thousand samples to give one reading update every 50ms or so. Without this could explain noise issues.

I would do the same thing with the external adc if possible, it will only reduce noise. You can play around with different sample rates, as the adc itself running slower will reduce noise too but with less samples to average. There is usually some rate that gives the lowest noise overall once you average.
Thanks again guys

@Sailoog Already tried the 5V options, but didn't make things better. I got more or less the same readings. I will try again this weekend because i didn't had the resistor when I tried.

@seandepagnier Will definetly try the lowpass filter, and see what happens! As for the sampling, i'm currently reading every 10ms. Didn't try the i/o driven or DMA approach (actually i had no idea DMA existed), but will try that too.

Overall it's working quite good already, but will try your suggestions this weekend and report back Smile
Back in the day i forgot to post the code just in case someone needs it. 
Is far from perfect, and I would even say is far from good, but at least it might serve as inspiration for others trying to do the same.

I still have in mind to change the code to use interrupts, but I just don't have time.

[attachment=2356]
Sean is correct a good known aliment is critical.
also some cheaper devices don't have their rev voltage set on chip and i suspect possible power noise from a buck-converter <possible false information.> .
i found a fix in my code by doing
filtering.double ReadVoltage(byte pin) {
double reading = analogRead(pin); // Reference voltage is 3v3 so maximum reading is 3v3 = 4095 in range 0 to 4095
if (reading < 1 || reading > 4095) return 0;
// return * pow(reading,3) + 0.000000016557283 * pow(reading,2) + 0.000854596860691 * reading + 0.065440348345433;
return (-0.000000000000016 * pow(reading, 4) + 0.000000000118171 * pow(reading, 3) - 0.000000301211691 * pow(reading, 2) + 0.001109019271794 * reading + 0.034143524634089) * 1279;
} // Added an improved polynomial, use either, comment out as required