This article describes a wind speed measurement device - or anemometer, for short - built with the Raspberry Pi. A wind sensor is connected to the GPIO interface. The Pi collects measurements and uploads them to a web server, which stores them in a MySQL database.
The server shows wind speed over time, average, maximum and a histogram. Here's an example screenshot:
Additional sensors can be used to measure temperature, humidity and voltage levels:
All the software is provided and free. Installation requires a little bit of familiarity with Linux and Apache. I’m happy to help if you get stuck :-) The hardware part is very simple and doesn’t even require soldering. The only complex step is wind sensor calibration, for which you’ll need a reference anemometer. Calibration is not needed if you get the exact same sensor that I use myself (which is available from Amazon Germany).
In my case the Pi is located at our RC model flying field, which is at a somewhat remote location (solar power, internet via 3G USB stick). Rain and temperature are easy to tell from the usual weather apps, but wind speed is very local and even specialized apps like Windfinder are often way off. The Pi, however, measures wind speed pretty accurately at exactly the spot I care about. So I can reliably tell if the weather is good for flying and what planes I should bring (some planes handle wind better than others).
For the RC airfiled use case the software includes some simple extra features:
The project has the following software components:
These hardware components are needed:
A wind sensor is a simple switch that goes open->close->open->close->… as it rotates. It can be connected directly to the Pi, no electronics are required. If you don’t use the Eltako sensor you will have to calibrate the software so it can compute the right wind speed from the rotation frequency. A reference anemometer is required for this step. Maybe some manufacturers even provide calibration information. If you have successfully calibrated your sensor and want to help others that have the same model, send me your calibration results so I can publish them here.
This is the hardware (except for the wind sensor): Raspberry Pi B+, Huawei E303 for 3G internet, 12V low voltage cutoff (red LED), brightness sensor (yellow LED) to turn the system off at night, and a 12V car USB adapter (left). One day I’ll put all the electronics in a real case…
The Eltako wind sensor on a 3 meter slat, and our solar panels. The Pi doesn’t need this many panels; the primary consumers are our battery chargers, which draw about a hundred times more power. 12V car batteries are in the shed.
Follow the official instructions to install Raspberry Pi OS. After the installation you should upgrade all packages:
sudo apt-get update ; sudo apt-get dist-upgrade
If you plan to connect a DHT22/AM2302 sensor, install Python support for it:
sudo apt-get install python-dev
git clone https://github.com/adafruit/Adafruit_Python_DHT
cd Adafruit_Python_DHT
sudo python setup.py install
PrivateEyePi explains how to connect the DHT22 sensor. They use an additional resistor to connect the DHT sensor, but for me it works fine without one.
To use the MCP3008 SPI ADC, install Python support for it:
sudo apt-get install python-dev
git clone git://github.com/doceme/py-spidev
cd py-spidev
sudo python setup.py install
Then run sudo raspi-config
and under "Advanced Options", enable SPI.
Raspberry Pi Spy has an excellent tutorial on the MCP3008.
The application consists of three components:
Upload the contents of the server/
directory to a directory on your web server. Let’s assume www.wind.foo/ipa/
for this example. The web server must run PHP 5.3 or higher and MySQL. Make sure the client/
and logs/
directories, as well as the file client/ipa-client.zip
, are writable by the PHP user.
Create a new MySQL database, then copy or rename common/config-sample.php
to common/config.php
and fill in the credentials for that database. Navigate to www.wind.foo/ipa/admin
. This will automatically create all database tables and a default configuration. Set server:log_level
to debug, client:logging_level
to DEBUG and client:upload_interval_seconds
to a small value such as 5. This makes the initial setup easier because client and server log more and interact more frequently. Reset these values when everything is working. Server logs are stored in the logs/
directory and may be helpful when something doesn’t work.
The server/
directory contains several subdirectories, some of which you might eventually want to protect with a password using .htaccess
and .htpasswd
:
admin/
- The admin panel. This should be password protected. The admin password is only entered by you when visiting the admin panel.client/
- Files for interacting with the Pi. This should be password protected if you want to prevent an attacker (or rather, a prankster) from uploading fake wind data or downloading your client binary and config file. The client/
password will be sent by the Pi for every server interaction. You will probably need to store it in your crontab, so don’t use any existing password. For illustration purposes we will assume the credentials for the client/
directory to be "clientUser" and "clientPassword".common/
- Common PHP files, no need to protect this.logs/
- This already contains an .htaccess file that denies access for everyone.view/
- This is for viewing wind data. You should protect this if you don’t want your data to be public. Wind data isn’t really critical, but the shed door status or the pilot count might be.I recommend that you set up passwords after everything else is up and running, so if something doesn’t work you have one less thing to debug.
Create a directory on the Pi, e.g. /home/pi/ipa
, and copy the contents of the client/
directory to it. Run chmod a+x *
in this directory to make the scripts executable.
The client application and its configuration are downloaded from the server and run by the ipa_wrapper.sh
script. This script will also automatically perform an update if you upload a new version to the server or change the client configuration. Run the script now to test if it’s working:
./ipa_wrapper.sh http://clientUser:clientPassword@www.wind.foo/ipa/client
Omit the user:password part if you haven’t set a password for the client/
directory yet. The script will run sudo (this is required for GPIO access), so the user account must be a sudoer and no root password must be set. This is the default setup for the user "pi".
You should see no error messages on the client, and some data should appear on www.wind.foo/ipa/view
. For now the wind data will be zero, but charts for CPU temperature and upload lag should appear.
To kill the application run the kill_ipa.sh
script.
To start the application automatically on boot add this line to your crontab:
@reboot ipa/ipa_wrapper.sh http://clientUser:clientPassword@www.wind.foo/ipa/client
The client can generate random data to populate the graphs. This allows to see what the visualization looks like, and to check whether the software part (i.e. everything after the sensor) works. This feature is enabled by setting the configuration key client:demo_mode_enabled
to 1
.
If something doesn’t work, check the log printed on the client (it is also stored in /home/pi/ipa/logs
) and/or the server (in the logs/
directory).
The provided file view/index.php
is very basic and will have to be edited manually to remove graphs you don’t use and make everything look nice.
The WordPress plugin is easy to use, can show only selected graphs and doesn’t require editing HTML.
NOTE: Previous versions used GPIO.BOARD notation. As of version 0.3.0 I’m using GPIO.BCM notation because it has become more popular.
RasPi TV has a nice cheat sheet for the pinouts.
Set adc_enabled
to 1
and specify the list of channels and reference voltages in adc_channels
and adc_vrefs
. When you have more than one channel, use a comma separated list with the same number of elements for both.
Set dht_enabled
to 1
and dht_pin
to the GPIO pin to which the sensor is connected. dht_sensor
determines the sensor type (11 for DHT11, 22 for both DHT22 and AM2302). dht_retries
controls the number of read retries in case the GPIO communication with the sensor fails.
First connect a simple circuit tester (such as a multimeter) and slowly rotate the wind sensor 360 degrees. Take note of the number N of changes from open to close plus from close to open. N should be even, probably either 2 or 4. Go to www.wind.foo/ipa/admin
and enter this number for edges_per_revolution
. Example: If the sensor closes and opens twice per revolution, enter edges_per_revolution=4
. Knowing the number of edges per revolution ensures accurate measurements even if the cycle duration within one rotation is uneven.
Now connect the sensor to the GPIO pins: One terminal (it doesn’t matter which one) goes to 3.3V (pin 1), the other to the wind_input_pin
specified in the configuration (pin 4 by default). No resistor is needed since the Pi has buit-in pull up/down resistors.
Next we’ll start the calibration tool to check if the sensor is working:
cd ~/ipa/current ; sudo ./wind_calibrate.py
The actual client application must not be running at the same time because both require exclusive accesses to the GPIO pin.
Rotate the sensor by hand. The tool prints something like this:
16:09:28.356 0.268 11.615 1.565 14.721
These value are:
So if one rotation takes one second the second value should be approximately 1. It should also decrease with increasing rotation speed.
Congratulations, this was all the hardware work :-)
At low speeds mechanical friction will be significant and the relation between rotation speed and wind speed is nonlinear. At higher speeds friction will become negligible and the relation is approximately linear. I have used the following formula to describe this:
v [km/h] = LSF / (1 + rps) + HSF * rps
LSF and HSF stand for low and high speed factors, rps for rotations per second. LSF and HSF are the parameters we need to determine.
The default configuration contains the LSF and HSF values for the Eltako sensor that I use. If you use a different sensor you need to determine LSF and HSF for your particular model. This requires a reference anemometer. I like the Kaindl Windtronic 2 (called "Kaindl Windmaster 2" in Germany).
Set wind_low_speed_factor=0
and wind_high_speed_factor=1
in ipa.cfg
. Note that this file will later be overwritten with the server side values when ipa_wrapper.sh
is run, so the calibration results should also be entered in the admin panel.
Get a large and powerful fan, set it to maximum speed and point it at the sensor. Then start wind_calibrate.py
. Note that the wind speed measured by the sensor may vary significantly depending on its position relative to the fan. Find an arrangement where the wind speed is stable. Make a copy of the readouts on the Pi, specifically the 10 second average since it reduces random fluctuations. Let’s call this value v_avg_pi
. This isn’t the correct speed yet unless the correct value for HSF happens to be 1 for your sensor.
Now use the reference anemometer to measure the wind speed at exactly the same position; we’ll call this v_avg_ref
.
Set wind_high_speed_factor
in ipa.cfg
to the quotient of v_avg_ref/v_avg_pi
.
Next find out at which wind speed, as measured by the reference anemometer, the Pi’s sensor starts turning at all, i.e. which wind speed is required to overcome friction in the sensor. Move the fan away from the sensor just before the sensor stops rotating. For the Eltako sensor this is around 1.8 km/h, for higher quality sensors it is probably lower, for cheaper ones it might be higher. Use this value for wind_low_speed_factor
. Also, use the rotation duration measured at this speed, plus roughly one or two seconds, for wind_max_rotation_seconds
. For example, if the slowest possible rotation takes 9.2 seconds, set wind_max_rotation_seconds=10
.
wind_max_rotation_seconds
is the cutoff threshold that allows v=0: If no rotation was observed within this time the wind speed is zero. Of course this means that all wind speeds below LSF are considered zero. This is not a theoretically optimal assumption, but in practice it would be confusing to show some magic non-zero estimate when the sensor is actually not rotating. Unless LSF is very high it probably doesn’t matter much. FWIW, my handheld reference anemometer does the same thing :-)
Restart wind_calibrate.py
to pick up the config values and experiment with different wind speeds to verify that the calibration was successful. Remember to enter wind_low_speed_factor
, wind_high_speed_factor
and wind_max_rotation_seconds
in the admin panel since ipa.cfg
is overwritten when you update the configuration on the server.
When someone is at the flying field they always open the shed door to enter their name in the log book. Using a simple magnetic switch connected to a GPIO pin the Pi can detect this. The door_*
values configure this feature. door_pud
defines wether to use pull up or pull down on the input pin. door_open_state
defines which state (switch open or closed) means the door is open.
Using two buttons connected to two GPIO pins we can count the number of pilots at the field: When people check in they press the "plus" button, when they leave they press the "minus" button. This gives me a rough idea of how busy the field is. A third GPIO pin is connected to a LED that blinks N times after a button is pressed, where N is the current pilot count. At night (04:00 by default) the count is reset to 0, in case someone forgot to check out.
Electrical configuration is similar to the door sensor described above.
I found that in the summer the CPU may get pretty hot with the Pi running in an unventilated small shed. To avoid damage from overheating you can configure a maximum CPU temperature, client:temperature_shutdown_at
(degrees celsius). If this temperature is reached the system shuts down completely. I didn’t find much information on how hot the Pi’s CPU is allowed to get, so I’m not sure if the default of 75°C is good.
Over time the database and server logs grow. Especially at debug level the logs can take up several MB per day. You can use the prune button on the admin panel to delete old database records and log files.
This can also be automated: Enter the amount of days you want to keep, click the prune button, then copy the browser’s URL and configure a cron job to request this URL every day using curl
or wget
.
Cause: The ipa.cfg
file is created on the server when the database is initialized and whenever you change a setting. It is then downloaded to the client by the ipa_wrapper.sh
script. main.py
should not be started directly.
Solution: [This summarizes the regular instructions.] Open the admin page in your browser and make sure there are no error messages. To verify this step, manually download the ipa-client.zip
archive from the client/
directory on the server to a temporary directory. Verify it contains the client side Python scripts and the ipa.cfg
file, then delete the archive and any files you might have extracted. Now start ipa_wrapper.sh
as described in the instructions. This will (again) download ipa-client.zip
, unzip it and start the main Python script. Whenever the configuration changes and the server updates ipa-client.zip
the ipa_wrapper.sh
script repeats this automatically.
It may seem complicated to have the Pi pull the configuration (and the application), but depending on how the Pi is connected to the internet (dynamic IP, NAT) it is not straightforward to push config changes.
If you have any questions or suggestions, please feel free to contact me.