Building An SMS Reminder With Ruby & Raspberry PiPhoto 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 systemd.

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 /lib/systemd/system/vitamind.service.

The [...] 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:

Mounted RPI

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.

Mounted RPI


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.

    Powered By ConvertKit
    Subscribe to get updates on cool stuff I build and for new posts -- or follow me on Twitter and join me on my journey on rediscovering Ruby, Elixir, JavaScript and the Web.