Photo by Emmanuel Hayford
Just about a fortnight ago, I became a father. I acquired new responsibilities, one of which is to make sure our newborn gets his supplements. In our case, I must give a drop of vitamin D to our son every day for a year. My partner asked me to remind her about this. I could use a reminder on my phone, but I miss reminders – I have all notifications turned off.
The Raspberry Pi is a functional computer with modest specs for portability. The official OS on the Raspberry Pi is Raspberry Pi OS which is based on Debian, a Linux distribution. It comes loaded with free and open-source software. Two of which I used for our project: Ruby and
I just got a Raspberry Pi (RPi) and I thought a fun way to make use of it would be to delegate the reminding to it so that it sends daily SMS messages to my partner to give our little one his Vitamin D drop.
Sending Messages with Ruby and Twilio
The Ruby code to manage messages is nothing special. Twilio has good support for Ruby so it was enough to create a Twilio client that loops through an array of messages. I used the
dotenv gem to hide sensitive information then I created a separate thread within which I wrote a periodic loop to pick a random message from our array of messages.
With the way I wrote the loop, there’ll be some time slippage but it’s not something we care about. A good way to fix this would have been to write the loop in a way that takes into account how much time dispatching a message takes so the looping period doesn’t slip ahead. Before you ask about
cron, I didn’t want to use it. I’ll pick systemd.timer in the future over
cron for a more fine-grained control. There’s also a seemingly good timers gem that could have been useful. I like to keep my dependencies to a minimum – This is what I quickly came up with:
# /home/pi/vitamind.rb require 'bundler/inline' gemfile do gem 'twilio-ruby' gem 'dotenv' end Dotenv.load('.env') class Messenger def initialize @client = Twilio::REST::Client.new(ENV['ACCOUNT_SID'], ENV['AUTH_TOKEN']) end def dispatch(message) @client.messages.create(from: ENV['FROM'], to: ENV['TO'], body: message) end end thread = Thread.new do MESSAGES = [ "1 drop of Vit D for the little one!", "Another reminder message", "More reminder SMS" ].freeze @messenger = Messenger.new loop do @messenger.dispatch(MESSAGES.sample) # Don't care about slippage sleep rand(82_800..93_600) rescue Exception => e # Log e end end end thread.join
This does it for the Ruby side.
Running on ARM means it doesn’t need to consume much to function, at 3.4W, the Raspberry Pi 4 doesn’t consume much compared to the average 100W consumption of a laptop. This is where delegating small tasks to a dedicated Raspberry Pi makes sense.
Running an SMS Service on the Raspberry Pi
To realise the SMS service, I “daemonised” the Ruby script using
systemd. A script as a daemon means it can run quietly in the background while I used the RPi for other tasks. In fact, following the same strategy, I
plan on managing manage a business Twitter account that set me back $15 each month through an external service.
The first thing after the script was to write a unit file
vitamind.service for the service to
/lib/systemd/system with the following content:
[Unit] Description=Vitamin D reminder service for H. [Service] User=pi Group=pi WorkingDirectory=/home/pi Restart=always ExecStart=/usr/bin/ruby vitamind.rb [Install] WantedBy=multi-user.target
The full path for this file is
[...] denote sections. Here I have Unit, Service and Install sections. The Unit section is for metadata and configuring the relationship of the unit with other units. The Service section provides configuration for services and lastly, the Install section, which is optional but in my case is critical for our purpose. The Install section has a
WantedBy= directive that dictates how a unit should be enabled.
When a unit is enabled, the Install section as I have it marks it to start automatically at boot. It meant I could restart the RPi at any time and not worry about whether the service is working or not: It’ll start working automatically. The only thing I need to worry about in my ruby script is to ensure an SMS message is sent only once per day irrespective of how many times I restart the RPi.
From here I enabled the service with
systemctl enable vitamind.service and then brought the unit to start working with
systemctl start vitamind.service.
systemctl status vitamind.service tells us the status of the service as show below:
From this point on I’ve done everything necessary to keep our little SMS reminder running until we decide to stop it or our balance on Twilio runs dry.
I found a permanent base of power supply for our RPi in some corner at home. I only SSH into this from my desk when I need to.
I have a lot more tasks to delegate to this little computer, mostly just to tinker with apps, servers and services. At this point, we get daily reminders. On several occasions, we forgot to give our newborn his Vitamin D drop – The service saved those days.