Projects

One of the best parts of being a ham operator is to build things. There are so many tools out there for building things, including Raspberry Pi's, Arduino's, WiFi boards such as the NodeMCUs, and other single board technoligies. This page lets you view some of the projects I have worked on and documented. Hopefully it will help you!

Click on a selection

Untitled Document

Setting up an ADS-B Receiver

I will describe here how to set up your own ADS-B Receiving station in Linux. You can click on any image to enlarge.

What does a typical ADS-B output look like?

One of the programs that you can use to decode ADS-B with the RTL-SDR dongle is called "DUMP1090". This is a Linux/Max/Windows based command line ADS-B decoder. It is very useful for Linux based devices such as the Raspberry Pi, but it also works just as well on desktop PCs and laptops. With dump1090 you could set up a Raspberry Pi decoder that feeds data to a online webserver which shows a map. Dump1090 can be downloaded from its most updated Git repository here. To download dump1090 for Windows simply go to the Git repository and download the dump1090 zip file. Dump1090 is reported to probably have the best ADS-B decoding performance out of all the software. Another advantage to dump1090 is that extra mapping software is not required as it can generate a Google Map webserver which shows the aircraft. An typical image is shown below.

The dump1090 program when running on a Raspberry Pi can also produce a rather nice plot on a Web browser running on your local network (PC, Linux or Mac). You might also like to use this as a check that the dump1090 program is still running as expected, and that the TCP/IP link is still up on your Raspberry Pi. Simply enter the URL:

http://<address-or-name-of-RPi>:8080

where the address can be a number or a name, e.g.

http://192.168.0.19:8080

The output will look similar to the following:

Another program, one that I use, to display a tracked planes is called Virtual Radar. Virtual Radar Server is an open-source .NET application that runs a local web server. You can connect to the web server with any modern browser and see the positions of aircraft on a Google Maps map. To run this in Linux, you will need to install a program called "Mono". Mono is an open-source implementation of the .NET framework that you can use to run .NET programs under non-Windows operating systems, such as Linux. You can use it to run Virtual Radar Server on Linux. However Mono relies heavily on native libraries which aren't always stable, and the parts of Virtual Radar Server that rely on Windows-only COM modules won't work, so the Windows .NET version is the one to use if you want stability and all of the features.

The web output of the Virtual Radar software is shown below or you can view mine at http://n8mdp.ddns.net:8085/VirtualRadar.

Back to Top

Preparing the Linux machine or a Raspberry Pi

Before I begin, I want to give credit to David & Cecilia Taylor. They have put together some great webpages on a variety of applications for Satellites, Weather, and the Raspberry Pi, one of which is setting up the ADS-B application. I am using their procedure here and will be repeating many of the steps.

Installing the needed software for the RTL-SDR for Linux

Like so many, I am not a Linux expert, but I am a quick learner. If you have RPi/Linux questions, you'll probably need to ask someone else. In the lines below, you can highlight the command required, use Ctrl-C or right-click Copy to get the text into the clipboard, and then if you use a terminal program called PuTTY, you can right-click on the text area of the terminal session to enter the command into your Linux machine or Raspberry Pi.

Please note that during some of the following commands you may be prompted for Yes or No answers, perhaps about the disk space which will be used by the new download. Unless specified otherwise here, simply accept the default answer, usually "Yes" (or "y").

A. Open a command prompt and type: sudo apt-get update

This will update the table of updates to the latest revision.

B. Again at the command prompt type: sudo apt-get upgrade

This will update the operating system to the latest version, and may therefore take "some time".

C. Next, at the command prompt type: sudo apt-get install git-core

This installs the git repository fetch code. You may already have this installed, in which case you will get a message advising that you already have the most up-to-date version.

Back to Top

Installing and compiling the RTL-SDR driver

D. Next, we will use the instructions to setup sdr-rtl which are given on this Web page for a Raspberry Pi. They worked on my Linux machine as well. We stop where the text says "To run the rtl server type rtl_tcp -a and the IP address of your Pi". For your convenience, the instructions are copied below, and these are exactly what I typed into my Linux machine. You don't need to follow the instructions on the quoted Web page as well as what's below. Step (9) below has been changed so that you no longer need the "sudo" commands to run the programs accessing the dongle.

1. sudo apt-get install git

2. sudo apt-get install cmake

3. sudo apt-get install libusb-1.0-0-dev

4. sudo apt-get install build-essential

You can also put all those commands onto one line should you wish (A linux trick I learned):

      sudo apt-get install git git-core cmake libusb-1.0-0-dev build-essential

although I prefer to do one command at a time in case there is an error, and you can see which step caused the problem.

Now install the RTL-2832U USB dongle driver source and compile:

5. git clone git://git.osmocom.org/rtl-sdr.git

6. cd rtl-sdr

7. mkdir build

8. cd build

9. cmake ../ -DINSTALL_UDEV_RULES=ON

10. make

11. sudo make install

12. sudo ldconfig

If this was successful you should see no return on the previous command. Install the RTL-2832U USB dongle into your Linux machine or on an external powered USB HUB if you are using a Raspberry Pi.

While many report using a powered USB hub, you may find the built-in ports adequate, if you have enough 5V and power(current) fed to the Raspberry Pi. You may get different results, and possibly if you use the live plug-and-play will more likely need the hub.

cd ~

sudo cp ./rtl-sdr/rtl-sdr.rules /etc/udev/rules.d/

sudo reboot

14. Open a terminal window and type: rtl_test -t

The first time I tried this was with my NooElec R820T tuner, I got the following:

This was what I was expecting to get. Don't worry when it says that there is "No E4000 tuner found, aborting". This is ok. As long as it recognizes the dongle, it says that it is sampling, and that you see the gain values, you should be in good shape.

For Raspberry Pi users, if you get error messages such as "cb transfer status: 1, canceling." it may be because the dongle is taking too much power from the Raspberry Pi. David Taylor claimed not to have seen this with all the dongles he tested. You may be able to resolved this problem by putting the USB dongle on a separately powered USB hub.

Back to Top

Installing Dump1090

E. Now download the latest release dump1090 application source code. We will use the instructions from this Web page. We start following the instructions from about half way down the page where it says "git clone git://github.com/antirez/dump1090.git", which requires us to take these steps:

1. cd ~ or cd /home/pi/ (goes to your home directory)

2. git clone git://github.com/MalcolmRobb/dump1090.git

3. cd dump1090

4. make

NOTE: If got errors at this point where pkg-config could not be found on your Linux, installing pkg-config cures the problem. But if you had no errors from the make step, you will not need not need to do steps 5 and 6.

5. sudo apt-get install pkg-config

6. make

F. When you type "./dump1090 --interactive" in your terminal window, if you've got everything installed, compiled and connected correctly then you should see a screen displaying aircraft being received by your Linux machine or RPi. However, likely you will need to run this with privilege to allow the program to open the port. Note the double "--" if you are typing the command by hand, and note the "./" in front of the word "dump1090".

            ./dump1090 --interactive

If you want to see the output over the network at this point, remember to add the "--net" command-line switch:

            ./dump1090 --interactive --net

Back to Top

Installing Mono

Since I originally chose to use the Virtual Radar software to display my ADS-B information in linux, a program call "mono" is required. As previously mentioned, Mono is an open-source implementation of the .NET framework that you can use to run .NET programs under non-Windows operating systems, such as Linux. You can use it to run Virtual Radar Server on Linux. However Mono relies heavily on native libraries which aren't always stable, and the parts of Virtual Radar Server that rely on

I will be repeating the installation procedure from the Linux page of the VirtualRadar website.

First of all you need to make sure you have the mono runtime installed. For most Linux distributions this can be installed via the package manager (e.g. sudo apt-get install mono-complete on Debian / Ubuntu), otherwise you can go to http://www.mono-project.com and download it from there.

Mono uses X11. Most Linux distributions already have X11 installed but some (notably OS/X) do not and will need to have it installed.

The Mono version has been successfully tested under OS/X Mountain Lion with XQuartz and Mono 3.0.3.

Back to Top

Installing Virtual Radar and configuring

Once Mono (and X11) is installed you can download and unpack VRS from here:

VirtualRadar.tar.gz

Decompress that somewhere. It will create a folder called VirtualRadar - open a shell prompt, cd into the VirtualRadar folder and enter:

mono VirtualRadar.exe

The Web server status section tells you the IP addresses currently connected to the Virtual Radar serve. Just below that section, you will see a hyperlink, like mine http://127.0.0.1:8085/VirtualRadar, that will allow you to pull up the Virtual Radar webpage locally on your computer. Make note of the port address in the IP address. This will be discussed shortly. The Feed status section displays the connections status and message information that is transferred from the dump1090 software. I don't rebroadcast my server data so I am not going to discuss this.

I mentioned about the port used with Virtual Radar. By default VRS will listen to port 8080. If you have other software listening to port 8080, or you just want to use another port, then you can change the port via a configuration file.

Start VRS and click Help | About. Click the link to take you to the configuration files folder. For most Linux distributions it will be ~/.local/share/VirtualRadar.

In the configuration folder create a text file called InstallerConfiguration.xml.

Open the file and paste this into it:

<?xml version="1.0" encoding="utf-8" ?>
<InstallerSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<WebServerPort>8080</WebServerPort>
</InstallerSettings>

Change the "8080" in the file to the port number you want. I changed mine to 8085. Keep this in mind as we further configure Virtual Radar server.

Let's start to configure Virtual Radar Server. I will use my settings as the example here. From the menu bar in the VRS window, select Tools and select the Options option. This will open up a window similar to the following:

The first thing that I did was to hit the New button so I could create a new receiver called N8MDP. You can see in the above window how I set all three of the general settings to N8MDP.

The next step is to set the receiver location general settings. By clicking on my newly created N8MDP receiver in the listing, I can set the general settings like I show below:

I would like to discuss the Data Feed:Data Source option here. This setting allows you to select the format that the data feed is transmitting. The supported formats are:

1. BaseStation - Kinetic's de-facto standard, usually transmitted by BaseStation on port 30003.

2. SBS-3 Raw Feed - Kinetic's binary format for Mode-S and ADS-B messages picked up by the SBS-3, usually transmitted either directly by the receiver (when connected to the network) or by BaseStation on port 30006.

3. AVR or Beast Raw Feed - the open standard AVR format for Mode-S and ADS-B messages or the Beast's binary format. Virtual Radar Server can automatically detect which of these formats is being sent.

4. Compressed VRS - a compressed feed format that is similar to BaseStation but requires much less bandwidth. At the time of writing only Virtual Radar Server rebroadcast servers can transmit this format.

For this installation, I use the BaseStation option. It works the best with dump1090.

The other option I want to discuss is the Network:Port option. Don't let this confuse you with the server port option I presented above that defaults to 8080. This is the port number that the receiver or software, in this case dump1090, is transmitting messages on. Port 30003 is the purt number that dump1090 defaults to for this particular purpose.

I recommend that you create a Receiver Location. I think this is used when the information is plotted using Google Maps. As shown below, highlight Receiver Locations and hit the New button.

You can then enter in the options as shown below. I use the N8MDP as the location. You can then put in your Latitude and Longitude information.

The next important configuration setting is the Raw Feed Decoding as I show below. Explore these various settings.

The next configuration is the Web Server settings. Again, explore these settings to see what works for you. Do note the UPnP:Internet port number setting. Make sure you set this to the internet port value I discussed previously. Mine is set to 8085. Don't forget that if you want to share your ADS-B with others on the internet, you must open up this port as well on your internet router as well. Otherwise, nobody outside your firewall on the router will be able to access the data on your webpage.

Next to configure is the Web Site option. It's important that you set the four options in the Google Maps section. This forces Google maps to immeidately to to your location where your receiver is located, the map typ the web page will display and the initial zoom level. The map types include road map, satellite view, etc. Again you can set the options that work the best for you.

Lastly, you can change some of the General settings that best fit your requirements.

Once you have made all of your settings, click the OK button to take you back to the main screen.

I would recommend at this point that you close all of your programs and reboot the computer to get back to a fresh start.

Once you have rebooted your computer, start the dump1090 program in a terminal window. In a second terminal window, cd into the VirtualRadar folder and enter: mono VirtualRadar.exe to start the application. If dump1090 is displaying aircraft data, you should see planes being tracked in the Virtual Radar terminal window and on a webpage. If you do not see any tracked planes in a virtual radar webpage, go back and make sure that all of your settings are correct.

Further documentation on the Virtual Radar server can be found at the Virtual Radar Documentation page.

If you want to install the same Virtual Radar software but on a Windows machine, you can obtain the Windows installer software here. Most of the configuration options I explained above still apply. There have been some updates to the software since I installed it. It is possible some screens may not look the same but the configuration is very similar to what I explained.

Back to Top

Installing Plug-in Options

If you took a look at my Virtual Radar page, you might have noticed silhouettes and flags of various air carriers. These are plug-in options that you can install into your Virtual Radar Server. They are not available from the Virtual Radar website and are no longer available from the original SBS-Resources website. I did confirm that the sites have been taken down. Since these files are no longer available from the site, I zipped up the files for download. The files can be downloaded here.

Once you have downloaded the file, uncompress the files to a new folder in a path similar to the followng:

c:\SBS-resources\Files

Install this on a Windows system to avoid hassles with using software like Linux WINE. You will upload the proper files to the Virtual Radar server software on your Linux machine after you have selected the silhouette and flag files. Let me walk through what I did to get the files I needed for Virtual Radar.

Once you have completed this, you can then proceed to step 7 below.

After the Resources 6.7 (or similar) has been installed:

1. Run the program in Windows. You might get a pop-up windows that looks like the following:

sbs1

I selected the 32-bit box and just hit the next button.

2. The following main window opens:

sbs2

3. There is a file in the Registrations section that we need to get loaded up. Select the Registrations button, then click on the .SQB File button that opens up in the next window.

sbs3

Click the OK button. This process will take a few minutes. Ultimately, you will see the Files copied box fill up like this:

sbs4

The file that we are going to use the BaseStation.sqb file. Go ahead and hit the Close button. Then hit the Home button in the Registrations window.

4. In the Installation File window, choose the Operator Flags button so we can get the soilhouettes and the carrier flags.

sbs2

5. The Operator Flags window opens up so that you can select Operator and Silhouette flag files.

sbs5

Review the various operator flag versions and make your selection. Like in step 3, the following window will pop-up. Hit the OK button to start the process and wait a few minutes for the files to load. When the files are loaded, click Close.

sbs6

6. When the Operator Flags window opens back up, select the Silhouette tab. Then select the Silhouettes button and follow the same process as you did in step 5. Once that is complete, hit the Home button.

7. At this point, you should have the three key file folders installed that are needed to install in your Virtual Radar applications. You will need to go get those folders. They are likely in the director C:\SBS-resources\Files.

To make life easier, I copied the entire SBS-resources file folder that had the contents of all the file folders to a flash drive. I then copied the contents of the entire SBS-resources file folder to the Home directory in my Ubuntu system.

This is all we need from the SBS-resources application.

8. From the menu bar in the VRS window, select Tools and select the Options option. Click on the Data Sources from the tree so that the Aircraft Data information opens up, like I show below.

There are three Aircraft Data lines that need to be filled in:

1. Database filename
2. Flags folder
3. Silhouettes folder

On each line, you have the ability to browse for the necessary files and folders needed. For the Database filename, select the BaseStation.sqb file that is in the Registration folder in the location where you save the entire contents of the SBS-resources folder in your Home directory. For the Flags folder, select the particular OperatorLogos folder that you want to use. Don't select and files, just the folder only. For the Silhouettes folder, again just select the SilhouettesLogos folder only. When you are done, your window should look similar to mine above. If not, go back and make sure you selected the right file and folders you want. When done, click the OK button.

Believe it or not, you are done! Restart your Virtual Radar webpage that you are viewing and see of you start seeing any silhouettes and aircraft operator logos in your page. Also go ahead and click and any of the planes being tracked to see more information about the plane.

 

Untitled Document

Raspberry Pi Lightning Detector

Why build a weather station when you can easily buy one

Ever since I was a kid, I have always been fascinated by storms and especially lightning. Today, with all of my Ham Radio antennas and radios, protecting them from lighting is very important to me. Being able to detect lightning before the storms gets close to my home gives me the opportunity to switch off the antennas in time. I've already had to have my Icom PCR-1000 fixed twice due to lighting crashed and enough static to damage the front end diodes.

Today I rely on severe weather alerts to warn me of pending storms so that I can switch off the antennas. But what if I'm not home? That's why I am building the following lightning detection system so that it will automatically turn off the antennas when lightning is close to my house. If we happenv to lose power at the house, at least the relays on the antenna switch off disconnecting the antennas.

So the following page is dedicated to my building the SwitchDoc Labs lighting detector using the Raspberry Pi.

Back to Top

The SwitchDoc Labs Lightning Detector

What I'm about to build is the Lighting Detector from SwitchDoc Labs using their new Thunderboard. I was impressed with this product as advertised online, so I bought one. The end product will look like this:

Lightning Detector

This is the Thunderboard which will go inside the housing at the top.

Thunderboard

Excerpts from the SwitchDoc Labs describe the Thunderboard as follows:

The Thunder Board Grove I2C Lightning Detector for the Raspberry Pi and Arduino - is a programmable Lightning Sensor board that detects the presence and approach of potentially hazardous lightning activity in the vicinity and provides an estimation on the distance to the head of the storm. The embedded lightning algorithm checks the incoming signal pattern to reject the potential man-made disturbers and various noise sources.

How the heck do we detect lightning? You would think it would be pretty easy, but it turns out it is not. It's not just like a giant spark. Well, it is a giant spark, but there are lots of other things that make electrical noise that can be confused for lightning. Your computer (even your Raspberry PI and Arduino!), your car, the motor in your refrigerator, your cell phone, your computer monitor, your AM/FM radio and even your TV. They all make electrical noise that can be confused with Lightning.

The Thunder Board is an I2C device and detects Lightning and provides a distance estimate to the 'leading edge' of an incoming storm.

The block diagram of the Lightning Detector is as follows. Click on image to enlarge.

According to SwitchDoc Labs, the entire hardware kit is as follows:

Item # Item Image
1 Thunder Board Lighting Detector
2 Pi2Grover Board
3 Grove LCD Display with Backlight
4 Grove Buzzer
5 Additional Grove Cables (2) 30cm Cables
6 Raspberry Pi 3 or better!
7 3D-Print Files available at GitHub

I contracted to my daughter to make the 3D printed parts which are shown here:

Now that I have all of the pieces / parts needed to start the project, the next phase is to initialize the Raspberry Pi 3 and install the software so I can do initial testing.

Back to Top

Installing the software on the Raspberry Pi 3

While SwitchDoc Labs in their second tutorial on this project puts all of the hardware together, I decided to install the software on the Raspberry Pi 3 first. I followed the instructions located in the Switcdoc Tutorial 2 for installing the software. I'm not going to repeat the instructions here. Follow the appropriate instructions for installing an operating system on the RPi. A decent set of instructions can be found here.

Once I had the software installed, it's important to start checking if all of the parts are recognized by the Raspberry Pi. So I pre-wired all of the boards before installing into the 3D printed box. The wiring diagram is shown below. Click on image to enlarge.

The wired up system looks as follows. Click on image to enlarge.

With everything wired up, I powered up the Raspberry Pi to start my testing. to start the testing, you need to open a terminal window. Or in my case, I am using SSH for which I use the program 'PUTTY' and connect via an IP address. In either PUTTY or a terminal window on the PI, you type:

sudo i2cdetect -y 1

The results should look like the following:

Depending on your Raspberry Pi, you may not see 0x03 or 0x70. If you don't see 0x3e and 0x62, then your LCD is not connected correctly. Note, you will not see the ThunderBoard in this test. By the way, I had to disconnect the buzzer as it was quite annoying since it was constantly buzzing.

To test the LCD display, in either PUTTY or a terminal window, you type:

sudo python testLCD.py

You should see the display changing colors and printing text on the display: "Going to sleep in xx..." where the xx is a countdown of numbers.

Once the test is over, you will see in PUTTY or the terminal window:

To test the ThunderBoard, you type in PUTTY or a terminal window:

sudo python testThunderBoard.py

You should definitely see the first line. It should say 0x02 or 0x03 depending on the version of the Thunder Board that you have.

You may see more lines if your environment is especially electrically noisy. Hold it up against a monitor may trigger the noise system. So far, so good!

You should be able to get a noise trigger by holding it close to your phone or computer monitor, but not guarantees unfortunately. Depends on too many factors. The only way to fully test the devices is to wait for a thunderstorm or use the Thunder Board Lightning Simulator.

Interesting that after a few minutes, the software test did start to report something. My guess is that my ThunderBoard was in close contact with some noise generators around my workbench.

Looking good. At this point, I'm satisfied that things are beginning to work. Now to install the hardware into the 3D printed case.

Back to Top

Installing the hardware into the 3D printed case

Using the instructions from Tutorial 2 on the SwitchDoc Labs website, here is what I had to do.

Step # Instruction Image
1 Removed power from the Raspberry Pi from my prior testing. Be sure to shut it down before you remove power!
2 If any of your 3D parts are too tight, you can use a fingernail file to slightly mold them. Or as I did, my standoffs were too small. So I wrapped them with electrical tape so they would fit snug into the mounting holes.

Install the Raspberry Pi into the 3D printed case.
3 Install the Raspberry Pi into the 3D printed case. I used nylon standoffs and screws.
4 Take the Pi2Grover board and align the pins on the Raspberry Pi 3 GPIO header accurately and the carefully push down the Pi2Grover board on to the Raspberry Pi 3 board. I actually did this above when I tested the boards. Note that the end of the Pi2Grover board that hangs over the USB and Network plugs on the Raspberry Pi 3 has no pins on the bottom of the Pi2Grover board so it can't short out. But I put electrical tape on the USB connectors just in case!
5 Take a 20cm Grove Cable plug it in the upper right Grove connector on Pi2Grover labeled I2C and connect the other end to the I2C LCD 2/Backlight.
6 Take a 30cm Grove Cable and plug it into Pi2Grover label I2C under the Cable in Step 3 above. Take a 30cm Grove Cable and plug it into Pi2Grover in the plug labels D16/19.

Make sure you know which cable is which because you will need to feed it though the top case cover.
7 Put Non-Conductive tape (Like electricians tap, painters blue tape or scotch tape) over the conductors on the LCD Board and over the exposed contacts and metal on the Raspberry Pi as shown. This is in case the LCD is pushed back into the box by mistake when the Kit is powered up. Just a caution!
8 Please the Grove Buzzer in the slot as shown. Don't plug it in yet! Wait until you have installed the software, otherwise, it will buzz all the time.
9 Push the I2C LCD w/Backlight into the top of the Box as shown. Watch the Orientation. If it is too tight, use a nail file to shave a bit off. Too loose? Tape it in. By the way, I disconnected the Grove cable from the Pi2 Grover board to make it easy to tape the display to the cover panel. Once I was done, I plugged it back into the Pi2Grover board.
10 Now route the cables intended for the ThunderBoard through the hole in the top and then up the 3D tube (placing the tube on the stand on the top of the box), then through the pylon stand (putting the pylon stand into the tube) and then replugging the cables into the Thunder Board. MAKE SURE YOU PLUG THE CABLES INTO THE RIGHT GROVE CONNECTORS. I used a little sticky tab to identify one of the cables.




10 Insert the ThunderBoard into the top box part as shown.
10 Snap the top of the box on to the box bottom and then snap the lightning board box on the top of the lightning pylon. You may have to apply some tape to the box top cover as I did in order to keep the top cover on. Not a big deal. At some point, will have to connect the buzzer and will have to lift off the top cover. You are now done.


That's all for the initial assembly. Again, I will install the Grove Buzzer later and the remaining 20cm Grove Cable. At this point, I don't want the buzzer to buzz incessantly while I complete the software install. Believe me, it's irritating as I found out when I was testing the system!

Back to Top

Auto starting the Lightning Detector at boot-up

If you want your IOT software to restart after boot up of your Raspberry Pi (a generally good idea), then in a terminal windows (or from PUTTY), type the following:

sudo nano /etc/rc.local

And add the following lines to the end of the file before the 'exit 0' statement.

cd /home/pi/SDL_Pi_ThunderBoard_IOT/

sudo python ThunderBoardIOT.py &

You can test this by rebooting the Pi. Type:

sudo reboot

Everything worked great. I even plugged the buzzer back in and the buzzer shut off as soon as the software started. SO this project is basically done. My plans don't end here. I will be studying the python code so that I can control my antenna relays using this detector. When lighting is too close, I will have the Raspberry Pi turn off the antenna relay and protect my radios. I also plan to have the Lightning Detector serve a webpage so that you can see when lighting occurs in my area. So, Work in Progress!

I hope you enjoyed this page!

Untitled Document

Raspberry Pi LED Badge Weather Warning Application

A couple of years ago at the Dayton Hamvention, I purchased a number of LED Badges with the intention to hack them for usage with Raspberry Pi's. One of the use cases was to use the RPi to drive the LED Badge to display weather alerts and warnings. You probably noticed at the top of my website that I display weather advisories, watches, and warning as a marquee. But what if I am not looking at my website? How would I know whether there were alerts for where I live. Thus the idea to use the RPI and the LED Badge to to create a weather alert scrolling message board and place the system in my sunroom where it would update 24/7.

The pieces of the project were simple. A Raspberry Pi, and it can be an original Pi like I used, and an LED Badge like the one I show below;

These LED badge displays typically have a USB socket for charging and programming, and appear to a PC or Pi as a virtual serial port.

The badges come with Windows drivers and software to download messages to the device. The intention with the software is that you type in your message, download the message to the badge, then unplug and run the display from its internal battery. Messages are stored internally in flash memory so the device can be powered off and it will retain the message.

For my purposes, I need the Raspberry Pi to monitor weather alerts from the local National Weather Service and search for my county in lists of possible weather alerts. Once my county has been identified, the Pi will upload the message to the display and scroll the alert until a new alert message is identified.

So the first job was to get the device running on my Windows PC. The device installed as a serial device (the common Prolific PL2303 driver) and the supplied software successfully sent messages to it:

One of the problems with these LED Badges is that you don't know how the data is encoded when you send a message from the software to thge badge over USB.

So with the software working, we need to take a look at what it was sending to the device. To do this, I needed to obtain a serial monitor program. Based on other references online, I downloaded HHD Software Device Monitoriing Studio. This is a great tool for snooping on devices that use serial control such as USB devices.

I'm not going to go through the details of how to run the monitoring software but the important result is that you get a data view of the data transfered. To get the first results, I used the LED Badge software to send the message: Severe Thunderstorm Warning. The monitoring software captured the following:

It's very clear where the message is in the packet transfer. But there are other pieces of data, at least for my badge to be aware of. My badge allows multiple messages to be uploaded at one time. If you look carefully, there are blank areas where no characters were sent yet a block of '00' hex values are sent. This is important when writing the Python script that I developed for this application.

I should mention that a LED badge you have may not behave and transfer packets of data the same as mine. At least my analysis may help you to get yours working as well. Let's breakdown the data packet to explain what was sent.

The first set of 8 bytes (00 02 31 06 00 35 31 42 in Hex) appears to be an 8-byte header that is sent everytime a single message is sent. I duplicated this with other message tests. So every packet that is sent by the Pi must start with these first 8 bytes.

The next byte of interest is 1B Hex. Based on other message tests, this is a character count for the first message sent. Knowing this, we will be able to write a formula in Python to calculate the message length and include it in the data packet. For my message test: Severe Thunderstorm Warning, this is a 27 character message. 1B hex is 27 integer.

The next 27 bytyes sent is the message, from hex 53 to hex 68.

The first message block is a total of 60 bytes. Given that my test message was 27 characters, the remaining characters must be 00 hex and it is 60 - 27 = 33 characters to transmit.

The next byte to be concerned with appears to be a checksum byte. In the case for the Severe Thunderstorm Warning message, 89 Hex.

Testing different messages resulted in very different byte values that were sent. I tried for days to recreate how this byte was calculated, but was not successful. In order to send the correct checksum value, I had to confirm the value of all the alert messages I wanted the Python program to display. In my program, this was a total of 39 alert messages. I'll talk more about the alert messages and where they will be coming from later.

You will see in the Python program the different checksum values.

The next 4 bytes is another header, 02 31 06 40. This is a header for the second message that 'could' be uploaded into the badge memory. Whether you send a second message or not, the header must still be sent.

The next group of 64 bytes must be 00 hex since no message characters are sent. After the 64 bytes are sent, another checksum and 4 byte header is sent (77 02 31 06 80). Since I am not sending another message, I can always send the byte 77 as the checksum. I verified this in other test runs.

The next group of 64 bytes must be 00 hex since no message characters are sent. After the 64 bytes are sent, another checksum and 4 byte header is sent (B7 02 31 06 C0). Since I am not sending another message, I can always send the byte B7 as the checksum. I verified this in other test runs.

FInally, another group of 64 bytes must be 00 hex since no message characters are sent. After the 64 bytes are sent, another checksum and 3 byte header is sent (F7 02 33 01). Since I am not sending another message, I can always send the byte F7 as the checksum. I verified this in other test runs.

Understanding the packet structure, I needed to determine the messages I would send. The National Weather Service has a listing of standardized watches, warnings, advisories, and statement that they issue thorugh their RSS service. You can find this listing at https://www.weather.gov/help-map. The listing also provides priority levels that was helpful in my Python code should I recieved multiple warnings and watches for my county. This way, I display the highest prioirity message.

Here examples of messages and their priority level:

'Tornado Warning, 2
'Severe Thunderstorm Warning,4
'Flash Flood Warning, 5
'Blizzard Warning, 23
'Snow Squall Warning, 24
'Ice Storm Warning, 25
'Winter Storm Warning, 26
Tornado Watch, 44

As a I said previously, for the 39 messages I planned to use, I had to test each and every one of them to get the checksum value to send when that message needs to be displayed. This is where the serial monitoring sofware came in real handy. Here are some examples from my Python program:

# Checksum based on NWS listing. This section assigns the Checksum value
'Tornado Warning': '\xBB',
'Severe Thunderstorm Warning': '\x89',
'Flash Flood Warning': '\xEA',
'Blizzard Warning': '\x27',
'Snow Squall Warning': '\x21',
'Ice Storm Warning': '\x2C',
'Winter Storm Warning': '\x97'

Now the fun begins. The Python Code. I will break down the code so you can understand each section and what I did.

***** Python Code for running the Weather Alert badge on the RPi.

The first section is to import that necessary libraries. One of the most important libraries is the 'feedparser'. This is needed to parse the RSS feed from the NWS.

import serial
from time import sleep
import feedparser
import time
import datetime

The next section of code defines and assigns the priority level of the messages based on the NWS listing.

def alertType_to_priorityNum(alertmessage):
switcher = {
# Priority level based on NWS listing. This section assigns the priority level.
    'Tornado Warning': 2,
    'Severe Thunderstorm Warning': 4,
    'Flash Flood Warning': 5,
    'Blizzard Warning': 23,
    'Snow Squall Warning': 24,
    'Ice Storm Warning': 25,
    'Winter Storm Warning': 26,
    'High Wind Warning': 27,
    'Flood Warning': 38,
    'Lake Effect Snow Warning': 42,
    'Excessive Heat Warning': 43,
    'Tornado Watch': 44,
    'Severe Thunderstorm Watch': 45,
    'Flash Flood Watch': 46,
    'Wind Chill Warning': 49,
    'Extreme Cold Warning': 50,
    'Hard Freeze Warning': 51,
    'Freeze Warning': 52,
    'Red Flag Warning': 53,
    'Freezing Rain Advisory': 65,
    'Winter Weather Advisory': 66,
    'Lake Effect Snow Advisory': 67,
    'Wind Chill Advisory': 68,
    'Heat Advisory': 69,
    'Flood Advisory': 73,
    'Dense Fog Advisory': 79,
    'Frost Advisory': 91,
    'Blizzard Watch': 99,
    'Winter Storm Watch': 103,
    'Flood Watch': 108,
    'High Wind Watch': 109,
    'Wind Chill Watch': 112,
    'Lake Effect Snow Watch': 113,
    'Hard Freeze Watch': 114,
    'Freeze Watch': 115,
    'Air Quality Alert': 123,
    'Air Stagnation Advisory': 124,
    'None': 200,
    'No Watches, Warnings or Advisories': 900
}
return switcher.get(alertmessage)

The next section of code assigns the checksum values to each of the individual messages.

def alertType_to_checkSum(alertchecksum):
switcher = {
# Checksum based on NWS listing. This section assigns the Checksum value
    'Tornado Warning': '\xBB',
    'Severe Thunderstorm Warning': '\x89',
    'Flash Flood Warning': '\xEA',
    'Blizzard Warning': '\x27',
    'Snow Squall Warning': '\x21',
    'Ice Storm Warning': '\x2C',
    'Winter Storm Warning': '\x97',
    'High Wind Warning': '\x18',
    'Flood Warning': '\xD6',
    'Lake Effect Snow Warning': '\x9E',
    'Excessive Heat Warning': '\x3C',
    'Tornado Watch': '\xDA',
    'Severe Thunderstorm Watch': '\xA8',
    'Flash Flood Watch': '\x09',
    'Wind Chill Warning': '\x85',
    'Extreme Cold Warning': '\x65',
    'Hard Freeze Warning': '\xE8',
    'Freeze Warning': '\x44',
    'Red Flag Warning': '\x9A',
    'Freezing Rain Advisory': '\x4A',
    'Winter Weather Advisory': '\xD0',
    'Lake Effect Snow Advisory': '\x1A',
    'Wind Chill Advisory': '\x01',
    'Heat Advisory': '\xDF',
    'Flood Advisory': '\x52',
    'Dense Fog Advisory': '\x8D',
    'Frost Advisory': '\x6C',
    'Blizzard Watch': '\x46',
    'Winter Storm Watch': '\xB6',
    'Flood Watch': '\xF5',
    'High Wind Watch': '\x37',
    'Wind Chill Watch': '\xA4',
    'Lake Effect Snow Watch': '\xBD',
    'Hard Freeze Watch': '\x07',
    'Freeze Watch': '\x63',
    'Air Quality Alert': '\x2D',
    'Air Stagnation Advisory': '\xBB',
    'None': '\x73',
    'No Watches, Warnings or Advisories': '\x7C'
}
return switcher.get(alertchecksum)

The next definition is the primary section that writes the packets of data to the LED badge through the USB port. You should recognize many of the bytes we obtained in the analysis from the serial monitor data dump.

def write_string_to_LEDbadge(alertMessage):

#Open the USB serial port
ser=serial.Serial('/dev/ttyUSB1', 38400)
print ('Alert Message :', alertMessage)

# Block Group 1: Send the first 8 bytes
ser.write(b'\x00')
ser.write(b'\x02')
ser.write(b'\x31')
ser.write(b'\x06')
ser.write(b'\x00')
ser.write(b'\x35')
ser.write(b'\x31')
ser.write(b'\x42')

# Determine the length of the alert message string. Convert to hexadecimal

wxStringLength = len(alertMessage)
print('String Length = ',wxStringLength)

# Block 2 is the hex equivalent of number of characters to send. Write the 1 byte.
byteBlock2 = chr(wxStringLength)
arr2 = bytearray(byteBlock2, 'utf-8')
ser.write(arr2)

# Block 3: Need to loop through the number of characters in the string. Write them.
for x in range(len(alertMessage)):
     byteBlock3 = alertMessage[x:x+1]
     ser.write(bytearray(byteBlock3,'utf-8'))

#Block 4: Send empty characters. Depends on the length of the wx_string.
# Take 60 characters less the wx_string to know how many blank characters are sent

blankChars = 60 - len(alertMessage)
print(blankChars)

for x in range(blankChars):
     ser.write(bytearray('\x00','utf-8'))

#Block 5: Send the message checksum.
alertChecksum = alertType_to_checkSum(alertMessage)
print('AlertChecksum = ', alertChecksum)

# For some reason, I had to use the "latin_1" encoding for hte byte to send correctly.
ser.write(bytes(alertChecksum, 'latin_1'))

#Block 6: Send the next 4 header bytes
ser.write(b'\x02')
ser.write(b'\x31')
ser.write(b'\x06')
ser.write(b'\x40')

#Block 7: Send empty characters
for x in range(64):
     ser.write(bytearray('\x00','utf-8'))

#Block 8: Send the next checksum and 4 header bytes
ser.write(b'\x77')
ser.write(b'\x02')
ser.write(b'\x31')
ser.write(b'\x06')
ser.write(b'\x80')

#Block 9: Send empty characters
for x in range(64):
     ser.write(bytearray('\x00','utf-8'))

#Block 10: Send the next checksum and 4 header bytes
ser.write(b'\xB7')
ser.write(b'\x02')
ser.write(b'\x31')
ser.write(b'\x06')
ser.write(b'\xC0')

#Block 11: Send the next block of 64 empty characters
for x in range(64):
     ser.write(bytearray('\x00','utf-8'))

#Block 12: Send the final 4 bytes
ser.write(b'\xF7')
ser.write(b'\x02')
ser.write(b'\x33')
ser.write(b'\x01')

#Close the serial port

ser.close()

The next section is a display timer. Every 2 minutes, the display will update should a new prioirty message be recieved.

def loop_display():
     now=time.time()
     timer = 0
     while timer < 120:
          end = time.time()
          timer = round (end-now)

Now comes the main loop. This loop goes out and gets the latest NWS RSS feed for Ohio, parses the feed looking for the county I am in, adjusting the priority level if higher priority messages come in, and then send the message to the definition which writes the message to the USB port. I tried to comment the code as much as possible.

print ('Starting loop')
loopcntr = 0


#Main Code Here
while True:
     print('Next loop')
     loopcntr +=1
     print(loopcntr)


# Reset Geauga Found flag. This flag tracks if there are alerts or not for my county.
geauga_found_flag = 0

# Get the RSS feed from NWS for Ohio
alertFeed = feedparser.parse("http://alerts.weather.gov/cap/oh.php?x=0")

# Obtain number of entries in the feed. This is a normal RSS action
num_entries = len(alertFeed['entries'])

# Reset the Priority Number variable to 900. This number will reflect No Watches/Warnings
priorityNum = 900
priorityChecksum = '\x00'

# Loop to find all occurences of Geauga County in the feed, if any, even if there are watches and warnings in the same feed. This is how we will set the priority levels.
for nws_entry_ID in range(num_entries):
     if alertFeed['entries'][nws_entry_ID]['title'] == 'There are no active watches, warnings or advisories':
     break
     areaDescription = alertFeed['entries'][nws_entry_ID]['cap_areadesc']
     print (areaDescription)
     geauga_found = alertFeed['entries'][nws_entry_ID]['cap_areadesc'].find('Geauga')
     print(geauga_found)
     if geauga_found != -1:
          geauga_found_flag = 1
          alertevent = alertFeed['entries'][nws_entry_ID]['cap_event']
          priorityNumNew = alertType_to_priorityNum(alertevent)
          if priorityNumNew < priorityNum:
               priorityNum = priorityNumNew
               alerts = alertevent

# If the Geauga Found flag is never set to 1, then there are no Warnings or Watches. Check it here. Must come after all entries have been scanned.
     if geauga_found_flag == 0:
          alerts = 'No Watches, Warnings or Advisories'
          alertChecksum = '\x7C'

     write_string_to_LEDbadge(alerts)
     print ('Geauga County : ' + alerts)

#Run the loop timer for two minutes before next update.
    loop_display()


That's it! I've been running this code successfully for many months. I set the Raspberry Pi to start the code automatically. So if we lose power or I shut down the Pi, all that needs to be done is to restore power and the Python script starts right up.

You can download the full Python script HERE.

I plan to expand the application by adding bright green, yellow, and red LEDs to indicate no warnings, watches, or warnings.

I hope you found this project interesting. Again, I must reiterate that not every LED badge or message board works the same. Many of the LED boards use the Prolific PL2303 driver, which is very common. Most of these boards are made in China and may use some kind proprietary coding for sending messages to the board. I have a larger LED board that programs completely different than the mini LED Badge I used here. So like me, you may have to do a little bit of hacking and reverse engineering to get your LED board to work.

I hope you enjoyed reading this project.

Untitled Document

Arduinio Anemometer & Windvane

Interesting Anemometer Videos


Back to Top

What is an Anemometer and Wind Vane

An Anemometer is a weather sensor device used to measure wind speed. From Wikipedia, an anemometer is a device used for measuring the speed of wind, and is also a common weather station instrument. The term is derived from the Greek word anemos, which means wind, and is used to describe any wind speed measurement instrument used in meteorology. A weather vane, wind vane, or weathercock is an instrument for showing the direction of the wind. It is typically used as an architectural ornament to the highest point of a building. The word vane comes from the Old English word fana meaning "flag".

Back to Top

Wiring the Anemometer / WindVane to the Arduino Uno

One of the best sites that I visited that showed how to hook up the Davis Anemometer / Windvane was at cactus.io in Australia. I used their approach to wire up my sensor to the Arduino Uno/Ethernet Shield.

The anemometer/windvane has an RJ-11 jack on it, similar to a telephone jack. Fortunately, I had some spare telephone wall plates that I used to wire up to the Arduino, saving the trouble of cutting the wire. The overall wiring looks as follows:

Anemometer Wiring

The colors of the wires on the anemometer jack were different than what was shown in the above illustration. So I had to map the colors of the RJ-11 plug on the sensor cable to the wall plate colors as follows:

RJ-11 Plug Color
Wire
Wall Plate Wire Color
Yellow
Power
Black
Blue
Wind Direction
Red
Red
Ground
Green
Black
Wind Speed
Yellow
Wall Plate

As you noticed in the above Arduino wiring diagram, there are two connections to the Arduino. The Wind speed circuit is connected to a digital pin (digital Pin 2) and the wind direction circuit is connected to an analog pin (Analog Pin 4).

The wind speed circuit is a switch that is activated once per revolution of the wind cups. In this hookup, we are using a 4.7K pullup resistor. This will pull the pin 2 to 5V when the switch is open. If we don't use a pullup resistor, the circuit voltage could float and cause false triggers on the input. When the mercury switch on the wind cups close, pin 2 will be pulled to GND for a short duration while the magnet passes the switch. We use this pulse on pin 2 of the Arduino to detect every time the wind cups goes through one revolution. I'll discuss later how to measure the wind speed. For now, let's start with detecting the wind direction.

The Davis wind vane has a 20k linear potentiometer attached to it. The output from the wind direction circuit is connected to an analog pin on the Arduino. As we move the wind vane around we should get a reading between 0 and 1023. The Arduino has a 10 bit A to D converter which gives us the range of 0 to 1023. This would also correspond to a voltage of 0 to 5V. In the software, we will need to convert the 0 to 1023 to a 0 to 360 range to give us the wind direction.

The potentiometer in the wind vane has a dead band that will result in the value 0 on the analog pin. The diagram below shows the dead band for the Davis anemometer/windvane I'm using for testing. In this image we are looking down over the top of the wind vane. The anemometer is resting on the cups.

Wind Direction

The wind vane is calibrated from the factory to be 0 when the vane is lined up along the length of the support bar pointing away from the mounting bracket.

Typically speaking, there are 8 compass points between 0° and 359°. These 8 compass points fall within a range of 45°. Below is a table showing how I set up the 8 compass points and the angular range I used:

Compass Point
Angular Range
North (N)
337.5° to 22.5°
NorthEast (NE)
22.5° to 67.5°
East (E)
67.5° to 112.5°
SouthWest (SE)
112.5° to 157.5°
South (S)
157.5° to 202.5°
SouthWest (SW)
202.5° to 247.5°
West (W)
247.5° to 292.5°
NorthWest (NW)
292.5° to 337.5°

In my Arduino Sketch, I use this to give me not only the wind direction in degrees but also the compass point direction.

 

This could easily be increased to 16 compass points if you want to include NNE, SSW, WNW, etc, if you wanted to do that. But keep in mind that means more code in the Arduino sketch.

Back to Top

Calibrating Wind Direction

The simplest way to set up the anemometer for wind direction calibration is to have the mounting arm pointing directly to north on the compass. This means the direction that is obtained by converting the analog input value to a direction value will line up correctly with North. However if you are unable to point the mounting arm to magnetic north then we need to apply an offset to our wind direction calculation to correct the wind direction reading.

Wind Direction

To determine the offset to apply we need to point the wind vane to magnetic north. Using a compass we can determine the angle offset from the wind vane to the support bar. The 0 to 1023 output value from the wind vane stays relative the metal support bar. We then translate the 0 - 1023 value to a 0 - 360 value it is still relative to the support bar. However our magnetic north heading is now 40 degrees to the left of the support bar.

In the situation in the diagram above we need to add 40 to the translated wind direction so that our direction reading is now showing the calibrated wind direction. In the sketch we now need to supply the offset value. To do this change the value on line 5 to #define Offset 40. In the situation below we need to subtract -45 from the wind direction. We need to set the offset on line 5 to #define Offset -45.

Wind Direction

If the magnetic north heading relative to the support bar is between 0 to 180 then we subtract the offset from the Direction output to get the adjusted wind direction. If the magnetic north heading relative to the support bar is between 181 to 360 then we add the offset to the Direction output to get the adjusted wind direction.

This is not the only way to calibrate the wind direction but it works for the way we calculate wind direction in the software.

Back to Top

The Arduino Wind Direction Sketch

The following Arduino sketch reads the values on the Analog input pin. converts the raw A/D to a compass value and then displays the direction.

Davis Wind Direction Sketch       (Download Sketch)
	// Arduino sketch to display wind direction using a Davis Anemometer / Wind Vane
	
	int VaneValue;      // raw analog value from wind vane 
	int Direction;  slated 0 - 360 direction 
	int CalDirection;   // converted value with offset applied 
	int LastValue;      // last direction value
	
	#define Offset 0; 
	
	void setup() 
	{ 
	LastValue = 0; 
	Serial.begin(9600); 
	Serial.println("Vane Value\tDirection\tHeading"); 
	} 
	
	void loop() 
	{ 
	delay(1000);                                    // wait for a second
	VaneValue = analogRead(A4);                     // Read the analog value from the A/D converter
	Direction = map(VaneValue, 0, 1023, 0, 359);    // Map the A/D value into the compass range
	CalDirection = Direction + Offset;              // Add an offset if the anemometer/windvane arm is not pointing North
	
	if(CalDirection > 360)                          // Calculate for a calibrated anemometer/windvane arm 
	CalDirection = CalDirection - 360; 
	
	if(CalDirection < 0) 
	CalDirection = CalDirection + 360; 
	
	// Only update the display if change greater than 2 degrees. Otherwise skip until a change is detected.
	if(abs(CalDirection - LastValue) > 2) 
	{ 
	Serial.print(VaneValue); Serial.print("\t\t"); 
	Serial.print(CalDirection); Serial.print("\t\t"); 
	getHeading(CalDirection);                     // Get the compass heading to display
	LastValue = CalDirection;                     // Set the LastValue variable to dtermine of direct changed > 2 degrees 
	}
	} 
	
	// Converts compass direction to heading 
	void getHeading(int direction) { 
	if(direction > 337 and direction <= 22) 
	Serial.println("N"); 
	else if (direction > 22 and direction <= 67) 
	Serial.println("NE"); 
	else if (direction > 67 and direction <= 112) 
	Serial.println("E"); 
	else if (direction > 112 and direction <= 157) 
	Serial.println("SE"); 
	else if (direction > 157 and direction <= 202) 
	Serial.println("S"); 
	else if (direction > 202 and direction <= 247) 
	Serial.println("SW"); 
	else if (direction > 247 and direction <= 292) 
	Serial.println("W"); 
	else if (direction > 292 and direction <= 337) 
	Serial.println("NW"); 
	else 
	Serial.println("N"); 
	}
		

The output in the Arduino serial monitor should look like the following:

Wind Direction Output

Back to Top

How to measure wind speed with the Anemometer

The wind cups of the anemometer assembly have a reed switch mounted inside near the shaft. This switch is activated once per revolution of the cups. To calculate the wind speed, a formula must be used to convert the number of times the switch activates per period of time to miles per hour.

According to the Davis Anemometer technical document that came with my weather station, 1 mile per hour is equal to 1600 revolutions per hour.

Using the formula V = P(2.25/T) we can calculate the speed in miles per hour, where:

    ==> V is speed in miles per hour

    ==> P is number of pulses per sample period (same as Wind Speed Sample Period on the data sheet)

    ==> T is the sample period in seconds

In the Arduino sketch, we are using the formula as provided by Davis to calculate the instantaneous wind speed in miles per hour.

Due to the random nature of switch activations on pin 2 (sometimes referred to switch bounce), a hardware interrupt on digital input pin 2 is used, which generates an interrupt on the falling edge of the pulse (5V dropping to ground and back to 5V when the switch is closed by the anemometer). The interrupt service routine (ISR) that is executed on the interrupt increments a counter.

Since the Davis spec states that the wind speed sample period is 2.25 seconds, it's safe to use a delay of 3 seconds to get an average sample for the wind speed. Even though the delay will halt the loop code for that period of time, the interrupt handler called "Rotation" is still executed. This will be the sample period (T) used in the calculation. The calculation is performed in the main loop.

Note: Code in Interrupt Service Routines should be kept to a minimum.

Back to Top

The Arduino Wind Speed Sketch

Davis Wind Speed Sketch 1        Download Sketch
	// Arduino sketch to display wind speed using a Davis Anemometer / Wind Vane
	
	#include  
	
	#define WindSensorPin (2)                   // The pin location of the anemometer sensor 
	
	volatile unsigned long Rotations;           // cup rotation counter used in interrupt routine 
	volatile unsigned long ContactBounceTime;   // Timer to avoid contact bounce in interrupt routine 
	
	float WindSpeed;                            // speed miles per hour 
	
	void setup() { 
	Serial.begin(9600); 
	
	pinMode(WindSensorPin, INPUT); 
	attachInterrupt(digitalPinToInterrupt(WindSensorPin), isr_rotation, FALLING);   // Set up the interupt
	
	Serial.println("Davis Wind Speed Test"); 
	Serial.println("Rotations\tMPH"); 
	} 
	
	void loop() { 
	Rotations = 0;                              // Set Rotations count to 0 ready for calculations 
	
	sei();                                      // Enables interrupts 
	delay (3000);                               // Wait 3 seconds to average 
	cli();                                      // Disable interrupts 
	
	// convert to mp/h using the formula V=P(2.25/T) 
	// V = P(2.25/3) = P * 0.75 
	WindSpeed = Rotations * 0.75; 
	
	Serial.print(Rotations); Serial.print("\t\t"); 
	Serial.println(WindSpeed); 
	} 
	
	// This is the function that the interrupt calls to increment the rotation count 
	void isr_rotation () { 
	
	if ((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact. 
	Rotations++; 
	ContactBounceTime = millis(); 
	} 
	}
		

The output should look like the following:

Anemometer Wiring

If you want to add more flare to the output, you can use the windspeed to determine a particular wind strength string that will related to the wind speed. For example, in the next sketch, I added a wind strength function using the following criteria (source: Wikipedia):

Wind Speed (MPH)
Wind Speed (Knots)
Wind Strength
Beaufort Number
<1 MPH <1 knot Calm
0
1 - 3 MPH 1 - 3 knots Light Air
1
4 - 7 MPH 4 - 6 knots Light Breeze
2
8 - 12 MPH 7 - 10 knots Gentle Breeze
3
13 - 18 MPH 11 - 16 knots Moderate Breeze
4
19 - 24 MPH 17 - 21 knots Fresh Breeze
5
25 - 31 MPH 22 - 27 knots Strong Breeze
6
32 - 38 MPH 28 - 33 knots Near Gale
7
39 - 46 MPH 34 - 40 knots Gale
8
47 - 54 MPH 41 - 47 knots Strong Gale
9
55 - 63 MPH 48 - 55 knots Storm
10
64 - 72 MPH 56 - 63 knots Violent Storm
11
>72 MPH >63 knots Hurricane Force
12
Davis Wind Speed Sketch 2        (Download Sketch)
// Arduino sketch to display wind speed using a Davis Anemometer / Wind Vane
		
		#include  
		
		#define WindSensorPin (2)                   // The pin location of the anemometer sensor 
		
		volatile unsigned long Rotations;           // cup rotation counter used in interrupt routine 
		volatile unsigned long ContactBounceTime;   // Timer to avoid contact bounce in interrupt routine 
		
		float WindSpeed;                            // speed miles per hour 
		
		void setup() { 
		Serial.begin(9600); 
		
		pinMode(WindSensorPin, INPUT); 
		attachInterrupt(digitalPinToInterrupt(WindSensorPin), isr_rotation, FALLING);   // Set up the interupt
		
		Serial.println("Davis Wind Speed Test"); 
		Serial.println("Rotations\tMPH\tWind Strength"); 
		} 
		
		void loop() { 
		Rotations = 0;                              // Set Rotations count to 0 ready for calculations 
		
		sei();                                      // Enables interrupts 
		delay (3000);                               // Wait 3 seconds to average 
		cli();                                      // Disable interrupts 
		
		// convert to mp/h using the formula V=P(2.25/T) 
		// V = P(2.25/3) = P * 0.75 
		WindSpeed = Rotations * 0.75; 
		
		Serial.print(Rotations); Serial.print("\t\t"); 
		Serial.print(WindSpeed); Serial.print("\t");
		getWindStrength(WindSpeed); 
		} 
		
		// This is the function that the interrupt calls to increment the rotation count 
		void isr_rotation () { 
		
		if ((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact. 
		Rotations++; 
		ContactBounceTime = millis(); 
		} 
		}
		
		// converts wind speed to wind strength 
		void getWindStrength(float speed) { 
		
		if(speed < 1) 
		Serial.println("Calm"); 
		else if(speed >= 1 && speed < 4) 
		Serial.println("Light Air"); 
		else if(speed >= 4 && speed < 8) 
		Serial.println("Light Breeze"); 
		else if(speed >= 8 && speed < 13) 
		Serial.println("Gentle Breeze"); 
		else if(speed >= 13 && speed < 19) 
		Serial.println("Moderate Breeze"); 
		else if(speed >= 19 && speed < 25) 
		Serial.println("Fresh Breeze"); 
		else if(speed >= 25 && speed < 32) 
		Serial.println("Strong Breeze"); 
		else if(speed >= 32 && speed < 39) 
		Serial.println("Near Gale"); 
		else if(speed >= 39 && speed < 47) 
		Serial.println("Gale"); 
		else if(speed >= 47 && speed < 55) 
		Serial.println("Severe Gale"); 
		else if(speed >= 55 && speed < 64) 
		Serial.println("Storm"); 
		else if(speed >= 64 && speed < 72) 
		Serial.println("Violent Storm"); 
		else 
		Serial.println("Hurricane Force"); 
		}
		
		

The output should look like the following:

Anemometer Wiring

This should give you enough information to use the David Anemometer / Wind Vane

Back to Top

The Arduino Wind Speed and Direction Sketch

Finally, I took some of the code used on the wind direction and wind speed sketch to create a combined program that does both. Using basic math, you can also display the wind speed in knots or km/hr. We can also display the window direction by degrees or direction based on the wind vane position.

In this revised sketch, I will use a timer interrupt that will generate an interrupt when the 0.5 second timer is triggered. The interrupt service routine called "isr_timer" will execute and this will be used to create a 2.5 second sample period.

I used the TimerOne library to provide the timer interrupt functionality. You can download this library from several sources including the arduino website.

Make sure that the variables WindSensorPin (line 2), WindVanePin (line 3) and VaneOffset (line 4) match your configuration.

Davis Wind Speed / Direction Sketch        (Download Sketch)
// Arduino sketch to display both Wind Speed and Direction
		
		#include "TimerOne.h"                     // Timer Interrupt set to 2 second for read sensors 
		#include  
		
		#define WindSensorPin (2)                 // The pin location of the anemometer sensor 
		#define WindVanePin (A4)                  // The pin the wind vane sensor is connected to 
		#define VaneOffset -180;                  // define the anemometer offset from magnetic north 
		
		int VaneValue;                            // raw analog value from wind vane 
		int Direction;                            // translated 0 - 360 direction 
		int CalDirection;                         // converted value with offset applied 
		int LastValue;                            // last direction value 
		
		volatile bool IsSampleRequired;           // this is set true every 2.5s. Get wind speed 
		volatile unsigned int TimerCount;         // used to determine 2.5sec timer count 
		volatile unsigned long Rotations;         // cup rotation counter used in interrupt routine 
		volatile unsigned long ContactBounceTime; // Timer to avoid contact bounce in isr 
		
		float WindSpeed; // speed miles per hour 
		
		void setup() { 
		
		LastValue = 0; 
		
		IsSampleRequired = false; 
		
		TimerCount = 0; 
		Rotations = 0;                          // Set Rotations to 0 ready for calculations 
		
		Serial.begin(9600); 
		
		pinMode(WindSensorPin, INPUT); 
		attachInterrupt(digitalPinToInterrupt(WindSensorPin), isr_rotation, FALLING); 
		
		Serial.println("Davis Anemometer Test"); 
		Serial.println("Speed (MPH)\tKnots\tDirection\tStrength"); 
		
		// Setup the timer interupt 
		Timer1.initialize(500000);              // Timer interrupt every 2.5 seconds 
		Timer1.attachInterrupt(isr_timer); 
		} 
		
		void loop() { 
		
		getWindDirection(); 
		
		// Only update the display if change greater than 2 degrees. 
		if(abs(CalDirection - LastValue) > 2) { 
		LastValue = CalDirection; 
		} 
		
		if(IsSampleRequired) { 
		// convert to mp/h using the formula V=P(2.25/T) 
		// V = P(2.25/2.5) = P * 0.9 
		WindSpeed = Rotations * 0.9; 
		Rotations = 0; // Reset count for next sample 
		
		IsSampleRequired = false; 
		
		Serial.print(WindSpeed); Serial.print("\t\t"); 
		Serial.print(getKnots(WindSpeed)); Serial.print("\t"); 
		Serial.print(CalDirection); 
		getHeading(CalDirection); Serial.print("\t\t"); 
		getWindStrength(WindSpeed); 
		} 
		} 
		
		// isr handler for timer interrupt 
		void isr_timer() { 
		
		TimerCount++; 
		
		if(TimerCount == 6) 
		{ 
		IsSampleRequired = true; 
		TimerCount = 0; 
		} 
		} 
		
		// This is the function that the interrupt calls to increment the rotation count 
		void isr_rotation() { 
		
		if((millis() - ContactBounceTime) > 15 ) { // debounce the switch contact. 
		Rotations++; 
		ContactBounceTime = millis(); 
		} 
		} 
		
		// Convert MPH to Knots 
		float getKnots(float speed) { 
		return speed * 0.868976; 
		} 
		
		// Get Wind Direction 
		void getWindDirection() { 
		
		VaneValue = analogRead(WindVanePin); 
		Direction = map(VaneValue, 0, 1023, 0, 359); 
		CalDirection = Direction + VaneOffset; 
		
		if(CalDirection > 360) 
		CalDirection = CalDirection - 360; 
		
		if(CalDirection < 0) 
		CalDirection = CalDirection + 360; 
		
		} 
		
		// Converts compass direction to heading 
		void getHeading(int direction) { 
		if(direction > 337 and direction <= 22) 
		Serial.print(" N"); 
		else if (direction > 22 and direction <= 67) 
		Serial.print(" NE"); 
		else if (direction > 67 and direction <= 112) 
		Serial.print(" E"); 
		else if (direction > 112 and direction <= 157) 
		Serial.print(" SE"); 
		else if (direction > 157 and direction <= 202) 
		Serial.print(" S"); 
		else if (direction > 202 and direction <= 247) 
		Serial.print(" SW"); 
		else if (direction > 247 and direction <= 292) 
		Serial.print(" W"); 
		else if (direction > 292 and direction <= 337) 
		Serial.print(" NW"); 
		else 
		Serial.print(" N"); 
		} 
		
		// converts wind speed to wind strength 
		void getWindStrength(float speed) { 
		
		if(speed < 1) 
		Serial.println("Calm"); 
		else if(speed >= 1 && speed < 4) 
		Serial.println("Light Air"); 
		else if(speed >= 4 && speed < 8) 
		Serial.println("Light Breeze"); 
		else if(speed >= 8 && speed < 13) 
		Serial.println("Gentle Breeze"); 
		else if(speed >= 13 && speed < 19) 
		Serial.println("Moderate Breeze"); 
		else if(speed >= 19 && speed < 25) 
		Serial.println("Fresh Breeze"); 
		else if(speed >= 25 && speed < 32) 
		Serial.println("Strong Breeze"); 
		else if(speed >= 32 && speed < 39) 
		Serial.println("Near Gale"); 
		else if(speed >= 39 && speed < 47) 
		Serial.println("Gale"); 
		else if(speed >= 47 && speed < 55) 
		Serial.println("Severe Gale"); 
		else if(speed >= 55 && speed < 64) 
		Serial.println("Storm"); 
		else if(speed >= 64 && speed < 72) 
		Serial.println("Violent Storm"); 
		else 
		Serial.println("Hurricane Force"); 
		}
		

The output should look like the following:

Anemometer Wiring

Untitled Document

Listen to N8MDP's Live WebSDR

Heard of WebSDR? Listen to my 40 and 20 meter SDR using the WebSDR tool. Click on the image below:
WebSDR

Latest Propogation Data
radar

 

Latest DX Spots

radar

 

radar

 

 

 

 

Back to top of page

Untitled Document
|   Home   |    SSTV   |    DSSTV   |    SDR   |    Aviation   |    Projects   |    Weather   |    Logbook    |    Contact   |


© 2024 , Jonathan Tucker N8MDP. All Rights Reserved.
Image by macrovector_official on Freepik