Skip to main content

Christmas radvent calendar




WARNING: We must be fast approaching the giggly, festive season. Or it's simply the effects of the "grape juice for grown-ups" kicking in. Whichever the reason, there is an uncustomary sprinkling of sentimentality and holiday cheer seasoning this post.

(ANOTHER) WARNING: The names Santa Claus / Father Christmas / Saint Nicholas / Abominable Snowman are all being used interchangeably throughout this post to describe the identical, elusive character that symbolises the spirit of Christmas.  We apologise if you are offended by this carelessness generalisation. We're not that sorry, really.


Let's reflect (seriously) for a moment.  If in the often chaotic run up to Christmas, finding time to dabble in our next project (or the spare change required to fund it) appear high up in our list of festive worries, we can consider ourselves extremely fortunate.  What a luxury it is to be encountering completely trivial dilemmas, such as whether we can refill our stockings with a few more Raspberry Pi's or ESP8266's, to help fill the occasional breaks in the Christmas schedule.  Like those rare lulls between feasts of gassy vegetables and films featuring Macaulay Culkin, or when the kids have been ushered up to bed, more likely, Bruce Willis.

Therefore, we need to spare a thought for those folks from all spectrum of society who have challenges less self-indulgent than our own.  For whom Christmas is nothing but a stark reminder of those who have, and have not.  And, similarly, let's not forget those who dedicate themselves to tirelessly help those in need.

So we've made a commitment this year.  We will donate a small some each day in December to charities that help the most vulnerable, instead of placing the usual monthly order of electronic componentry for our ongoing projects.  But to put some fun into this activity, we'll build an advent calendar that not only counts up the days to Christmas Day with starry LEDs, but makes each LED represent a (semi-)randomly selected charity that we will donate to each day.

In the words of those that live on one sunny coast of the US, wouldn't that just be soooo "rad"?  Hence the radvent calendar.  Get it?

Quickly moving on...

Of course, true to our usual form, we won't pass on any opportunities to infuse some silliness into the final product.  But you are likely to have known that already.

House Rule

Despite common misconceptions, the official countdown to Christmas starts on the 1st of December.  Not on the 1st of October, when certain streaming services start recommending playlists that include Wham and Mariah Carey to signal impending danger to our bank balance.  Not even the very nanosecond Halloween finishes, when supermarket aisles begin to be commandeered for peddling Christmas paraphernalia.  1st of December.  Remember it.


Of course, this simple "1st of December" rule also helps in the forgotten art of advent calendar making.  We don't need to factor in leap years. Millennium bugs. Nor do we need to create 146 flaps / windows / bin lids to open.  Let's all stick to the 25.

Merry Making!

As this entire idea revolves around charities, we need a reliable information source that provides us with a list of bona fide charities that we can donate to easily.  Furthermore, we would like to effortlessly pre-select those that focus on helping the homeless.  In other words, no, we will not be donating to the "Save the Abominable Snowmen of the Sahara" campaign, however well-intentioned the non-existent conservation programme.

JustGiving has an API that allows us to interact with its services in a limited capacity.  While it doesn't appear to allow us to make donations programmatically, or search for - or list - charities using a filter, once we know the charity's ID, we can use the API to obtain useful information about the organisation, and keep this data up to date.  And better still, we can obtain the URL that allows us to donate easily through their platform.

There are other providers of this data (in the UK, for example, the Charity Commission appears to have an API also).  And if we would like to donate to the charity directly, we could do this outside of JustGiving.  But for the purposes of simplifying the donation process, and making use of their rather well documented API, we have concluded that this is likely to be the quickest route open to us to make this project a success in the time left until the countdown begins.

Like almost all things on the Winter Wilderness Web these days, the journey to use the JustGiving API starts with an all too familiar sign up page.


In case you were wondering, we do not write in a language that has an alphabet consisting of differently sized, black rectangles.  That would be bizarre.

Once registered, in order to access JustGiving services programmatically, we require an "Application ID", also known as an API key.  This key is unique to us, and must be embedded in our REST API request header to identify ourselves.


Never share your API keys with others, and avoid storing them in your code.  Code tends to get shared around with every Santa and his reindeer - for example using GitHub - rather like a plate of stale mince pies.  It's always preferable to store your keys as environment variables in the operating system the code is running on, or in a separate file stored securely on the filesystem, which are then imported when the program starts up.  Word!


Once we signed up, there appeared to already be a default Application ID available for us to use.  We're not sure what any of the other details here mean - but we like the green box which tells us that the "status" is Live.  We'll interpret this loosely as approval by JustGiving for us to proceed as planned.


As alluded to earlier, we (not so) thoroughly searched through JustGiving API documentation, and could not find an endpoint that allowed us to list all the charities available, either in entirety, or using a search criteria (presumably because the returned result may be huge).  However, if we have a specific charity ID in hand, we appear to be able to access information about it using this endpoint:

http://api.justgiving.com/v1/charity/{charityId}

This, clearly, means we need to hand craft a list of candidate charities that we will be randomly selecting from.  This also gives us an opportunity to refine the list to charities that we would really like to focus on during Christmas.  Sorry the Abominable Snowmen of a North African desert - you won't be rehoused in Lapland just yet.

So how do we get hold of these charity IDs?  We had to invest half an hour doing it, but navigating to each charity's landing page on JustGiving and hovering over links appear to do the trick.  Besides, this was actually a really great way to read about these organisations and what they offer the community.  In many of the cases, we learnt that they were in geographic locations uncomfortably close by.

For example, here is the ID for the homeless charity Shelter.


In case you missed the big, red, bold number - that says:

73

Testing the /charity API endpoint using this charity ID indeed reveals that we're on course for a minor victory.  Here we are, using curl on a Windows machine, pulling down lots of useful information about Shelter.  Notice how we buried our API key in the header of the request.  Or you might not, because we blanked it out.


In fact, there's just too much information here.  We'll only use couple of these attributes to maintain our sanity - but more on that later.

That was an enormous amount of unforgiving text crammed into a small unsuspecting Windows command terminal.  Quite frankly, we've now got a headache (and it's not the "grape juice", honest).  It's time to make it five times worse by visiting a local IKEA store peak-time, on a weekend, when the entire population decides to pay the Scandinavian retail establishment a visit (mainly for the meatballs in the cafe).

All the things that we do in the name of making...

So the objective here (in addition to making it out of the store alive with a belly full of meatballs) is to find something that could happily house a lot of LEDs, and could be made to resemble a calendar.  Being able to drill holes through the material and cut out sections would also be a bonus.

This little wooden container lid seemed to fit the bill perfectly.  It's got a strangely non-IKEA-esque and highly pronounceable name: "365+".  And it's only £2.00.  Right.  Time to make a quick escape before we inadvertently end up with enough furniture to start a professional nursery.


As we didn't want lots of wires (and a Pi) sticking out of the back of the calendar, we purchased the container as well.  The 2 litre version turned out to be perfect to house absolutely everything we needed for the project (except the power lead, which can be dangled out of a hole at the back).  It's also possible to rest the container horizontally as well - which means it moonlights as a stand.



There ought to be a medical term used to describe people with a compulsive desire to vandalise IKEA purchases the moment they arrive back home (free advice: don't do it while still in the store - that makes you an actual criminal... and you won't ever be allowed back into the only premier Swedish furniture retailer).  We obliterated ours using an arsenal consisting of an electric jigsaw, craft knife and some sandpaper.  The gaping rectangular hole should now allow us to slot in a 3D-printed contraption that houses an OLED screen and push switch.  Because - clearly - that's the way we roll these days.


But hang on a second.  This wooden plank doesn't look that festive yet... almost like a cheap container lid that someone barbarically tortured with a power tool.  Where's all the Winter cheer and merriment we promised you?

It's time to bring in the reinforcements... helpers that are yet to be tarnished by grown-up cynicism.  Let's convince the little one(s) to participate in a little impromptu "arts and craft" hour late on a Sunday night (when they really ought to be doing their homework instead... or sleeping).

Here's the obligatory little Santa (...or is it Father Christmas?) that our eldest daughter came up with.  She *may have* been inspired by a picture discovered on Google.  But for purposes of copyright... this is clearly an artistic interpretation of said picture found on Google.


Bit of late-night, parental geekery ensued.  We scanned the drawing, transformed it into a SVG file, then a MP3 file, a MP4 file, then a MI5 file, and only after some slapdash conversion work involving 768 other different file formats, it was finally imported into SketchUp.  There, we pushed and pulled it into a little compartment that can be 3D-printed and slotted into the aforementioned "hi-tech, on-demand latching enabler".  More commonly known as h-o-l-e™.


As with all our 3D prints to date, we exported our creation as a STL file, and printed them on our FlashForge Finder 3D printer using FlashPrint.  A little product placement never hurt anyone.  Unless the product is a cannonball, and it's placed on your head.


Incidentally, the square gap will be used by the 128×128 OLED screen, the circular hole - sorry, H.O.L.E (you don't want the advanced version of this) - for the rather prominent push switch.

After we sanded and primed the prints, our little helper added some much needed colour using acrylic paints.  Then, the creations were coated in absurdly glossy varnish.  Is it Christmas enough, now?  Huh?  Is it?

After a stint of superglue-ing, and Polyfilla-ing to close the gap left carelessly around the edges, this "Santa" centrepiece was permanently affixed to this particular IKEA memorabilia.  There is simply no going back now.  If we were still contemplating using this as a lid for storing a small surplus of Brussels sprouts cooked on Christmas Day - tough! - we're just going to have to leave them on the counter to stink up the whole kitchen.


We can stand down our little helpers now.  They can go to bed, or watch My Little Pony, or whatever it is that they do these days.

With serenity restored, and kids asleep, it's time to start bashing the keyboard like a possessed seal.  And it's probably about time to think about what to display on our titchy OLED screen.  We've got a few ideas, and it will revolve around cycling through three "screens":

1. Ghost of Christmas Past

On the first screen, we will show the following information, useful to rigorously reinforce the Christmas sprit in the household:

  • Today's date - Why not?  This stuff is always quite useful, Christmas or not.
  • Number of days left to Christmas - An essential data point, to get the kids extremely excited every morning.  For added hilarity, make it show 1, only for it to revert back to 100 the next day.
  • Temperature in Lapland - Erm... because it's useful to know this, clearly.  For the welfare of Father Christmas and his zero hour contractor workforce, and all that.  Moreover, wannabe data scientists amongst the children might further look at the correlation between temperature, and the amount of presents distributed.

The first two bits of information can easily be established in Python using datetime.

But what about current temperature in Lapland?

We learnt two VERY important facts about the home of Father Christmas:- a) Lapland actually exists, and b) its capital is called Rovaniemi.  We won't be getting into the technicalities of whether a temperature reading from Rovaniemi strictly constitutes a reading from Lapland, or whether Lapland is the home of Father Christmas, or whether - in fact - this is just an excuse to populate a spare line in the OLED screen.  Regardless, we intend to obtain temperature data from our new, favourite city (that we didn't know existed).  And yes, there's an API for that.

OpenWeatherMap provides a free API that allows us to obtain global weather data. And it's extremely easy to use.  There are many other providers of weather data... but we're really not that bothered about the accuracy of the novelty temperature reading, and our requirement here is extremely limited.

Not unlike the JustGiving API, we'll need to sign up to the OpenWeatherMap service. 


And once registered, there is an API key waiting for us to use.  Christmas has clearly come early.


This API key needs to be injected into the URL (not in the header like with JustGiving) when the REST API request is made.

The current weather API documentation tells us exactly how this needs to be done. Note the use of "metric" at the very end.  Here, in the land of this Queen and that Queen, only Celsius will do.

https://api.openweathermap.org/data/2.5/weather?q={cityname}&APPID={apikey}&units=metric

As before, we'll first try it out using curl, just to see it in action.

Blimey, it really is quite cold in Lapland already...  Not sure if any of this information is actually relevant in a calendar.  But we promised ourselves the temperature in Lapland.  And we aim to deliver.


Once we're back in Python, we'll use the venerable Requests module to perform all of this API-ing, and access returned results as a dictionary data object.


So that's the data in our main screen sorted (while our low-light photography skills remain hilariously subpar).  What about the two others?

2. Ghost of Christmas Present

Well, on the second screen, we'll simply display the "randomly selected" charity of the day, and its impact statement (actually called impactStatementWhy in the REST result).  This gives us a short and useful description of the organisation.  Like: "Taking the abominable out of snowmen" for the one about their continued survival in the wild.

We didn't invest too much time in making this display look nice.  In fact, depending on the length of the charity's name, the screen can look quite congested and hard to decipher.  This is more a consequence of the small size of the screen... although with some time invested, it would certainly be possible to work out more elegant ways to split text (by words) across lines, or even, screens.


Next!

3. Ghost of Christmas Yet-to-Come (really, why not the "Future"?)

So how do we make it easy for ourselves to donate?

In an ideal world, we could link this advent calendar up to a system that allows us to make the payment automatically.  Or maybe it could even accept payments (although the chances of this little hack meeting the threshold of being accredited to process payments is... well, zero)?  But that would take the fun out of the experience slightly, anyway.  Besides, there is something to be said about us taking the conscious step to make the donation based on the recommendation, rather than it happening automatically while we carry on with our busy lives.  Therefore, we'll provide a facility to direct ourselves to the JustGiving landing page of the charity from where we can make the donation.

...And how do we intend to make that material connection, between our dozy, slow-to-fire-up, decaffeinated morning brain, and the charity's JustGiving page?  One way these things are realised in the real world is by using a QR code.  We simply point our smartphone at the QR code, and it'll take us directly to the page.  Quick Response, as intended.

For example, during testing, here it is directing us to the Simon Community Northern Ireland page, using the QR reader on our smartphone.  The value used to generate the QR code is the "profilePageUrl" key returned by the charity API.


And that's why we've decided that our third screen will be showing the QR code of the charity's JustGiving profile page.  And for this, we use the conveniently named qrcode module.

sudo pip3 install qrcode[pil]


Once installed, we can simply do something like this in our Python code to generate the graphic.

import qrcode
qr = qrcode.QRCode(
    version=None,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=10,
    border=4,
)
qr.add_data(<profilePageUrl of charity>)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img = img.resize((<OLED width>, <OLED height>))

After this, we end up with a pillow object, that we can display on the OLED screen using a driver.  Note that we have a final step to re-size the object to fit onto our 128×128 OLED screen.


Speaking of which, what do we do to show stuff on our screen?

Well, we described how to use a 128×32 SSD1306-based OLED screen when we went hiking in the Brecon Beacons.  We are using here, a slightly bigger 128×128 OLED screen, driven using the SSD1327 chip.  Guess what.  The wonderful luma.oled driver we can't stop using supports the SSR1327 as well... so the entire process is as easy as:

from luma.core.interface.serial import i2c
from luma.core.render import canvas
from luma.oled.device import ssd1327
serial = i2c(port=1, address=<I2C address of OLED screen>)
device = ssd1327(serial, 128, 128, mode="1")
device.display(img)

Where img could be the QR code we generated previously.  We print text to the display, in the same way we did before for our GPS tracker.

This concludes our segment on the stuff that we're planning to display on screen (and a rather odd association with A Christmas Carol accompanying the equally ghostly night-time photography).  No, we really do not know how to succeed in low light photography.

Now, we need to address one gigantic elf in the room.  Where is the razzmatazz we promised the audience?  The Christmas sparkle.  Which bit of this creation actually resembles an advent calendar in the traditional sense?  How many reindeer does Santa have?  Will Brexit ever happen?  So many questions. So little time for answers.

After much contemplation, we reached the inevitable conclusion.  We needed to vandalise this IKEA purchase some more by drilling 25 holes in it.  And by inserting LEDs of different colours into those holes.

And before our brain said "perhaps this is not such a good idea", it was too late.  25 holes were made.  We had even 3D-printed some LED holders that could be glued onto the back of the wood.


With the view of controlling all these LEDs independently, we initially entertained the possibility of using a GPIO I/O expander with a transistor-based voltage amplifier on each output to switch the LEDs on and off individually, without blowing up the Pi.

But then again, why not just use PWM?  That way, we could also control their brightness.

This called for the use of a PWM controller (or two).  Specifically, the Adafruit 16-channel 12-bit PWM controller.  Two of these will allow us to cover the 25 channels required.  And they can be daisy-chained together, along with the OLED screen using I2C, reducing the total amount of cabling required.


These PCA9685-based PWM controllers from Adafruit have lots going for them when it comes to controlling LEDs.  They are quite small, and have 220Ω resistors on the output of all the channels (meaning we shouldn't be able to annihilate our LEDs - or the Pi - with a sizeable current draw).  In fact, using a 5V input, and considering the two different types of forward voltages our coloured LEDs will have, we should be drawing 7mA for our green/blue/white LEDs, and 14mA for our red/yellow LEDs.  All safely under the "general" 20mA figure.

The positive terminal of the LEDs simply get cabled to the PWM pin of its channel, with the negative terminal connected to the ground pin.  When everything is cabled up, we end up with a total rat's nest of cables that quite frankly should belong in a modern art museum as an exhibit.  Note that our second controller has had an I2C address selection pins shorted, to give it a different address to the first.


For now, we'll progress on the basis that the 5V line is powered by the 5V terminal on the Pi.  We suspect that all of this should be safely handled well under 1A.  If we observe indications that that the current draw is too much, we can always move the power supply to a dedicated unit.

Time for lights, camera and (hopefully) action!

We've used the Adafruit PCA9685 library before, for many of our (suspect) robot projects.  We'll experiment with 60Hz for the PWM frequency - approximately on the verge of what humans can detect - but might need to up this later if we detect some flicker.

import Adafruit_PCA9685
light_controller_1 = Adafruit_PCA9685.PCA9685(<I2C address of PWM controller>)
light_controller_1.set_pwm_freq(60)
light_controller_1.set_pwm(<LED channel>, <tick value start>, <tick value stop>)

We will aim to always have the duty cycle start at 0, with the stop value (out of 4096) effectively controlling the brightness.

For example, very bright LED on channel 0 would be something like this.

light_controller_1.set_pwm(0, 0, 512)

Not so bright LED on channel 0 would be something like this.

light_controller_1.set_pwm(0, 0, 8)

We use threading to ensure code controlling LEDs over time isn't blocking so that they can all be controlled simultaneously, and won't impact the main routine calling APIs and displaying stuff on screen.

And that's about it.  Playing around with this indeed shows that the lights and their brightness can be controlled from Python.  With often satisfying results in the dark.


Just to re-cap, we now have 3 I2C devices daisy-chained together.  They all have different addresses which can be shown using the i2cdetect command.


I2C address...Used for?
0×3dSSD1327 OLED
0×40Adafruit PCA9685 1
0×41Adafruit PCA9685 2

These are all connected back to the Pi on the following pins:

Raspberry PiI2C...Used for?
GPIO 3 (BCM 2)I2C SDA I2C data line
GPIO 5 (BCM 3)I2C SCLI2C clock line
5V5V5V
0V (Ground)0V0V

We've not talked much about the push switch ("push to close" / "normally open") which we've decided to connect one end to the 3.3V pin, other to GPIO pin 15 (BCM 22).  We've used a 330Ω resistor to protect our Pi by limiting current.  We then use the bog standard gpio.add_event_detect() method of the GPIO library to continually monitor the status of pin 15 and use a queue to announce the switch being pushed to all other parts of our program.

Here's the Duplo diagram of the entire circuitry:


Everything is connected.  They should be playing together nicely.  And with a whole bunch of Python code (shared at very end of this post), we should have something that does this:

  • If it hasn't already, the calendar picks a random charity (from list of those available) for the day and chooses a LED for it and lights it up.  This is stored locally in a CSV file (except the LED which is randomised everyday) so that it remembers the charities even after the calendar is powered off (by... erm... some curious little hands).  Previously selected charities are lit up by default at calendar start up.
  • The calendar will indefinitely scroll through the 3 screens - 2nd and 3rd screens will be of today's charity only (which we are reminded to donate to).
  • Each time the push button is pressed, it will toggle through the charities that have been selected to date - reminding us of all the charities that we've learnt about (and donated to).  We had an inordinate amount of fun creating a randomly named "starburst" effect which transitions between the charities' LEDs "dramatically".  Clearly, we need find a different hobby.
  • If the button isn't pressed for a set period, the calendar returns to cycling through the screens of today's charity only, but only after an equally dramatic "sparkle" effect (we must have been watching too much My Little Pony recently).

And that is it.  We've had to test this using a "Christmas Day" set in November (ignoring our very own advice from earlier on about Christmas starting in December), and this is reflected in the photos and videos that we've managed to capture of the calendar in operation.

Here it is, in all its glory (although real testing can't begin until we hit the 1st of December):


With more time and energy, this could be improved in an infinite number of ways, and given lots of other "special effects" to dazzle onlookers. Possibly even the OLED screen itself demands an upgrade to make it more legible.  But we think we've now got the moral of this particular story... and it didn't require Macaulay Culkin or Bruce Willis to educate us.

...That the build-up to Christmas should be about more than just the sum of the weird and wonderful components that we use for our making.  And - perhaps - that should even be the case all year round.  But let's take this opportunity at the end of the year to slow down, unwind, recharge our batteries (in some cases, quite literally).  And appreciate what we have, and spare a thought for those who don't.


LAST WARNING (OF THE YEAR, HOPEFULLY): Our next post is likely to arrive with an unusually large amount of nonsense and silliness (even by our standards), purely to make up for this uncharacteristically sentimental anomaly of a project.  Will next year see the return of Mr. Ted E-U (as demanded by 33% of our readership, mathematically proven using the following formula: 1/3×2, rounded down = 0 person)?  Or will a breakthrough finally be made on the scientifically impossible Rosie hexapod?  Or - more likely in our case - a hybrid teddy bear hexapod that can be featured on a Countryfile episode about extraordinary (and possibly slightly disturbing) wildlife found in the West Country.

Either way, it promises to be another exciting year here at Rosie.

Merry Christmas everyone, and a Happy New Year!

...Now, where was that Wikipedia entry on Abominable Snowmen?


<We're still fine tuning our code... it will be posted later>

The brilliantly festive "Beyond Wonderland" fonts used in the images and video can be found here.

Comments

Popular posts from this blog

Break an egg! You've got to be in it to win it.

What the 'egg? It turns out: parenting is actually quite hard.

Not least because you suddenly find yourself responsible for one, two, or - in our household - three little miniature versions of us that need to be kept well away from the soldering iron. Or the 3D printer. Or that marauding hexapod that you forgot to power off before you left for work in a hurry.  But to compound matters further, you find yourself well and truly ambushed - financially.  You are at all times being pressurised by dark forces beyond your control to make an investment, however dubious the return.

That's right.  Clearly, you will be considered an abject failure as a responsible adult if you don't purchase the latest, trendy parenting gizmo. That feeding bottle sterilising kit clinically proven to kill all known bacteria through the science of nuclear fission. Or that titanium alloy buggy guaranteed not to crumple in the event of a sudden collision with falling Soviet-era space debris.  Evidently,…

Tea minus 30

We're fast approaching Christmas time.  And if robots were to make one simple observation about the human species during the Christmas festivities, it's that they watch a lot of TV.  A LOT.  Often, accompanied by an inappropriate amount of greenhouse gas-producing food.  Stuff you don't normally eat during the remainder of the year - for good reason.

And most so-called shows on TV are boring to robots like Rosie.  After all, why watch a minor subspecies of the human race - celebrities - stumble awkwardly around the dance floor, dressed like a faulty, sparking circuit board?  Such branch of entertainment doesn't require robots to engage any of their proud circuitry.  Their processors remain idle.  Memory under-utilised.

But if robots are to be part of people's homes (and blend in), they need to look at least a little interested in some of this irrational nonsense.  Nobody likes a party pooper.  A killjoy.  And this is where a certain subgenre of TV entertainment co…

Raspberry bye, hello

Let us make this very clear from the onset of this exotic excursion.

This is not a case of Raspberry Bye. Our relationship with our favourite single-board computer hasn't at all soured. In fact, we've become wholly inseparable. There's been many months of undeniable fun that's been had with the venerable computer strangely named after an edible fruit. To the extent that our relationship requires a healthy break. And quite frankly, our Pis require a well earned summer holiday to do whatever it is that robots and computers do during their time off. Crash. Burn. Refuel (with questionable toxins). Not at all unlike their human counterparts. And ultimately, it would be nice if they could return to a brand new, adorable pet waiting for them at home, a likeable little companion that they can just get along with.

Well, we visited a pet shop, but couldn't find anything as small and smart as this adorable pup we stumbled up on while searching the Internet for a new, miniat…