Building A Weather Station
office review workflow design javascript data


This post describes a project I created to solve several problems I had with weather. The project is comprised of weather sensors, monitoring hardware, and a webapp I built. The webapp manages weather data streaming from instrumentation I have setup at my house. These tools allow me to monitor the current weather conditions, visualize salient data, and receive custom alerts when certain anomalies are detected by my sensors. Here is a picture of my weather station serving data to a consumer running in my office:


The weather creates unique conditions where I live. My house is located on a mountainside relatively close to the highest winds every recorded on earth. The elevation and mountain geography creates conditions at my house that can be markedly different than in surrounding towns. Anabatic winds force warm, moisture-laden air masses up the mountain to produce a bevy of weather oddities and extreme conditions. Damaging winds can hew through the mountain-scape, while the valleys below remain untouched. Freezing clouds can accrete rime at my house, while the weather in town can be relatively placid. Snowfall totals can differ substantially between my house and other locations only a few miles away.

My motivation for building a weather station started as a way to solve problems that arose from the disparity in weather between my house and surrounding locations. When I query the conditions at my house using or any other Internet service, the reported weather can deviate wildly from the actual conditions at my house.1 These weather inaccuracies are especially problematic when traveling. Capricious weather can render the access road to my house impassible.

I wanted to build a weather station that would allow me to obtain accurate weather measurements at my house, so that I could stay apprised of the conditions. The weather station project I detail below now provides accurate weather data that I use to make informed decisions about how safe it is to travel to or from my house at any given time.


I started this project by researching weather instrumentation. I first considered using various Arduino or Tessel configurations, but I soon came to the conclusion that the available hardware was not a good fit for my project. I needed weather resistant hardware that could operate in extreme conditions, preferably with low power consumption.

My research eventually lead me to purchase the Davis 6250 Vantage Vue for this project because it had several advantages compared to other solutions I considered. Davis Instruments produces some of the best weather instruments on the market.2 The build quality and caliber of sensors used in their hardware is excellent. Another advantage of the Vantage Vue is that it has built-in integration with Weather Underground, which provides accessibility to collected data through a very well designed API. This API allows me to collect the data measured from the weather station at my house as well as supplementary data and summary analyses available through Weather Underground, such as regional forecasts, hazardous weather alerts, and predicted snowfall. The API also allows me to access my weather station data remotely so I can view the conditions at my house through the Weather Underground iOS app.


The first step to building my weather station was to replace the console. The Vantage Vue ships with a diminutive wireless console that looks like it’s right out of 1986. As soon as I picked it up, I knew that the non-color display and lack of weather visualizations would not suffice. I didn’t want to stare at a bunch of raw numbers. I wanted my weather station to display data on big color screens all over my house with aesthetically pleasing visualizations.

For my weather station, I split the console functionality into two separate entities—a server to manage the data and clients to display the data. There were many advantages to decoupling the data from the display. Instead of one console, I could now have as many consoles visualizing the data as I wanted by setting up a centralized server to broadcast data to each of the clients. Writing my own server also provided inroads to customization and enhanced functionality. I wanted to build additional features into my weather station software that Davis didn’t include in their software. Since my server would be networked, I could also incorporate other Internet data sources into my weather station should I choose to do so.3

The only component I needed for my weather station server was a Raspberry Pi Model B and wireless network adapter to connect into my home network. The Pi is inexpensive and energy efficient, making it ideal for running my weather station. I scavenged the remaining components for the server by raiding my electronics graveyard for spare parts. The Pi doesn’t ship with memory or a power supply, so I used a power supply taken from an old iOS USB wall plug, a microUSB-to-USB cable from an old digital camera, and an SD card that came with the digital camera for memory. Here’s a picture of my server hardware. The micro-USB cable on the left is for power, the HDMI cable at the bottom is for the visual display, and the USB wireless network adaptor on the right side is for serving data to consumers:

For the client in my office pictured above, I used an old Apple Cinema display that I originally purchased while writing my Ph.D. thesis. The only other hardware I needed for the office monitor was a spare HDMI-to-DVI adaptor I pilfered from another project to connect the display to the Pi. Since the Pi is networked, I can also retrieve my weather visualizations through any networked device with a web browser.


I built the webapp for this project entirely in Javascript. The server is written using Node mostly because it eliminated the cognitive burden of switching back and forth between different programming languages on the client and server. Node also had several excellent libraries for handling some of the features I wanted to build into my webapp, which also made it a good choice for this project. Using Node on the Raspberry Pi is straight-forward thanks to helpful installation guides. I installed a Debian distribution on my Pi and then used the compiled binaries for Node and Chromium for a web browser.

For rendering the weather visualizations, my webapp uses websockets. Websockets are part of the HTML5 initiative to improve real-time, event-driven applications on the web by providing bi-directional communication channels between the browser and server over a single TCP connection. My weather app uses websockets to send incoming weather data to clients as it is receive by the server through a persistent connection. This architecture allows clients like the one in my office to update in real-time without the need to manually refresh the browser.4

The server-side code for my webapp is remarkably concise thanks to Node and several excellent libraries that I am leveraging. I’m using and express to handle the networking and routing logic, respectively. Node is very readable in my opinion, but the code may look strange to someone not familiar with it. I use callbacks and closures extensively as they’re common idioms in Node’s asynchronous, event-driven, non-blocking architecture. Here’s the core server-side code for my webapp:

// module imports
var alert_trigger = require('./alerts').alert_trigger,
    express = require('express'),
    http = require('http'),
    app = express(),
    server = http.createServer(app).listen(3000),
    io = require('').listen(server);

// constants
var base_url = '',
    api_key = 'my_api_key'
    pws = 'my_weather_station',
    features = '/alerts/almanac/forecast/forecast10day/hourly10day/conditions/q/',
    data_type = '.json';

var url = base_url + api_key + features + pws + data_type;
var radar = [base_url,

// app config
app.configure(function() {
    app.use(express.static(__dirname + '/public'))
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }))
    app.use('/static', express.static(__dirname + '/static'));
    app.set('views', __dirname + '/views')
    app.set('view engine', 'jade')

// weather fetcher
function get_weather(url, fn) {
    http.get(url, function(res) {
        var body = '';
        res.on('data', function(chunk) {
            body += chunk;
        res.on('end', function() {
            var wu_res = JSON.parse(body);
            wu_res['map'] = radar;

// app route
app.get('/rime/', function (req, res) {
    res.render('index', {title : 'Rime'})

// websocket
io.sockets.on('connection', function(socket) {
    setInterval(function() {
        get_weather(url, function(wu_res) {
            socket.emit('weather', wu_res)
    }, 60 * 10 * 1000);

get_weather() fetches weather data and processes the resulting JSON in the response. Visualizations are routed to /rime/. My websocket pushes data to the clients with sockets.emit() and fires text alerts, when appropriate, using alert_trigger().


As my server receives weather data from my Vantage Vue, it inspects the data to see if any alerts need to be issued. I have alerting logic setup to fire off text messages to my phone and my wife’s phone if the weather sensors measure certain conditions, such as if the wind exceeds a certain speed or the temperature drops below a critical value. I also send alerts to my phone that are issued by the National Weather Service, such as winter storm warning predictions or other weather advisories. This functionality is tremendously useful when traveling as I don’t always regularly examine the weather on my iPhone.

My server manages text alerts through Nodemailer and a Google SMTP server I configured for my webapp. Here’s an example alert I recently received from my weather station app, which I call Rime:


On the client side, I wanted to replace the stock Davis console with a visualization dashboard to display weather data that is important to me. I subdivided the visualization into three sub-components, which I call radar view, weather strip, and weather line, respectively.

The radar view is obtained directly from Weather Underground and appears in my dashboard as a GIF image. This visualization is extremely useful. If I want to take the dog for a walk or do some yard work outside, I can see at a glance if there are any storms or inclement weather approaching. The GIF animation is nice because it allows me to gauge the speed and direction of weather patterns and determine if and when they are likely to reach my house.

To render the radar GIF, I first generate a socket object on my private network to connect to my server’s static IP. Next, I instantiate a listener for the message event. When the server sends this client a message, the client updates the GIF in the browser by removing the old GIF and replacing it with a new image. I use a small JQuery function to achieve this functionality. Here’s the code:

var socket = io.connect('');

socket.on('weather', function(data) {
    $('#radar').append($('<img>', {
        id: "radar_img"

I built the remaining two visualizations for my dashboard using D3. D3 is a tremendously powerful library that facilitates low-level DOM manipulation of HTML, CSS, and SVG. Fortunately, the visualizations I created for this project were fairly simple to build because I didn’t need to generate any interactivity like hover-effects, linked elements, or transitions. However, because D3 is fairly low-level, these two visualizations still required about 600 lines of code in total to generate.

I call the first visualization I designed a weather strip. It shows the most recent output from several of the sensors on my Vantage Vue. Here’s what it looks like:5

Moving left to right, this viz shows the current temperature on the left with a short text description, followed by wind speed and direction in the center, and the daily snow fall total and current wind chill on the right. Alerts (not pictured) show up in blaze orange to the right of the weather text description. Adding labels, numbers, and units of measure cluttered the interface too much so I elected to elide these details. Temperature is displayed Fahrenheit, wind in MPH, and snowfall in inches.

I call the second dashboard visualization a weather line. It provides a summary of the predicted forecast for the current day and next several days. One of my favorite iOS apps is Weather Line, and I designed this dashboard visualization with inspiration from the iOS app. Copy what you like. Here’s an example of my weather line viz:

The blue and gray lines show the daily high and low temperatures, respectively, along with icons for the associated conditions. Each vertical line demarcates the day. The current day is highlighted in light blue. Together, the weather strip and weather line visualizations provide a current view and long-term outlook of the weather.


With the architecture I’ve described in this post, my weather station now solves most of the problems I previously had with weather and travel. In addition, I’ve found a number of other situations where having weather dashboards around the house is useful. Any time I’m leaving the house to walk the dog, mow the lawn, or run errands, it’s useful to quickly glance at the weather dashboard on my way out the door to select the correct outerwear. Pulling out my phone, navigating to a weather app, and waiting for it to load inaccurate weather data feels like it takes an eternity by comparison. I’m also experimenting with adding a few other features to the dashboard that should be useful this winter. My wife is a big cross country skier and has already requested that I add the current conditions for her favorite ski courses so that she can quickly prepare for her morning workout.

  1. I don’t know the first thing about weather prediction, but I suspect most weather services try to infer conditions for a given location by interpolating data they’ve collected from surrounding sensors. In most cases, this behavior probably works without issue, but at my location, it introduces serious inaccuracies. 

  2. Davis weather stations are not cheap. If I was to setup a more economical weather station, I’d probably select the Ambient 2080, which is a good compromise between cost, functionality, and durability. 

  3. One feature I’m working on right now is to integrate Aurora Borelis predictions into my weather station using geomagnetic data and the NOAA Polar Operational Environmental Satellites. We have epic auroras on some winter nights. 

  4. I’d eventually like to tap into the real-time data streaming directly from the Vantage View, rather than using the API

  5. My app is named Rime. When I took this screen shot of my app, it was graupeling outside of my house. The irony was not lost on me.