Building a Julia-Powered E-Ink Dashboard: A Dev Log

Every great journey starts with a single step. :man_mage:

In the spirit of open source, adventure, and fun, I am making an on-going development log here about building a Julia-powered e-ink dashboard ran on a Raspberry Pi. This was sparked off by the apparent interest folks had for my original post:

As I continue pressing along with this small hobby project, I invite you to tag along as I go on many side-quests, (mis)adventures, and more in this little project! Who knows what we’ll end up making?

~ tcp :deciduous_tree:

9 Likes

Log 01: Undated Sketches

Up until now, I went on a small side-quest to explore weather APIs. A component that I want to make for my dashboard is a weather component to check things like:

  • UV Index :sun:
  • Temperature :thermometer:
  • Humidity :hot_face:
  • Precipitation & Amount :cloud_with_rain:
  • Wind speed & Direction :leaf_fluttering_in_wind:
  • Sunset & Sunrise :waxing_crescent_moon:
  • Air Quality Index :cherry_blossom:

To this end, I found OpenMeteo! I had found GitHub - vnegi10/WeatherReport.jl: A simple weather app for the Julia REPL by @vnegi10 which looked promising but found it’s priority wasn’t to give data from the website but have a nice REPL output of weather. Very cool!

Reading some discussions on issues by Vikas, I found that OpenMeteo had an OpenAPI.yaml spec. Promising! Played with that using OpenAPI.jl but ultimately found that it was extremely limited – they are still in the process of supporting it. So, hit a bit of a wall.

But then! I discovered a strange thing called a flatbuffer spec they use. Turns out flatc can generate a variety of language bindings for an API wrapper package using this spec. And of course, Julia was not supported – until I found an old fork of flatbuffers by @jonalm that purportedly supported Julia. I knew it worked once upon a time thanks to confirmation by @evetion. Buoyed by this promising end to create Julia bindings for OpenMeteo’s flatbuffer spec, I went ahead, cloned it, ran cmake and ran the make pipeline.

It failed.

BUT I WOULD NOT CONCEDE DEFEAT! Using a combination of C++ programming skills I had from years ago and help from ChatGPT, I quickly monkey-patched the fork. And – pow! – I generated Julia bindings for OpenMeteo using the spec!

Now, I am in the process of making a little Julia OpenMeteo package that builds on top of these bindings with direct inspiration from OpenMeteo’s python-requests package.

Do I know what I am doing? Not entirely. Am I motivated? Mostly. Do I want weather data? You betcha.

Stay tuned!

5 Likes

Log 02: July 13th, 2025 - Weather Imagineering

After tinkering further with OpenMeteo, I decided to take a break and determine what data I want exactly. I concluded that basically, this is the information I want from the Weather Forecast API:

Location

  • latitude=42.3751
  • longitude=-71.1056

Daily Variables Requested

  • temperature_2m_max: Daily maximum temperature at 2 meters
  • temperature_2m_min: Daily minimum temperature at 2 meters
  • sunset: Time of sunset
  • sunrise: Time of sunrise
  • wind_speed_10m_max: Maximum wind speed at 10 meters
  • wind_gusts_10m_max: Maximum wind gusts at 10 meters
  • wind_speed_10m_min: Minimum wind speed at 10 meters
  • wind_gusts_10m_min: Minimum wind gusts at 10 meters

Hourly Variables Requested

  • temperature_2m: Temperature at 2 meters
  • uv_index: UV index
  • precipitation_probability: Probability of precipitation
  • precipitation: Amount of precipitation
  • relative_humidity_2m: Relative humidity at 2 meters
  • wind_speed_10m: Wind speed at 10 meters
  • wind_direction_10m: Wind direction at 10 meters

Other Options

  • timezone=America/New_York: Output times in the Eastern Time zone (encoded as %2F)
  • forecast_days=3: Request a 3-day forecast
  • timeformat=unixtime: Timestamps in Unix time format (seconds since epoch)
  • wind_speed_unit=mph: Wind speeds in miles per hour
  • temperature_unit=fahrenheit: Temperatures in Fahrenheit
  • precipitation_unit=inch: Precipitation in inches

Curl Request Example:

curl https://api.open-meteo.com/v1/forecast?latitude=42.3751&longitude=-71.1056&daily=temperature_2m_max,temperature_2m_min,sunset,sunrise,wind_speed_10m_max,wind_gusts_10m_max,wind_speed_10m_min,wind_gusts_10m_min&hourly=temperature_2m,uv_index,precipitation_probability,precipitation,relative_humidity_2m,wind_speed_10m,wind_direction_10m&timezone=America%2FNew_York&forecast_days=3&timeformat=unixtime&wind_speed_unit=mph&temperature_unit=fahrenheit&precipitation_unit=inch

And Air Quality API:

Hourly Variables Requested

  • us_aqi: U.S. Air Quality Index (based on EPA standards)

Other Options

  • forecast_days=3: Request a 3-day forecast

Curl Request Example:

curl https://air-quality-api.open-meteo.com/v1/air-quality?latitude=52.52&longitude=13.41&hourly=us_aqi&forecast_days=3

From there, I began tinkering with how to display this data on my display. I sketched out a small draft of this here:

Basically, I am imagining using Makie.jl and a PolarAxis radial chart to display this information (thanks to @asinghvi17 and @sdanisch for the tips here):

You can make multiple polar axes and use PolarAxis | Makie

In my designing, I found this example to be beautiful and something I could base my work off of:

I also found this other example which was also hugely inspiring:

This will be a task for future TCP.


Finally, I decided to put some thoughts to code and began drafting a Jinkies.jl framework for how to β€œchunk” the regions of an e-ink display. As of now, I have two structs called Display and another called Component. Display holds information about the e-ink display and what components are in the display. A Component holds plotting information about what goes into the display and where.

I am imagining making Component’s more like an interface too so I can make any component that could be rendered to an image using tools like Makie, Luxor, and more.

Currently, I have made a PrettyTables.jl interface to represent an e-ink display’s pixels in a coarser granularity than pixel dimensions. Here’s how things look so far:

julia> Jinkies.set_display_grid!(100, 120)

E-Ink Display Grid
β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”
β”‚     β”‚ 001 β”‚ 002 β”‚ 003 β”‚ 004 β”‚ 005 β”‚ 006 β”‚ 007 β”‚ 008 β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚  1  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚  2  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚  3  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚  4  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚  ⬀  β”‚
β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜

And then for colors, I can do something like this to say where a part of an e-ink display already has a component:

julia> Jinkies._update_display_grid_colors!(
    [[1,1], [1,2],[2,1],[2,2]]
)

I like the display representation so far, but I need to mess around a bit further with the component and display interplay. Also encoding what information should be stored here and there.


And that is where we shall leave the dev log for now. Nice day of exploring, hacking, and imagineering! Onwards!

1 Like

Should that be an output only? Let’s invoke a Cloud Seeding API (Cloud seeding - Wikipedia) from the REPL.

2 Likes

I need to create more tutorials for these, but you may find more meteo functions in GMT.jl

and the not yet documented other than docstrings meteostat()

1 Like

Oh this is great! Not sure if it would be helpful to you, but would you want me to spin up a small repo that has the Julia flatbuffer OpenMeteo bindings now? Could maybe enrich GMT.jl’s OpenMeteo’s functionality even more?

Happy to share here – just on a sidequest for weather data. :sun:

Thanks a lot for the offer but honestly I don’t know the answer right now. You see, I have a strange stubbornness (for the the Julia community habits) that I don’t want, or I’m I’m very reluctant to accept, new dependencies. All the functionality I showed above was achieved without adding any new dependencies to the ones already in GMT.jl (which other than jll and standard libs are very few. Two only).

Could it be that those flatbuffers are the same as GDAL driver FlatGeobuf? If yes, it means that perhaps GDAL could be used to access the OpenMeteo data.

Hmm, the weather docs say that

Plot and/or retrieve weather data obtained from the Open-Meteo API. Please consult the site for further details. You will find that there are many variables available to plot and with not so obvious names. But confess that didn’t explore much the functionality of this function after writting it (which was inspired in WeatherReport.jl)