I migrated all my Raspberry Pi’s to Docker on my M1 this past month. The ssh’ing, updating, and backing up of six Raspberry Pi’s, with the ‘not if but when’ curruption of SD Cards looming, was too much for me. I appreciated the DOTADIW compartmentalization of the Pi’s, but Docker is just so much easier to manage, modify, fix, backup, jump into relevant logs, and most importantly running on my M1 it is more reliable than SD Cards.

Customizing My iPad Magic Keyboard Case w/ the WordPress Logo »

Customizing My iPad Magic Keyboard Case w/ the WordPress Logo

The over priced magic keyboard case is great in nearly every way. It’s a solid protective case, easy to take the iPad off of, always around the iPad for the times I do need to type, great keyboard to dock on.. it’s all the things. Except I find it hard to open. I fumble 9/10 times to open it. Without a cutout groove like the macbook, the hardest part, for me, is knowing which side is up by feel, so I know which side has the weight (as it‘s counter intuitive to and very different than macbooks we’re all used to), for which direction to force.

Pick a side any side *rolls the dice

I know, I know, there’s a giant cut out for the camera to the top iPad side, so that’s the indication thats heavy side, as well the impressions with the Apple logo and bend seams. But that just isn’t enough for me and my brain, for whatever reason.. even after a month of heavy use. Out of frustration, I nearly wrote “iPad” and “keyboard” on the two sides with a white acrylic pen, but opted to just go with a less obvious indicator, a sticker instead.

The stickers I had on hand were from work, WordPress VIP, which is a beautiful graphic that encompasses WordPress well, it’s a great sticker and has meaning close to my heart – however it just wasn’t the right fit, the black was glossy instead of matte, and it didn’t play nice covering the embossed Apple logo:

I thought about ordering developer stickers (you know the style) and putting a bunch on, but just felt weird and inauthentic buying them outright instead of collecting them from conventions and things like that (something, something, COVID). I then thought it’d be cool to use that acryclic pen for just the “W” WordPress logo, but I was fearful as I don’t have steady enough hands for that, even when tracing. So I thought about stencilling, and that’s when I remembered I own a 3d printer, am capable of creating things in CAD and have lots of spray paint!

Gearing up to design the ”W“ logo, I headed to thingiverse.com first, just in case someone could save me the time, and wouldn’t you know it. The logo, in the cutout I wanted, was already made perfectly for a two colour coaster:

I took it into Cura, scaled it to the size I wanted for the 11” case adjusting the width and hight and lowering the depth a bit, and started printing the free design with $0.09 of material:

Beautiful logo. I tell ya, this thing looks great on it’s own.
Good size. I should of printed a nub to help peal it off when paint is on it, but I’ll manage w/ a pair of tweezers.

With the stencil printed, I grabbed a can of spray paint and headed outside. I practised the shot many times before on cardboard to ensure I got the distance and angles right.

Then after carefully finding centre, ensuring I had the right side or orientation, second guessing myself, then finding perfectly the centre again, I hit it with a splash of spray paint, and Voila:

I sprayed way too close and pooled the paint. Gahh, life needs a undo button. However, I think I got it looking alright:

It’s a bit blotchy right now, I may end up roughing it up a bit with sand paper. I imagine this well wear well overtime. In retrospect as I see the image of the stencil on the case, I think I should of taken the time to design the inverse of this for that cleaner logo look instead of the silhouette/blast look – but, I’m happy with it.

I now clearly know which side is up, and customized my stuff with the best logo, that means the most to me: WordPress has been at the centre of my life in the centre of my career for well over a dozen years it’s been putting food on my table for me and my family, I’m proud to have this logo on my gear.

Update:

Much like a regrettable tattoo, I began to dislike the blast/silhouette look on this more and more each day. It was a bit extreme for me and my modest iPad. I wanted just the logo itself, in a more subtle/clean look. So I found lifes undo button and just sanded it off:

Whatever silicon/plastic/magic material this is made off, it held up well to sanding. Looked no worse off once completed.

I then designed a stencil for just the logo, opening up Shapr3d I cut out the logo and built some scaffolding to hold the floating pieces in place.

I’ve shared this stencil here: https://www.thingiverse.com/thing:4720428

To my surprise, it printed well enough, as I hadn’t done overhangs without supports like this before but it printed just fine:

After centering, masking, and painting (lightly this time, to avoid pooling) I finally got the look I was after:

Much better. No more obnoxious ‘blast’. Admittedly, the lines aren’t as clean as I had hoped for, I may of been able to get it a bit cleaner by pushing down on the stencil while spraying, but I think it still looks good and will wear well.

Building a Sleep Training Light with ESP8266, ESPHome, & Home Assistant »

Building a Sleep Training Light with ESP8266, ESPHome, & Home Assistant

My kids, specifically the eldest, loves to wake up before 7. My wife and I, do not like to wake up before 7. My kids can’t read the time yet or understand why every month the sun seems gets up at a different time.

Our solution to help stop this was a sleep training clock: a visual way to differentiate sleeping time with awake time. Online they run about $40 and, in my opinion, are just over the top with features and buttons. A simple scheduled LED was all we were after. So I built one with an esp8266, ESPHome, & HomeAssistant:

Requirements:

Wiring it Together

I’ve just moved to using an iPad as my primary personal computer so I no longer have Fritzing, so my crude drawing in Notes.app will have to do:

Common anaode RGB wiring to an ESP8266, pins D5, D6, D7.

Setting up ESPHome Module

In Home Assistant follow the ESPHome Add Module wizard filling in info about your ESPHome and WIFI networking settings. Editing the file once complete, mine looks like:

esphome:
  name: sleep_training_light
  platform: ESP8266
  board: esp12e

wifi:
  ssid: !secret wifi_name
  password: !secret wifi_pass
  manual_ip:
    static_ip: !secret sleep_training_light_ip
    gateway: !secret ip_gateway
    subnet: !secret ip_subnet

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "sleep-training-light"
    password: !secret fallback_pass

# LED
# @see https://community.home-assistant.io/t/inverse-rgb-led-values/170069/3
# common Anode
light:
  - platform: rgb
    name: "Sleep Training Light"
    red: output_red
    green: output_green
    blue: output_blue  
    
output:
  - platform: esp8266_pwm
    id: output_red
    #D6
    pin: GPIO12
    inverted: true
  - platform: esp8266_pwm
    id: output_green
    #D7
    pin: GPIO13
    inverted: true
  - platform: esp8266_pwm
    id: output_blue
    #D5
    pin: GPIO14
    inverted: true

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:
  password: !secret api_pass

ota:
  password: !secret ota_pass

Add to HA

Home Assistants notification area should alert you to the new ESPHome device. You can add this entity.

Automation

At time of writing, I found the Confugration > Automattions area of Home Assistant a bit buggy, I ended up mostly setting up the automation via the File Editor. Here’s why my /config/automations.yaml looks like:

- id: sleep_training_light_night
  alias: Sleep Training Light -> Night
  description: ''
  trigger:
  - platform: time
    at: '20:00:00'
  action:
  - data:
      brightness: 80
      entity_id:
      - light.sleep_training_light
      rgb_color:
      - 162
      - 116
      - 255
    service: light.turn_on
    entity_id: light.sleep_training_light
  mode: single
- id: sleep_training_light_wakeup
  alias: Sleep Training Light -> Wake Up
  description: ''
  trigger:
  - at: 07:00:00
    platform: time
  action:
  - data:
      brightness: 0
      entity_id:
      - light.sleep_training_light
      rgb_color:
      - 0
      - 0
      - 0
    entity_id: light.sleep_training_light
    service: light.turn_on
  mode: single

This turns on the light at 8:00pm, and turns off the light at 7:00am.

Case

I designed a custom case (will update this post with a link to the thingiverse.com design shortly) for another project (IR Blaster) and reused it for this one, however I printed with clear filament and kept the RGB LED inside, giving the box a nice glow:

Create Custom IR remote w/ Home Assistant & ESPHome »

Create Custom IR remote w/ Home Assistant & ESPHome

I had several nagging reasons to create a single universal remote that was controllable from an app on our phones:

  • I was looking for a cool Home Assistant project, plus a project to use up some of the extra ESP8266’s I had
  • We had way too many IR remotes in our living room
  • We only ever pressed one or two button per remotes
  • Setting up for the evening using said remotes, was a juggle
  • Remotes were always stashed high and far from kids, or lost in couch cushions
  • Related to above, many of the batteries in the remotes are dangerous to small kids if somehow broken/opened
  • We always had our phones on us, but remotes were always out of reach or forgotten, always after getting comfy

Here’s what I did to build a custom universal remote that replaced all my IR remotes including my AppleTV touch remote:

Requirements

  1. HomeAssistant setup & running (mines on a rpi3b)
  2. HomeAssistant mobile App
  3. ESP8266 (or ESP32) w/ micro usb cable
  4. IR LED and IR Receiver
  5. NPN transistor (BC337)
  6. Jumper cables, or wire + soldering stuff
  7. Heat shrink
  8. Optionally for installation: long micro-usb to usb A cable, 3d printed case, Velcro to stick case to wall

Optional: Setup Apple TV to use IR

Not totally sure what interface the Apple Remote uses, but if looking to Replace Apple TV: you can map the Apple TV to listen to some arbitrary button presses from any unused remote following this doc.

Build an IR Receiver to Capture Codes

First step is to capture all the IR codes. I set up a receiver as follows:

The nice part about this layout for the IR receiver I had is I was able to just slip it into a breadboard, no wiring:

In the ESPHome section of Home Assistant:

  1. Click the big “+” add button in bottom
  2. Fill out the wizard
  3. Once created, edit the module and append to the bottom of the config:
remote_receiver:
  #D5
  pin: 
    number: GPIO14
    inverted: True
    mode: INPUT_PULLUP
  dump: raw
  idle: 25ms

For reference, mine in full looks like:

esphome:
  name: ir_receiver
  platform: ESP8266
  board: nodemcuv2
wifi:
  ssid: !secret wifi_name
  password: !secret wifi_pass
  ap:
    ssid: "Ir Receiver Fallback Hotspot"
    password: !secret irfallback
captive_portal:
logger:
api:
  password: !secret ota_pass
ota:
  password: !secret ota_pass

remote_receiver:
  #D5
  pin: 
    number: GPIO14
    inverted: True
    mode: INPUT_PULLUP
  dump: raw
  idle: 25ms

Then validate, and upload through the USB port if this is the first chunk of code being sent, after that, no need to be plugged in and Over The Air works fine. Home Assistants Notification panel should alert of a new device detected, and you can add in the device and the entity.

Once uploaded, the logs should continue to show in the modal window. Try pointing an IR remote at it and smashing some buttons. If it yells a bunch of crazy number strings at you: it’s working!

There’s some important notes while looking to record a single button:

  • if the “Received Raw” starts with a negative number, you pin may not be inverted and may need to be. IR signals are a positive number (length of high value), followed by a negative number (length of low value).
  • A single press resulted for me in 3 lines. I needed to manually merge the first two and ignore the third. Why? I have no idea. For example, here’s the power button for my TV:
[20:13:30][D][remote.raw:028]: Received Raw: 8992, -4452, 619, -541, 598, -1639, 621, -1663, 598, -540, 598, -540, 597, -541, 598, -541, 597, -1641, 620, -1640, 620, -541, 596, -1640, 621, -541, 597, -541, 597, -541, 598, -540, 598, -541, 597, -1639, 621, -1640, 620, -1640, 620, 
[20:13:30][D][remote.raw:041]:   -1641, 620, -541, 597, -541, 598, -540, 598, -540, 598, -540, 598, -540, 598, -541, 597, -541, 597, -1640, 620, -1640, 621, -1641, 619, -1640, 621
[20:13:30][D][remote.raw:041]: Received Raw: 8993, -2205, 621

I combine the first and second lines, and remove the last line. The need to remove the third comes from line 2 ending in a positive, and line 3 starting in a positive, IR patterns must be positive negative. I don’t know why that third line is there, but removing it everything still worked in the end. From the above, my IR signal for the TV power button ended up being:

8992, -4452, 619, -541, 598, -1639, 621, -1663, 598, -540, 598, -540, 597, -541, 598, -541, 597, -1641, 620, -1640, 620, -541, 596, -1640, 621, -541, 597, -541, 597, -541, 598, -540, 598, -541, 597, -1639, 621, -1640, 620, -1640, 620, -1641, 620, -541, 597, -541, 598, -540, 598, -540, 598, -540, 598, -540, 598, -541, 597, -541, 597, -1640, 620, -1640, 621, -1641, 619, -1640, 621

Creating the IR Blaster

With the codes captured, next step is to build a transmitter. I think it’s best to use a dedicated board for sending, instead of adding the blaster to the receiver. I originally tried to setup just one board that would both receive and sent the codes, but found the boards lacked the brains to push and get at the same time successfully, which made debugging nearly impossible. I found it far easier to dedicate one device to just receiving and another to sending.

I read that ESP8266’s weren’t that great at sending the blasts and ESP32’s were a better choice due to some improved chips onboard, however I had success with ESP8266.

I wired the blaster like so:

The just like before, created a new ESPHome entry with the following appended to the config, adding the in the buttons and their codes:

remote_transmitter:
  # D2
  pin: 
    number: GPIO4
  carrier_duty_percent: 50%
  
switch:
  - platform: template
    id: tv_power_btn
    name: TV Power Button
    turn_on_action:
      - remote_transmitter.transmit_raw:
          carrier_frequency: 38kHz
          code: [8995, -4449, 623......-1638, 622, -1639, 622]
        
  - platform: template
    name: TV Up Button
    id: tv_up_button
    turn_on_action:
      - remote_transmitter.transmit_raw:
          carrier_frequency: 38kHz
          code: [8995, -4447, 625......-1637, 623, -1637, 624]
          
  - platform: template
    name: TV Left Button
    id: tv_left_button
    turn_on_action:
      - remote_transmitter.transmit_raw:
          carrier_frequency: 38kHz
          code: [8937, -4506, 566......-1671, 590, -1693, 567]

...ect

LoveLace Remote Interface

I saw some articles about using a tiles plugin for remote, but I reckoned I could get away using Button cards in a Vertical and Horizontal cards to create a table for button cards. To do this, I:

  1. Created a vertical group for the remote
  2. Created a horizontal group for a row of buttons
  3. Created more adjacent horizontal groups for the following rows
  4. I created the buttons by selecting a Button Card
    1. selecting the entity
    2. Getting a cool icon from materialdesignicons.com
    3. Setting “show name” to off
    4. Setting Icon Height to 50

Here’s a peak at my setup in it’s .yaml form:

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - type: button
        tap_action:
          action: toggle
        entity: switch.insignia_tv_power_button
        name: TV
        icon: 'mdi:power-standby'
        icon_height: 50px
        show_name: false
      - type: button
        tap_action:
          action: toggle
        entity: switch.tv_nothing_button
        show_name: false
        show_icon: false
      - type: button
        ...
  - type: horizontal-stack
    cards:
      - type: button
      ...

Which I laid out to look like:

An important one here is the tv_nothing_button , it is what is says, it’s a button that just like all the others is registered on the esphome setup, however, it sends a invalid value, triggering nothing, but allows the UI to house proper spacing and a consistent look.

Update: looks like the grid card was just released in 0.118 to make this even easier!

Installation Placement

All our devices and lights to control with IR are on a single wall, so mounting the on the opposite wall, higher up towards the roof above our curtains gave the blaster direct line of sight to all devices, and kept it out of the way.

Before mounting I tested all functionality, walking around with the ESP8266 connected to a battery pack to find just the right spot, and quicker debugging.

Case

Most of the designs for esp8266 cases on thingiverse.com were too small or oriented the wrong way for this single LED wall mount use. So I designed a custom case with shapr3d.com. I designed a case for both small and large styles of ESP8266 as I had both, and also did it in a “dead bug” style with lots of room for the bulky dupont connections.

The case can be downloaded here: https://www.thingiverse.com/thing:4739748

Design in shapr3d

Everything Together

Considering for each button press, the flow data is Home Assistant App on Phone > Wifi > Home Assistant > Wifi > ESP8266 blaster > Target Device, it actually works really fast. No noticeable latency or delays connecting. Additionally, I just used the normal HA ESPHome API, not MQTT. Every time I open the app, the module is already connected and ready to press a button (no load or unavailability).

Our families use case for the buttons we needed, is simple and straight forward, so this solution of just firing up the Home Assistant App on our phones was the perfect fit.

Automation & Shortcuts

With all the button entities added into Home Assistant, scripts and automations can be made with ease. For example, I created a “boom button” script that toggles all the power buttons.

Additionally, the Shortcuts app on iOS with the Home Assistant app allows you to turn entities on and off, or run scripts. The title of these shortcuts are pre-wired into Siri, so “Turn TV On” voice commands can be wired up in just a few minutes. More about this here.

I did not store the state (“on/off”) of a button in Home Assistant. Though the button entity can, doing so raised risk of state being incorrect if any of the devices are toggled manually or via the real remotes. I opted it was best to treat the buttons just like an IR remote that are unaware of a device being on or off. This limited automation (eg: turn on if not on), however I’m okay with that.

ESP8266 NodeMCU LED Busy Server »

ESP8266 NodeMCU LED Busy Server

I built a Slack status light because it looked awesome and simple, and at that time I knew nothing about DIY electronics. As I learned more, I outlined how to build a cheaper one on a Raspberry Pi. Then my friend then informed me that you can do it even cheaper with an ESP32 NodeMCU board. He was right. Here’s how I built a IoT busy light for around $5.

For this example, I opted for the older ESP8266 ESP12-E NodeMCU board, it is only $2.65 + 1.73 shipping and has everything needed. Pair that with a cheap RGB shared cathode LED $0.50-$2.00, and a few cents for resistors, for a grand total of around $5* in parts.

I won’t go into the details on the boards, but if unfamiliar it, the tl;dr is you write some C/Lua code using the Ardunio IDE, include WIFI for ESP8266 and Web Server libraries (here’s hello world example code and a great setup video) and add some handlers to toggle the GPIO pins on certain requests, then flash that code onto the board over USB. If never used before, I assure you it’s not as hard or complicated as it may sound or appear. When the board is plugged in, it fires up, the code executes, connecting to WIFI, and this tiny board becomes a server in your local network.

Here’s the wiring I did:

& here’s the code I flashed onto it (note you will need to add your WIFI credentials):

/* ESP8266 NodeMCU RGB LED Busy Server
 *  
 * Set an RGB LED to current status via web request 
 * 
 * 1. Flash this file to a ESP-12E
 * 2. Open Serial to get the IP address of board
 * 3. Optionally set a dedicated DHCP reservation at the router level to ensure same IP address is used
 * 4. Connect common anode or common cathode RGB LED (see pins below)
 * 5. Make `GET` request to `http://<IP>/<status>`
 *
 * Statuses:
 *
 * - 🔴 `/busy`
 * - 🟢 `/available`
 * - 🟡 `/away`
 * - ⚫️ `/offline`
 * 
 * @see https://davidsword.ca/esp8266-busy-server/
 */
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

// @see https://github.com/BretStateham/RGBLED
// must install this library in Arduino IDE
#include <RGBLED.h>

uint8_t red_led   = 16; // D0
uint8_t green_led = 5;  // D1
uint8_t blue_led  = 4;  // D2

ESP8266WebServer server;
RGBLED rgbLed( red_led, green_led, blue_led, COMMON_CATHODE ); // or `COMMON_ANODE` if using common anode RGB LED instead

char* ssid        = ""; // <<< enter your WIFI network name
char* password    = ""; // <<< enter your WIFI password

void setup()
{
  Serial.begin(115200);
  WiFi.begin(ssid,password);
  while(WiFi.status()!=WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }
  Serial.println("");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  
  digitalWrite(BUILTIN_LED, LOW);
  rgbLed.writeRGB(255,255,255);

  server.on("/",[](){server.send(200,"text/plain","see readme");});
  // @TODO can output current state on /status requests
    // server.on("/status",[](){server.send(200,"text/plain", "(r,g,b)=(" + String(rgbLed.redValue) + "," + String(rgbLed.greenValue) + "," + String(rgbLed.blueValue) + ")" });
  server.on("/busy",setBusy);
  server.on("/available",setAvailable);
  server.on("/away",setAway);
  server.on("/offline",setOffline);
  server.on("/off",setOffline);
  server.begin();
}

void loop()
{
  server.handleClient();
}

void setBusy()
{
  rgbLed.writeRGB(50,0,0); // red. not using the full `255` in an attempt to reduce brightness.
  server.send(204,"");
}
void setAvailable()
{
  rgbLed.writeRGB(0,25,0); // green
  server.send(204,"");
}
void setAway()
{
  rgbLed.writeRGB(100,25,0); // yellow. mix wasn't 50/50 red/green as expected. this might be different for each make of LED.
  server.send(204,"");
}
void setOffline()
{
  rgbLed.turnOff();
  server.send(204,"");
}

From there, I added a simple CURL request into my app that’s currently handling the updating of my other busy light based on a polling of Slack status.

I placed this downstairs in my kitchen as a sort of satellite Slack Busy Light, so my family could check in on my status while not in our upstairs hallway:

If you have a common anode RGB LED, be aware the mixing of colours (on startup and when ‘away’) probably won’t work out of the box. Mixing colours on a common anode RGB requires some sort of pulsing frequency – can probably Google for more info on this or a library. Also the script will need to be edited, changing LOWs to HIGHs and OUTPUTs to INPUTs, and vice versa. I’ve updated the script to use a library to get the mixed colours more accurate for both common anode and common cathode.

* despite this low cost, I personally didn’t want to wait for shipping overseas, so instead I went with a Canadian supplier and bought a pre-wired no soldering RGB LED for $3.95 and the board for $9.98 (on sale). There was a $12 shipping fee, but I order a lot from them so that cost is hardly noticed. Even so, that’s just $14 (+ $12 shipping). Amazon and many other stores also has low prices for these parts.

Learning To Count To 5 With a Raspberry Pi »

Learning To Count To 5 With a Raspberry Pi

Prototyping a small counting game – pressing a button to count up to 5 with LEDs and on an OLED screen – then celebrating once reached.

My daughter loves to help me build simple circuits, turning LED lights on and off on a breadboard. Not yet grasping her 1-2-3’s or A-B-C’s, I thought building a small game would be an engaging way to get her learning with the things she likes.

To put this together I used the following setup:

Wired as so:

By no means am I a Python developer, so I’m just swinging away here – the codes very repetitive, but hey, it does the trick easy enough.

# https://davidsword.ca/learning-to-count-to-5-with-a-raspberry-pi/

import time
import busio
import adafruit_ssd1306
import RPi.GPIO as GPIO
from board import SCL, SDA
from PIL import Image, ImageDraw, ImageFont

# settings
countTo       = 5 # probably don't change as there's 5 LEDs to count along with.
onCompleteMsg = "YAYY !!"
animateLoop   = 20 # how many LED chases to do
animateSpeed  = 0.04 # how fast to chase
screenPadding = 3 
fontSize      = 40
fontTTF       = 'whatever-font.ttf' # probably need to change this, see http://www.dafont.com/bitmap.php

# @see https://pinout.xyz/
# @see https://davidsword.ca/wp-content/uploads/2020/06/learning-to-count-to-5-with-a-raspberry-pi.png
GPIO.setmode(GPIO.BCM)
led1 = 5
led2 = 6
led3 = 13 
led4 = 19
led5 = 26
btn  = 22

# Display Setup
# @see https://learn.adafruit.com/adafruit-pioled-128x32-mini-oled-for-raspberry-pi/usage
i2c      = busio.I2C(SCL, SDA)
disp     = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c)
disp.fill(0)
disp.show()
width    = disp.width
height   = disp.height
image    = Image.new("1", (width, height))
draw     = ImageDraw.Draw(image)
font     = ImageFont.truetype(fontTTF, fontSize)

def displayClear():
    # Draw a black filled box to clear the image.
    draw.rectangle((0, 0, width, height), outline=0, fill=0)
    disp.fill(0)
    disp.show()

def display(val):
    displayClear()
    draw.text((screenPadding, screenPadding), str(val), font=font, fill=255)
    disp.image(image)
    disp.show()

def turnOn(pin):
    GPIO.output(pin, GPIO.HIGH)
    
def turnOff(pin):
    GPIO.output(pin, GPIO.LOW)

# @TODO loop this dynmically
def animation():
    turnOff(led1)
    turnOff(led2)
    turnOff(led3)
    turnOff(led4)
    turnOff(led5)
    turnOn(led1)
    time.sleep(animateSpeed)
    turnOff(led1)
    turnOn(led2)
    time.sleep(animateSpeed)
    turnOff(led2)
    turnOn(led3)
    time.sleep(animateSpeed)
    turnOff(led3)
    turnOn(led4)
    time.sleep(animateSpeed)
    turnOff(led4)
    turnOn(led5)
    time.sleep(animateSpeed)
    turnOff(led5)

GPIO.setup(led1, GPIO.OUT)
GPIO.setup(led2, GPIO.OUT)
GPIO.setup(led3, GPIO.OUT)
GPIO.setup(led4, GPIO.OUT)
GPIO.setup(led5, GPIO.OUT)
GPIO.setup(btn, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

displayClear()

count = 0
while True:
    if GPIO.input(btn) == GPIO.HIGH:
        if count < ( countTo - 1 ):
            count += 1
            display(count)
            if count == 1:
                turnOn(led1)
            if count == 2:
                turnOn(led2)
            if count == 3:
                turnOn(led3)
            if count == 4:
                turnOn(led4)
            # no long-pressing the button
            time.sleep(0.75)
        else:
            # we hit the limit, do some fun stuff.
            turnOn(led5)
            count = countTo
            display(count)
            time.sleep(0.75)
            display(onCompleteMsg)
            for z in range(animateLoop):
                animation()
            count = 0
            displayClear()

Run the script with python3 oled-count-game.py. If the script fails and you get a warning about missing modules, the modules can be installed usually with sudo pip3 install <module_name>, for the GPIO if that’s missing, it can be installed with sudo apt-get install python3-rpi.gpio --assume-yes.

A Cheaper DIY Status Light »

A Cheaper DIY Status Light

Building that thing I just built, but cheaper.

I recognize that my last post about a DIY Status Light the project had a total cost over $100 (and that wasn’t evening including SD cards, power supply, shipping & taxes). And that high cost wasn’t for core functionality, it was for aesthetics.

I wondered if I over did it, and how much it would cost and what we be involved, to build a status light for as cheap as possible. I quickly found the answer (from the store I frequent):

Raspiberry Pi Zero WH$20.95
4GB SD Card$5.95
Squid RGB LED$3.95
Total$30.85

Building this out, you’ll notice in the images on this post, I’m using a breadboard instead of the Squid. I did this because I had all the parts of the Squid already from separate kits just not assembled. Note everything in the pictures used is the exact same as the Squid, the Squids just pre-assembled and the cheapest way to get exactly what is needed and nothing more.

Because there is no big led matrix panel, I found a normal phone charger or laptop can be used to power the Pi and light instead of a proper >2 amp power supply. This saved about $5-10.

This is a solder-less approach, so there’s some extra cost in getting the Zero WH instead of just the Zero W and the pre-wired RGB LED, if you know how to solder and have the equipment and can shop around for cheaper parts or cheaper shipping, you may further shave a few bucks off.

Of course you’d need to get creative on a way to mount this at a place appropriate for your use case. Thinking back to when I used to work in an office setting, this would of been great to have stuck the Pi to the rear of my monitor and put this LED at the top corner of my monitor.


Never using GPIO pins before, the code to control this light was way easier than I thought it would be. I had a bit of a curve ball as it took me a while to realize my LED used a shared anode, not a shared cathode (like the Squid is) as both kinds exist, but the code changes to toggle the two is minor. Here’s the script for both and the wiring mentioned inline to test it out (I used $ pinout on my Pi to get the pin labelling):

Common anode (left), common cathode (right)
# https://davidsword.ca/a-cheaper-diy-status-light/

import sys
import RPi.GPIO as GPIO

GPIO.setwarnings(False)

# Note this is BOARD numbering.
GPIO.setmode(GPIO.BOARD)
redPin   = 3
greenPin = 5
bluePin  = 7

# set to false if using a RGB LED with common Anode.
commonCath = True

GPIO.setup(redPin,   GPIO.OUT, initial=0)
GPIO.setup(greenPin, GPIO.OUT, initial=0)
GPIO.setup(bluePin,  GPIO.OUT, initial=0)

if commonCath:
    setOn = 1
    setOff = 0
else:
    setOn = 0
    setOff = 1

def turnOn(pin):
    GPIO.output(pin, setOn)
    
def turnOff(pin):
    GPIO.output(pin, setOff)
    
def main():
    cmd = sys.argv[1]

    if cmd == "busy":
        turnOff(greenPin)
        turnOn(redPin)
    elif cmd == "available":
        turnOn(greenPin)
        turnOff(redPin)
    elif cmd == "offline":
        turnOff(greenPin)
        turnOff(redPin);
    else:
        print("Not a valid command")

main()

Edit this script if you’re using a common anode, set commonCath = False. This script can be run by passing the status as a single arg: python3 rgb-led-status.py <avaliable|busy|offline>. Example: $ python3 rgb-led-status.py busy

Forking the busy server, or setting up a new Flask server to run the code for adjusting this light as shown in the code snippet wouldn’t be a big lift at all (though you’ll have to take my word as I’m too pinched for time to prove that).

This was a fun 30 minute project – and a good way to save $60+ if function is greater than form to you.

(Case used in images is Zebra Zero GPIO Case by C4Labs).

UPDATE: My good friend, after reading this post, informed me there’s much much cheaper ways to do this using a $2 ESP32 board. I can’t wait to try that out.

DIY Slack Busy Light »

DIY Slack Busy Light

My adventure of recreating this DIY Raspberry Pi Busy Light by @eliostruyf for Slack.

A few months back, after listening to Deep Work and trying it out, I was hooked. I found I could accomplish the most difficult and daunting of tasks with absolute understanding and concentration. Though working from a small home with a young family and a couple of pets, needing to communicate at work, and my own personality traits, the absolute best I can do is 90 minute spans. 90 minutes of head down, no notifications, no distractions, no Slack, concentrated work – followed by a bit of a break, then another session. (I realize this is likely more Pomodoro technique than off-to-a-cabin-for-a-week true deep work, however the high level premise is the same, I get into a state of flow).

Setting up less notifications in my life, filtering all input, building timers, blocking out time for sessions, remembering to do it, and sticking to it, was all the easy stuff. Avoiding my family’s interruptions, was impossible. My wife, three year old daughter, and eleven month old son are all living their lives in our small home, feet away from me at any given time. They would randomly pop in, yell, knock, throw things, cry, text, ask, poke, question, hug etc. Breaking the session. Not only demotivating as it scratches the record on my focus that I just mentally committed too, but it would take around ten or more minutes to refocus and realign after they had left, leaving me a bit scrambled and a bit behind. Best illustrated in this comic:

My wife sympathized with me, and we both knew we needed some sort of way to indicate I was in these sessions, or on a call. The classic ‘door open/close’ or ‘headphones on/off’ wouldn’t work in our family, and shooting a text seemed unideal as that may backfire, ignite a conversation or trigger some reminder that may fill the brain with life instead of work. We both agreed that some sort of red light outside the door would be best.

I was thinking just a single LED light. I’d drill a hole in the door jam, and run some wires from a Raspberry Pi or other small computer or bluetooth device to turn the light on or off. This was the plan.

Then searching “DIY Busy Light” to see what else others have done, returned an amazing post by the very impressive @estruyf (which to my surprise, had only been posted days earlier) that took me away. It amazed me how good it looked and sounded. The code written for the project was minimal and well done. The parts looks awesome. I had never played with a Raspberry PI, or any DIY hardware, but this all excited me a great deal. I quickly ordered up everything.

I won’t go into detail on the hardware side as @estruyf’s post explains it all, but after a lengthy international shipping wait, it was easy enough put together.

I opted to not get the Pibow case and picked my own, but I was unable to put everything together because of the way my case and the holes of the board aligned the same, opposed to the Pibow that is wider and doesn’t use the Pis back two holes. Having no long tiny screws around to makeshift something myself, I ended up purchasing some legitimate standoffs and they worked well to pinch the whole thing together while giving the correct amount of even space between. Overall I was happy with how it turned out and I liked the darker backing and bolts:

On the software side, I use Slack for my work instead of Microsoft Teams as the tutorial guides for. And despite how my family will see only green or red, I recognized that I have more than just two states, each state needing to reflect differently in both Slack and on the Busy Light.

I began by defining those states in a JSON file, now hosted on Gist:

Life is complicated, so to me it was understandable to have this many different states. Basically, there are times I don’t want to be interrupted by work, other times family, sometimes both, and sometimes I welcome interruptions, and am open to new chats. Additionally, at my job communication is oxygen, so I do like to be explicit about why I’m not responsive during my normal work hours, and what state I’m in instead of just online or offline. (The rainbow status is just for fun, it’s available in the API so we’ve made it a signal that I want a hug from my daughter).

I next needed a consistent and quick method to reference and set these statuses. I didn’t want to memorize this table, or second guess myself on which version of an emoji to use. Even with knowing the keyboard shortcuts, I find the Slack UI a bit clunky for status setting – so I wrote a small Alfred workflow in PHP:

This Alfred Workflow pulls down, caches, and parses that JSON file of my statuses, displays them for quick selection, then when selected fires off a simple CURL to the Slack API, which sets my emoji status and online/offline presence. (I’ve opted to not share this code as it uses Slack Legacy Tokens, which are now unavailable).

Though I never forget to switch a status when I step out for a break or lunch, knowing myself, there was about a 100% chance of me forgetting to set an “In a meeting” status as I prepare and log into one. I automated this status setter with the Google Calendar Slack App. This app reads my meetings calendar and adjusts my Slack Status while in a scheduled meeting.

I furthered this automation to my Pomodoro timer (which I use to block out and keep on track of my 90min deep work sessions) so when my timer starts up, theres a call to an external workflow, triggering the Slack Status Workflow to set to DeepWork and go offline. Once the timer completes it clears the emoji and sets me to Active.

Everything boiled down to Slack being the canonical source of my current state, and defining a small pool of what that could be. Slack allows for easy automation and integration, and my JSON file allowed for easy editing and modifications. With this setup, the Busy Light just needed to read and reflect Slacks state.

To poll Slack, compare the emoji status and online/offline presence to the list of statuses, and determine the colour and send a request for the Busy Light, I thought about running a cron job on the Raspberry Pi itself, a single Python script that’d be under 100 lines, however, for several reasons I explain further below I opted to keep the RPI as a standalone server that only handled request. I only wanted the stable and simple Flask server by @estruyf on it.

So to bridge Slack and the Busy Light, instead I built a widget on my personal Dashboard “Albert”. Albert is an Express app that displays data from a personal PHP data API I have for myself (called ‘Artemis’, which supplies data to the Dashboard from other APIs like Google, current weather, stats on Things.app, etc).

The widget I wrote for the Dashboard in my API Service polls every 15 seconds, reading Slack, figuring out the status, and sending off a request to the Busy Light if it differs from the previous state sent. Making the Busy Light responsive within a quarter of a minute.

Besides being easier to debug and work on and writing in PHP instead of Python, another big draw of not writing the script on the Pi itself was that my Dashboard has a UI and a setup that allows for easily creating beautiful views for the data. Here’s some screen shots of the widget in four different sample states:

This Dashboard and widget is open all day on my second monitor, providing me a constant glimpse of Slack (despite the app not being open) and the Busy Light (despite the door being closed), which has proven very helpful for preventing accidental incorrect statuses.


So does it work?

A fair amount of time and energy (both things I don’t have) went into setting this up. It would be funny if for all the investments made that the light was ignored and useless. However, I’ve had the light up for about two weeks now and it’s been great. It works consistently, reliably, without fail, and most importantly the “red” is respected in our household. I’m getting in around 2-3 (uninterrupted) deep work sessions a day.


If I had to do it all over again:

I would opt out of using the Pogo pins. Despite how easy they look in the video on their site, for me I found they had to be tapped every so slightly in just the right angle after they were installed and already snug, nudged just right to get the lights up running. This problem would take about 5 extra minutes every time it was moved. I ended up switching which pins I used for ground and power and that helped a little, but it was just annoying to fix every time I worked on it. Luckily, I won’t be moving it anymore so this is a non-issue now and I will keep it as is, but I’d recommend instead to get a Zero WH and Unicorn HAT Mini so there’s a tight, solid, and reliable connection without all the fussing.

Secondly, when buying standoffs, I’d look for a kit that included longer screws, the small one-size provided in the kit I chose presented some challenges when putting the Pi on the case together, and made a simple task a litter harder and less secure than it ought to be.

Lastly, in my list statuses, there’s a few states that are starting to feel redundant and perhaps not as unique and useful as I had thought, like ‘On a Call’ vs ‘In a Meeting’. If I was starting up, I’d likley make the list as small as possible and grow it based on need instead of starting with a big list. Luckily, Alfred results in workflows order by frequency of use by default, so right about now after weeks of use, it will be evident which states I don’t use, and I can trim them off, refining the list.


Ah, this thing is so cool:


Links to build your own: