Coastal webcam with weather and tide data

Following on from my Raspberry Pi e-Ink display tide time project, I’ve made a web cam that will tweet photos overlaid with weather and tide information for any UK coastal place. It uses the official Raspberry Pi camera module.

Like the InkypHAT project, this uses Beautiful Soup to screen-scrape the UK Met Office web site, but a more general web page that has much more weather data, such as visibility, wind speeds and so on. Where my e-Ink project was designed to run once day, early in the morning, this can run hourly and should always give the current weather information.

You can tweak this code to work on pretty much any UK coastal place – all you need is the geohash code for your location, which you can find by browsing the Met Office web site and taking the final part of the URL: For example, Godrevy in Cornwall is gbujsuykk because the weather page is found at http://www.metoffice.gov.uk/public/weather/forecast/gbujsuykk.

Here’s what you need:

  • A Raspberry Pi computer with internet access (wifi or ethernet) and a recent version of Raspbian.
  • A Raspberry Pi camera module.
  • Some power.
  • A suitable seaside location with a nice view .
  • Optional: a weatherproof container for the Pi and camera to aid location.

First, install Beautiful Soup on the Raspberry Pi:
sudo apt-get install python3-bs4
This is a Python library that makes extracting data from other web sites much easier than it would otherwise be.

Next you may need to install the picamera library. This is a Python library that makes using the official camera module easier. You probably already have it, but check the installation page if you are not sure.

A weatherproof outdoor enclosure would be a good idea – lacking that on holiday I improvised in a Heath Robinson fashion with some cotton and blutak which lasted a few days, until someone opened the patio door and the whole thing rather fell apart:

Finally, if you want to tweet your photos you will need a Twitter account that has been verified with a phone number to get your Twitter API keys. You’ll also need to install the twython library. I recommend following the excellent instructions on the Raspberry Pi education web site: Getting Started with the Twitter API.

I learned a bit about the picamera and twython libraries in our Picademy photo booth project, and this builds on my learning there.

You can customise the code below with different locations and screen resolutions. When you run it, it will show a preview image for about 5 seconds and I suggest commenting out the line
twitter.update_status_with_media(status=message, media=photo)
when you are testing it to avoid spamming Twitter. Note that the preview image is only visible on the Pi’s HDMI output – you cannot see it over VNC*, as the camera module sends data straight to the Pi’s graphics chip. It also doesn’t save the photos. Each one is called webcam.jpg and is overwritten when a new one is taken, though if you do keep them you can make a time-lapse sequence (click on image below for animated GIF loop – look carefully and you can see the tide come in and go out):

* UPDATE – I’m a bit late to this, but you can see Raspberry Pi camera previews over VNC – see this official post on using a free RealVNC login to view remotely things like camera module previews and Minecraft that write straight to the graphics chip. I’ve tried this now and it works! Look for the section headed ‘PLAYING MINECRAFT AND OTHER DIRECTLY RENDERED APPS REMOTELY’.

You could do something clever to change exposure times or ISO settings at different times of day or depending on the weather – I haven’t tested this at night yet, but I suspect you won’t see much!

To automate the process I recommend using crontab – you can easily configure this using the Raspbian PIXEL desktop, just go to System Tools and Scheduled Tasks, for example to run every hour:

Here’s the Python code. I’m finding my way with Beautiful Soup and it may be very inefficient. Also I must give a disclaimer that the code may break at any time if the Met Office change their web site and no accuracy is offered for the tide time information – always cross-reference with another source if going out on the coast.

#!/usr/bin/python
# -*- coding: utf-8 -*-

# written by Giles Booth
# http://www.suppertime.co.uk/blogmywiki
# a coastal web cam using official Raspberry Pi camera
# based on my Picademy photobooth without buttons or filters
# and my InkyPHAT tide time project

from picamera import PiCamera
import picamera
from time import sleep
import datetime as dt
from twython import Twython
import requests
from bs4 import BeautifulSoup
import string

from auth import (
    consumer_key,
    consumer_secret,
    access_token,
    access_token_secret
)

twitter = Twython(
    consumer_key,
    consumer_secret,
    access_token,
    access_token_secret
)

def strip_non_ascii(string):
    ''' Returns the string without non ASCII characters'''
    stripped = (c for c in string if 0 < ord(c) < 127)
    return ''.join(stripped)

# Visibility (E = Excellent, VG = Very Good, G = Good, M = Moderate, P = Poor, VP = Very Poor)
visibilityDict = {'E': 'Excellent', 'VG': 'Very Good', 'G': 'Good', 'M': 'Moderate', 'P': 'Poor', 'VP': 'Very Poor'}

# Fetch weather & tides page from Met Office web site
# Find other geohash location codes on the site, eg
# Godrevy, Cornwall gbujsuykk
# Walton-on-Naze u10yuyqrf
# Gt Yarmouth u135pr5sv
page = requests.get("http://www.metoffice.gov.uk/public/weather/forecast/gbujsuykk")
soup = BeautifulSoup(page.content, 'html.parser')

# extract full location name
location = soup.find_all('h1', class_='offScreen')[0].get_text()
location = location.strip()
print(location)

#extract temperature
nowTemp = soup.find_all('i', class_='icon icon-animated')[0].get_text()
nowTemp = strip_non_ascii(nowTemp) + 'C' # picam cannot overlay degree signs
nowTemp = nowTemp.strip() # remove trailing and following spaces
print(nowTemp)

# extract outlook
weatherRow = soup.find('tr', class_='weatherWX')
weatherDescs = weatherRow.find_all('td')
weatherNow = weatherDescs[0].get('title', '')
print(weatherNow)

# extract wind info
windRow = soup.find('tr', class_='weatherWind wxContent')
windDescs = windRow.find_all('i', class_="icon")
windSpeed = windDescs[0].get_text()
windDirection = windDescs[0].get('data-value2', '')
windSpeed = windSpeed.strip() # remove spaces
windDirection = windDirection.strip() # remove spaces
print(windSpeed,windDirection)

# extract visibility
visibilityRow = soup.find('tr', class_='weatherVisibility wxContent')
visibilityDescs = visibilityRow.find_all('td')
visibility = visibilityDescs[0].get_text()
visibility = visibility.strip()
visibility = visibilityDict[visibility]
print(visibility)

# extract tide times
tideTimeDiv = soup.find('div', class_='tideTimes')
tideTimes = tideTimeDiv.find_all('tbody')
todayTideTimes = tideTimes[0]
tideList = todayTideTimes.find_all('td')
tides = 'Tides: '
tagCount = 0
for tag in tideList:
    tides = tides + tag.get_text()
    if tagCount == 2: # put metres & separator after every 3rd tag (height)
        tides = tides + 'm | '
    else:
        tides = tides + ' '
    tagCount += 1
    if tagCount > 2:
        tagCount = 0
tides = tides[:-3] # kludge to remove unwanted seperator at end
print(tides)

# set up camera
camera = PiCamera()
camera.rotation = 180 # my camera is upside down!
camera.resolution = (640, 480)
camera.start_preview(alpha=200)
sleep(5) # a delay of at least 2 seconds is needed for camera to warm up, auto exposure etc

start = dt.datetime.now()
camera.annotate_text_size = 16
camera.annotate_background = picamera.Color('blue')
camera.annotate_text = str(location + " WebCam test " + dt.datetime.now().strftime('%Y-%m-%d %H:%M') + '\n' + weatherNow + ' ' + nowTemp + '  Wind: ' + windSpeed + 'mph ' + windDirection + '  Visibility: ' + visibility + '\n' + tides)

camera.capture("/home/pi/webcam.jpg")
photo = open('/home/pi/webcam.jpg', 'rb')
message = "Automated " + location + " coastal webcam test " + dt.datetime.now().strftime('%H:%M')
# comment out next line to stop twitter spam while testing
twitter.update_status_with_media(status=message, media=photo)

camera.stop_preview()
camera.close()
This entry was posted in Raspberry Pi and tagged , , , , , , . Bookmark the permalink.

Leave a Reply