IP Anemometer



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:

wind data

Additional sensors can be used to measure temperature, humidity and voltage levels:

temperature and humidity data

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:

  • Detect "shed door open" to see if anyone is there.
  • Count number of pilots by means of manual "punch in"/"punch out" buttons. This allows to display if/how many people are currently flying.


The project has the following software components:

  • Client code on the Pi (Python)
  • Server code (PHP 5.3+ and MySQL)
  • Two display options:

These hardware components are needed:

  • Raspberry Pi
  • Wind sensor
    • I use an Eltako from amazon.de. It is accurate and reliable, and can be used out of the box.
    • For other sensors the software must be calibrated, which requires a reference anemometer and a fan or some other device to generate constant wind.
  • Jumper wire to connect the sensor to the GPIO pins (male to female, like this)
  • Optional:
    • DHT22/AM2302 sensor for temperature and humidity (can be connected directly, no additional components needed).
    • MCP3008 (SPI ADC) chip for measuring voltage levels (including some basic electronics, e.g. a voltage divider).

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.

What it looks like

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…

all components

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.

outside view


Raspberry Pi OS

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

DHT support

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.

SPI ADC support

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.

Anemometer application

The application consists of three components:

  • The client application. This is the Python code that runs on the Pi and measures the wind sensor rotation frequency, computes wind speed and other data, and uploads it to the server.
  • A server component that receives data from the Pi and shows it on a website. The server also provides the client application and configuration file for the client to download. This makes updates easy even when you have no SSH access to the Pi, because the Pi will pull updates itself.
  • A set of bash scripts for the client. These are typically started via cron at boot time. The main script downloads the client application and configuration file from the server and runs it. Other scripts perform helper tasks such as establishing a reverse SSH connection (if desired).


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.

Access control

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.

Client scripts

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.

Client application

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.

Startup and shutdown

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
Demo mode

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).

Visualization on the server

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.

ADC voltage sensor

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.

DHT temperature and humidity sensor

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.

Connecting the wind sensor

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:

  1. time of day
  2. rotation duration in seconds
  3. current speed in km/h
  4. average speed over last 10 seconds
  5. maximum speed since startup

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 :-)

Calibrating the wind sensor

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.

Door sensor

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.

Pilot count

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.

High temperature shutdown

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.


The config file (ipa.cfg) is missing or cannot be parsed

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.