Skip to content

pbeeken/WeatherKiosk

Repository files navigation

Weather Kiosk

This is an experiment to build a tide/weather viewer for a specific location using a Raspberry Pi connected to an old display. The display will be encased in a weatherproof box an mounted outside a boatyard office. (Assuming everything else goes well)

Beginnings

This started out as a quick and dirty experiment with a little used older model RasPi B model that I had gotten for a different purpose. I had an old monitor with a DVI input which I could cheaply adapt to HDMI. We had a mechanical tide clock that was never accurate at a boat club where I teach sailing and thought, there has to be a better system. Discovering that tide information is readily available from NOAA I decided to give it a try. I could always fall back on harmonic parameters (also available from NOAA) if the source became unavailable.

This is a complete refactoring of the original design. I learned a lot about html5 layouts and how to to keep screens from blinking and such.

Methods

Being very comfortable with python, matplotlib, numpy and a desire to learn how to make effective use of pandas dataframes I figured I could grab the data with a short program, build a graph (.png) and use a web browser to arrange and display the results. I do these projects for the learning experience as much as for anything else. I didn't want to just pull the data from the website because there is s a bunch of other graphics and materials I didn't want cluttering the screen. It went so successfully I started to expand the project.

Considerations

I use a web page that self updates and thus picks up any changes to the graphics. Once I got the tide part working I realized I had a lot more screen real estate and wanted to provide other useful tidbits. There are nearby weather buoys which record wind and, in some case, wave information. The national weather service provides a very elegant REST interface to fetch forcast information, although at the time of this writing no marine forcasts as of yet. Being a club mostly devoted to stick and rag people this is nice to have to remind people of what conditions are like outside the sheltered cove where we sit.

Another important part to make this project a reality is to turn the whole machine into a 'kiosk' that can be reset by simply turning it off and then on again. This part turned out not to be as straightforward as I originally thought. It took me a day to get the details right to pull this part of it together.

Finally is the building of a weatherproof case that allows the Pi to not get too hot. (Fortunately the wall where this is likely to be mounted is just inside a covered area out of direct weather.) For me, this is the easy part.

Making the Pi into a kiosk device

There is a seperate document on this. There is a lot of helpful information out there but, really, every setup is different and each use case requires tinkering, experimentation and testing. After starting with a full copy of Raspbian and peeling away unneeded software, I downgraded to Raspian Lite and built up. Because I am running python I needed to get a few important libraries installed that worked correctly on a RPi (hint, don't use pip in this situation, matplotlib, numpy and pandas need to be built specifically for the arm processor). Could I have used node? Sure, but because I am gathering data from disparate sources and wanted the convenience of pandas' robust importing to read and formatting data I'm kinda forced along the python path. I have since discovered that there is an effort to build a matplotlib type library in ts. I was even able to write a simple caching tool so I wouldn't be rude to the NWS and NOAA servers. In short: not a learning curve I wanted to go up right now.

Some of the tools I needed to build

While I had some familiarity with html from back in the days where all pages were built by hand, the huge addition of new tags and 'best practices' of how to set up css was new. The idea was to let the html do the layout while asynchronous background processes kept the graphics up to date. The main processes that I planed to have running:

  • tidesGraph.py : generates a huge graph similar to the one generated by NOAA. I liked the overall layout but I wanted to make my own so I could customize the display. Since it is a "clock" I run this about once every five minutes. I cache the data once per day since the update only looks out about 2 days. Oddly enough this is actually important near the target location as different boats where work has to be done aloft can time their repairs to the time of the tide. For some kinds of work you can pull the boat alongside a tower and access the place you need to go.
  • tidesGraphic.py : generates a cartoon of the tide level with an arrow that shows the current direction of the tide. Only needs to be updated once evey 30min or so.
  • tidesTable.py : builds a table of the next 4 tide extrema for easy reference. I use css to format it nicely for the screen
  • Winds.py : generates a wind graph of average and max wind for display. It is run about once every 15 minutes. Since the download is just a csv file it doesn't hammer too much. The tricky part here is that the buoy data can be unreliable. There are blank spots that have to be filtered, sometimes there is nothing at all so I had to devise a scheme to fall back to more reliable but not necessarily proximate sources to populate the chart.
  • forcast.py : work in progress. Right now I grab land information using the inagurated REST calls once an hour but what I want is are the nice marine forcast graphics that I can get in a web page.
  • tidedata.py : the home of a library of routines used by the above generators.
  • chromium-browser with a whole bunch of command line settings to run in a kiosk mode (a minimal Xwindows framework has to be installed, of course)

Because this is a kiosk I don't want to have to manage potential issues with handling and massaging data sets so I launch the above routines using a crontab as independent services. This way I don't have to deal with any potential leaks or other long term oddities that might crop up. Just restart the process. The only available 'fix' if something should go wrong (power glitch or whatever) is to unplug the box and re-plug it in.

Newer Addition

I have added a routine to fetch timing data from the US Naval Observatory. Because of a CORS hiccup I have made pything cgi routines to perform the data fetch. What I will do is what I have done for the tide data and cache the result every day reduce any demands on the USNO server. I only need fetch this informaion once per day. PS ProTip: when setting up your cgi-bin don't forget to set the executable bit. DUUHHH.

TODO:

  • I would like to have the screen shut down in the evening and start up at around dawn. I might even want it to turn off and turn on based on an external clock? This may require some electronic tinkering. Done a cron job shuts the processor down. When the video stops the monitor goes into a very low power state (works for this situation). The next morning the harbormaster just cycles the power button and all things begin a fresh.
  • Explore some hidden ways to put error messages into the screen, display some icons in discrete locations that indicate the health of the system. Done to some extent. There are subtle messages and formatting changes that get made when something is amiss. As mentioned above, the fix is to simply unplug and replug it in. So far the multiday burn-in tests have shown no unexpected behavior.
  • The moon image is ugly. I grabbed some jpgs from a website that look OK when viewed at full resolution but don't look good when rendered. Solution is to build an svg cartoon to display. Done I draw the cartoon using matplotlib and export as an svg. I can control the size and shape precisely and it scales to whatever size I want.

Additional notes

  • README.md : This overview
  • RaspberryPiKiosk.md : Work toward the kiosk setup
  • WeatherTests.ipynb : Jupyter Notebook with the experimentation on the data harvesting and presentation.

Because there is an interaction between the cgi-bin, timed updates using cron and shell scripts there are a few moving parts that have to inderoperate. The bin/ folder contains the materials that need to run asynchronously with the web page kiosk to update the background information. In addition there are crontab scripts that have to run as well.

Moon

Finally I built an background app (triggered by a cgi-bin request) to provide 3 moon 'lunes' to represent the current state of the moon to superimpose on the tide graph (for pure giggles.) It pulls its information from the USNO who maintain a really equisite REST API.

REEFACTOR 2022

Refactoring 2022

The final device tag worked well the first summer and did exactly what I wanted. Another sailor asked the valid question as to why radar might not be on the screen. While the original NOAA radar choked the poor little $\color{red}{Rasp} \Pi$ the new gif animation they released mid-summer 2022 worked perfectly. So I figured out how to embed it in the page.

This, of course, led to a rethinking of the whole 'full stack' management of the various elements. Instead of carefully choreographing the action with crontab, cgi calls and such why not control the whole thing through the web applications.

The hard layout work is done. I need to...

  • Since I a launch the browser on the pi with all the safeties off (it's a kiosk! No one is typing things in.) We can run scripts directly from the code on a schedule we set.
  • After setting up the frame we only need to toggle the visibility of the three <div></div> regions in the middle on a schedule.
  • This keeps the outer region (with a clock and a sunrise and sunset time) consitstent. I can even put easter eggs in the border area to flag the health of the system.

I created a branch from the main route with the tag Working-V01 so I could get back to my original kuldgy system. Now that I know what the final product should look like here is the overall plan.

  • At boot, the system will...

    • pull the latest changes from git updateSystem.sh.
    • move files where they need to be if not in the "kiosk" folder.
    • pre-load the initial graphics (all python programs).
    • launch the local server (for local control)
    • launch the browser in kiosk mode.
  • During operations commands from the browser will periodically...

    • run the graphics commands to update images.
    • based on the day of the week, cycle through the information screens.
    • N.B. CORS protections bedevil us. I tried moving all the REST calls to the js code to no avail. I tried a number of online tricks to circumvent the CORS security per many web suggestions. Many of these references were old. I now remember why I have the cgi calls in the web page code. Since we run a local server the only practical way to do all the CORS calls is via the server. So we will be keeping these code bits. Maybe we will organize them better.

Much of the work will be relocating and consolidating the different codebits.

System Organization

├───bin           # executables run outside the browser
├───cgi-bin       # executables run by the browser via cgi
├───deprecated    # old material destined to be gone one day
└───resources     # visuals and external graphics used by web pages

├───bin

file notes
.bashrc boot shell script, belongs at ~
.xinitrc xwindows start shell script, belongs at ~
forecast.py
quitChrome.sh
shutDown.sh
tidedata.py
tidesGraph.py
tidesGraphic.py
tidesTable.py
updateSystem.sh
updateTides.sh
updateWinds.sh
windGraph.py

├───cgi-bin

file notes
moonData.py creates the lunes for a given day
moonFetch.py
moonPhase.py
networkStatus.sh
sunFetch.py

└───resources

file notes
_forecastGrid.html template for forcast grid
_tideTable.html template for tide table
HHYC_Flag.png Our site icon
moon.svg default moon
FullMoon.png moon image for lune background
PorchImage.png Image for background in porch reservation table
SailingImage.png Image for background in boat reservation table
TideBackground.png Background for TideImage

└───resources/tmp

The transitory repository for generated images. Many cgi routines don't return graphics they simply generate visual media the app simply updates.

file notes
DetailTides.zip cache for Tides
ExtremTides.zip cache for wider Tides
moon_today.svg Lune for today, generated each day
moon_tomorrow.svg Lune for tomorrow, generated each day
moon_yesterday.svg Lune for yesterday, generated each day
tideGraph.png Tide graph, generated/updated every ~15min
tideGraphic.png Tide graphic, generated/updated every ~15min
windGraph.png Wind graph, generated/updated every ~15min

One thing I want to eliminate is the dependance on crontab for minute to minute operations. Let the cgi-bin method of launching processes do the lifting.

12/30/2022

The refactor to a self contained web-app is complete. css does indeed have a complex set of transistions that the browser manages with little more than some simple coding rules. The three main screens were isolated and switched off (held off screen) until a class change is made that tells the screens to transition onto screen. With a little practice it was possible to also have the exiting screen drop away under the incoming screen to appear as if it were dropped. Quite a bit of code was cleaned up and many routines organized into dispatchers. The final push would be to port the js to ts.

Some useful references: Easing gives an overview of some of the transitions available in css.

Animation The effective and useful, because it has examples and trials, reference.

Generic css overview More example driven css generics

tools building like above but with interactive tool building.

cubic bezier visualization

End of Execution Rocks and Western LI Sound buoy public data.

NIRACOOS UCONNs weather buoys are no longer being picked up by the NWS. This means that the historic wind data I was picking up is no longer available for plotting. There are, however, new pieces of data but they are presented as images already formed.

  • Wind information: Execution Rocks Weather Panel

  • Wave information: EXRX Wave Panel There are other products promised but as of this exploration, 2/10/2025, they are not ready yet. exec rocks: 40 53.00 N 73 43.70

Tested image extraction. Replace wind graph with slugs from graphical products?

1/24/2026

I have developed a python tool that grabs the images from the LIRACOOS weather buoys and OCRs the data fairly reliably. The idea is to have the weather kiosk grab the data on a regular basis (~10 min or so) and populate a circular buffer (so it doesn't eat up all the space) that can be used to present date from Exec Rocks. I can, as a backup, grab from the other proximate buoys.

Before implementing I need to check the status of our current setup.

  1. Update the python libraries. ✅
  2. Install tesseract-ocr, pytesseract, etc. ✅
  3. Update the git access code.
  4. See if our little RaspPi can handle the graphical processeing.

Item 1 through 4 tested and completed. The OCR capture is driven by a crontab asynchronous to the web interface and synchronous with the reported screen updates. As of 1/27/26 it is undergoing burn in to make sure the ring buffer is working. New graphic tools need to be developed to take advantage of the new dataset.

1/31/2026

Synchornized the windGraph.py tool between RaspPi and this repo.

2/10/2026

Clean up the operation in preparation for the new wind/wave graphic. Like many projects, this system grew organically and some features which were external were incorporated into main engine. Take a step back to review the overall goal of the kiosk.

The kiosk stands just inside the main door of the club house. It is designed to take some pressure off the staff from 'policing' the day boats as it displays the reservations for a given day. As with any sailing activity weather plays a vital role in how or even whether you are going to sail this day. Another screen was added to present a customized presentation of local conditions as reported by the NWS along with tide data and eventually radar. This turned out to be the most closely watched component of the system.

Later on the tail of the Covid era we implemented a reservation system for the porch tables as well to control the traffic on busy days like Friday, Saturday and Sunday.

In summary:

  1. Weather Information
  2. Boat Reservations
  3. Porch Reservations

These screens are cycled through on 20s intervals (based on airport timing) within a single document view.

When I started I had three different screens so I had an external program rotating through different pages. I had an outside script pressing a 'virtual' button to tab through the screens. Very kludgy but demonstrated proof of principal. As my understanding of web page design and the new superpowers of current browsers improved it became clear that what I needed to to do was have 1 page with a program that swapped between regions. The many of the screens were updated using iframes of local pages that could be edited independently. The idea was that it could be modified more easily.

Using crontab (this is a raspb-$\pi$), graphics are updated asynchronously (after all the data is only changed every 15 minutes.)

There are a handful of graphics that are updated on demand (the tide clock is just that)

Here is a tree of the current operational directory with commentary and annotation:

.
├── bin               - holds operational scripts for this system, run by crontab
│   ├── captureBuoyData.py
│   ├── collectWeatherData.sh
│   ├── quitChrome.sh
│   ├── restartMachine.sh
│   ├── rotateTabs.sh
│   ├── shutDown.sh
│   ├── testRotate.sh
│   └── updateSystem.sh
├── cgi-bin          - these are commands run from the browser as requests to generate illustrations
│   ├── forecast.py
│   ├── moonData.py
│   ├── moonFetch.py
│   ├── moonPhase.py
│   ├── networkStatus.py
│   ├── __pycache__
│   │   └── tidedata.cpython-39.pyc
│   ├── tidedata.py
│   ├── tidesCartoon.py
│   ├── tidesGraph.py
│   ├── tidesTable.py
│   ├── usNavObsData.py
│   └── windGraph.py
├── coreScript.js   - the 'main' js code for the kiosk central page
├── jsconfig.json
├── package.json
├── package-lock.json
├── RaspberryPiKiosk.md   - Notes on the immediate external processes that need to be set up and run.
├── README.md
├── resources     - repository for generated data
│   ├── favicon.ico
│   ├── _forecastGrid.html
│   ├── FullMoon.png
│   ├── HHYC_Flag.png
│   ├── moon.svg
│   ├── PorchImage.png
│   ├── SailingImage.png
│   ├── TideBackground.png
│   ├── TideBackground.svg
│   ├── _tideTable.html
│   ├── tmp      - temporary cached data and images
│   │   ├── DetailTides.zip
│   │   ├── ExtremTides.zip
│   │   ├── forecastGrid.html
│   │   ├── moon_today.svg
│   │   ├── moon_tomorrow.svg
│   │   ├── moon_yesterday.svg
│   │   ├── OCRDataCapture.log
│   │   ├── tideCartoon.png
│   │   ├── tideGraph.png
│   │   ├── tideTable.html
│   │   ├── wave_panel.png
│   │   ├── windGraph.png
│   │   └── wind_panel.png
│   ├── wave_data.csv  - wave data pulled from OCR
│   └── wind_data.csv  - wind data pulled from OCR
├── settings.json
├── WeatherKiosk.css
├── WeatherKiosk.html
├── WeatherKiosk.log.*   - As it sez, log files
├── weatherkioskTREE.txt - This file
└── WeatherTests.ipynb   - jn test bed for graphics

2/19/2026

Updated the current kiosk. Interestingly the Pi is more robust than I could have supposed. The OCR data fetch is more robust than I could have hoped. Running the kiosk 24/7 has caused the chromium browser to stall from time to time but the cron jobs keep on running. The machine keeps on cooking. Just restart and the whole thing picks up right where it would have been if it hadn't crashed. It almost makes me want to launch a watchdog timer from cron. I completed the LTZ correction problem and nearly broke all the robust data decoding for the OCR capture. Decimal points sometimes ~20/290 captures. I was trying to set limits but the better way was to notice that I know in advance what the precision of the value I am trying to parse. I check the returned string to see if it matches the pattern. If it doesn't (always due to a missing period) I know how to arithmetically fix the problem.

I also started to canonize the paths to files which vexed me given that I am running in 3 (4 with the debugger) environments. from pathlib import Path to the rescue.

About

Standalone Weather Kiosk for Marine Station

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors