Bentley Carr

About Me

I'm Bentley Carr and I love me!

Experiences

Over the years, I've done many things to help me gain experience in the area of computer science. Click below to find out about these.

Analog LED Clock

Having recently redecorated my room, I lacked any time-telling device which spiced up my room with a modern appearance. I wanted a clock which would use bright LED lights arranged in a circle around the outside of the clock to display the time in five minute intervals, like an analog clock, so that it retains the features of a traditional analog clock, but does so in a unique, sort of retro way.


Intern in the IT Development Department

At 8 Securities in December 2015

At the start of my summer holidays, I began to work in the 8 Securities Hong Kong office as an intern in the IT development team. During this time, I discovered the benefits of C# and Angular JS. I was also introduced into the idea of writing tests, more specifically Unit Tests, and the notion of TDD (Test Driven Development). I also learned about structuring programs, as well as a little about microservice architecture, and how it could give way to many advantages for a team's development. I was also introduced to scrum, and took part in not only daily scrums, but also in planning and review meetings at the beginning and end of each two-week sprint. There was also a focus on writing readable and concise code, as opposed to writing long, messy and hacky code, something I was not too accustomed in focusing on.

During my first week, I wrote a little console application which sent out an email to clients who had only partially registered, asking them to come back and finish registering. It was by working on this, which I discovered the beauty of C#, especially, C#'s LINQ queries and how C# implements asynchronous programming. Through this, I learned how to write asynchronous methods, and how to await asynchronous methods, whether it was to get a result, or just to manage control flow. I also learned about managing Tasks in C#, starting multiple tasks at once, awaiting results and using ContinueWith methods. I also used SQL queries for the first time. By developing this console application, I also learned about the SoC (Separation of Concerns) principal which states that sections of a program should be separated into separate parts, each only depending on the minimum amount of libraries which they require, and each referencing only certain parts of the application which consequently result in a program with clear control flow. There was also a focus on writing "nice" code as opposed to "messy" code. "Nice" code describes code which is self-documenting, requiring very little comments. It should generally be as concise as possible. This forced me to use LINQ queries and break long methods into multiple shorter methods which made my code much easier to follow and resulted in much better code.

In the first week, I also learned about microservice architecture, and how it compares to monolithic architecture. A microservice architecture consists of many services which only interact to each other through their APIs, and do not interact with their databases directly. I also learned about the structure of each of these services, and how service is made of multiple components. Each service has a controller component which controls the main logic. There is then an abstraction layer, an interface, which is expanded by a gateway component which connects the client to the server, and a fake gateway, used for testing. These components are not retrieved by the controller, but they are instead injected into the controller by either an IoC (Inversion of Control) or the Unit Testing controller. Another thing I learned about was the multiple environments which each service runs on. When a change is made, it is firstly deployed to the DEV environment if it successfully builds, where the Unit Tests will be run. If it passes everything successfully, it will be deployed to the UAT environment, and eventually the PRD (production) environment. This movement of updates from one environment to another is what we referred to as Continuous Integration.

I also had to modify the current PHP code for the registration page, to get the language which the user is using, so that my console application knows what language to send the emails out in.

During my second week, I fixed some bugs and tweaked the UI of an Angular app. I learned about Angular JS, and how it and Gulp can be used as a framework for large applications. I also realised that external libraries such as ui-grid, ui-router, ngDialog and ngProgress are used quite often in the development world. I also realised that sometimes it is better to use templates such as RDash for building an application, rather than writing everything from scratch. In this week, I was also required to add a button, which sends an http delete request to run an already-existing API.

I also worked with CSS to tweak the UI, and I also experienced first hand how bootstrap can be used to create flexible user-interfaces.

Another aspect of life in the development team which I took part in, was scrum. At the start of each day, we would meet for a maximum of 15 minutes to tell each other what we did the previous day, and what we were going to do today. I also took part in the planning of each sprint, where we assigned a certain amount of time to each task, and the review and retrospective meetings where we discussed what was good and bad about the sprint.


Chyme is an open-source app which gets a list of rhymes for a specified word. The logic was written in C#, and then an Android app was created with Xamarin. I also plan to create a little web-app based off Chyme with Angular 2 in the future.

Chyme can be used to get different types of rhymes of words. I define two words as being a Perfect Rhyme when the last stressed vowel sound and all subsequent sounds match (eg. rhyme and time). I define two words as being Semi-Rhymes when the last stressed vowel sound and all subsequent sound match the end of the second word, although the vowel of the second word need not be stressed (eg. Rhyme and enzyme). In addition, a half-rhyme is when the sounds following the last stressed vowel sounds match, although the actual vowel sounds themselves need not match.

In Chyme, I use the Carnegie Mellon University Pronunciating Dictionary to get the pronunciation of each word to determine whether they rhyme or not, and I also use Hermit Dave's Frequency Word List to sort the list of rhymes by how common they are.

Chyme will be available on Google Play soon and the source is available on Github.

Introduction

Having recently redecorated my room, I lacked any time-telling device which spiced up my room with a modern appearance. I wanted a clock which would use bright LED lights arranged in a circle around the outside of the clock to display the time in five minute intervals, like an analog clock, so that it retains the features of a traditional analog clock, but does so in a unique, sort of retro way.

The most important component of this project would be the choice of computer to power the clock. I decided to use the Raspberry Pi Model A+, due to its low cost, ease of use, and large community. In addition, I am also a Raspberry Pi fan, and had quite a few lying around.

Controlling the LEDs

When I first started this project, the newer models of the Raspberry Pi with more GPIO pins hadn’t been released yet; I only had a Model B on me, which only had 8 GPIO pins. Traditionally, one would connect the LED's cathode to a different GPIO pin and the negative side to a ground pin. Then, when one of the pins are set to a high potential, the connected LED would turn on, and when the pins are set to a low potential, the LEDs would turn off. Because LEDs will only turn on when the current is flowing in one direction and not the other, it is possible to control a large amount of LEDs with only a small amount of pins, as long as in the circuit, no more than one LED is turned on at any given time. This idea is commonly known as Charlieplexing.

If I have two GPIO pins, I can connect two LEDs in parallel, one with its anode connected to the first pin and its cathode to the second, and the other with the anode to the second and the cathode to the first. By making the current flow one way, we can turn on one LED, but we can also turn on the other LED by making the current flow the other way. When this is scaled up to 4 GPIO pins instead of 2, we are able to control 12 individual LEDs. This allows us to control 24 LEDs with only 8 GPIO pins like so:

I use pins 11, 13, 15 and 12 for the hour LEDs and pins 16, 18, 22 and 36 for the minute LEDs as shown below.

Keeping the Time

Another issue was that the Raspberry Pi does not have a hardware clock and it uses NTP; a protocol which gets the time from the internet every bootup. This means that if the clock doesn’t have a network connection, every time it turns off, the time will reset to midnight. This is obviously not ideal, so I ordered a DS3231 RTC module which keeps the time once the Pi has been switched off. It sits on the Pi’s pin-header nicely, although it does cover up one of the GPIO pins, however this is fine as the Model A+ has more than 8 GPIO pins, and if I were to use a Model B, it would be fine since the RTC doesn't actually use the GPIO which it sits ontop of.

Setting up the RTC was a little fiddly. Firstly, I had to setup I2C. This can be easily done through the raspi-config tool. I entered sudo raspi-config into the unix shell, navigated to 8 Advanced Options and then to A7 I2C to enable it. I then rebooted.

Secondly, I had to install some stuff: sudo apt-get install python-smbus i2c-tools python3 python3-rpi.gpio

The next steps are easier if we login as the root account.

sudo su

I then set up the RTC module. I firstly told the kernel to load the module when the Pi boots up:

echo i2c-bcm2708 > /etc/modules
echo i2c-dev > /etc/modules
echo rtc-ds1307 > /etc/modules

Next, we need to initialise the RTC on boot:

sed -i -e '$i \echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-0/new_device &\n' /etc/rc.local
sed -i -e '$i \echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device &\n' /etc/rc.local

Then reboot:

sudo reboot

(I used the DS1307 kernel modules even though I had a DS3231 device.)

I also had to add the following to /boot/config.txt, although it may change depending on which distribution of Raspbian is being used:

dtparam=i2c_arm=on
dtparam=spi=on

And I also added the following to /boot/cmdline.txt:

bcm2708.vc_i2c_override=1

This is as far as online articles guided me and I was still left with a few issues. Firstly, the hardware clock’s time would not persist between reboots! I eventually found out this was because the network time protocol (NTP) was still active on startup, and when the Pi started up, it was writing the network time to the hwclock, even though it wasn’t able to get the network time. This can be fixed by disabling NTP:

sudo apt-get remove ntp

After I had fixed this issue, the hardware clock’s time still wasn’t synchronising with the system’s time. This can be fixed by adding hwclock -s to rc.local.

sudo su
sed -i -e '$i \hwclock -s &\n' /etc/rc.local

Now, after rebooting, I could set the time and write it to the RTC.

date --set 2015-05-29
date --set 16:45:00
hwclock -w

The Software

I used Python to control the GPIO pins. Since I only have 12 LEDs for the minutes, I can only show the nearest 5 minutes. This means that each LED for 5 minutes will show the time for the multiple of 5, ±2 minutes. This means the nth LED will turn on if the current minute is (5n±2)th.

I also decided to make the quickly minute “hand” quickly spin around on the hour. This is analogous to how some clocks make a sound on the hour.

I use GPIO pins 11, 13, 15 and 12 for the hour LEDs and GPIO pins 16, 18, 22 and 36 for the minute LEDs. These correspond to the board numberings and can be changed at the top of the python file.

I execute the program on startup and it can be executed as so:

python clockcontroller.py

I also built in an LED testing feature which can be executed by:

python clockcontroller.py test

It can be downloaded here, and the code is below:

#!/usr/bin/env python3
import time
import math
import random
import sys
import RPi.GPIO as gpio

old_hour = 0
h_set = 0
m_set = 0

testing = False

GPIO_HOUR_ONE = 11
GPIO_HOUR_TWO = 13
GPIO_HOUR_THREE = 15
GPIO_HOUR_FOUR = 12

GPIO_MIN_ONE = 16
GPIO_MIN_TWO = 18
GPIO_MIN_THREE = 22
GPIO_MIN_FOUR = 36

# Set the LED of either hand.
def setSingleLed(GPIO_ONE, GPIO_TWO, GPIO_THREE, GPIO_FOUR, LED_NO):
  if LED_NO < 0 or LED_NO > 11:
    print ("GPIO LED number out of range; setSingleLed(" + str(GPIO_ONE)
      + "," + str(GPIO_TWO) + "," + str(GPIO_THREE) + ","
      + str(GPIO_FOUR) + "," + str(LED_NO) + ").")
    return 0

  if LED_NO == 0:
    gpio.setup(GPIO_ONE,gpio.OUT)
    gpio.output(GPIO_ONE,gpio.HIGH)
    gpio.setup(GPIO_FOUR,gpio.OUT)
    gpio.output(GPIO_FOUR,gpio.LOW)
    gpio.setup(GPIO_TWO,gpio.IN)
    gpio.setup(GPIO_THREE,gpio.IN)
    return 0
  if LED_NO == 1:
    gpio.setup(GPIO_FOUR,gpio.OUT)
    gpio.output(GPIO_FOUR,gpio.HIGH)
    gpio.setup(GPIO_ONE,gpio.OUT)
    gpio.output(GPIO_ONE,gpio.LOW)
    gpio.setup(GPIO_TWO,gpio.IN)
    gpio.setup(GPIO_THREE,gpio.IN)
    return 0
  if LED_NO == 2:
    gpio.setup(GPIO_ONE,gpio.OUT)
    gpio.output(GPIO_ONE,gpio.HIGH)
    gpio.setup(GPIO_TWO,gpio.OUT)
    gpio.output(GPIO_TWO,gpio.LOW)
    gpio.setup(GPIO_THREE,gpio.IN)
    gpio.setup(GPIO_FOUR,gpio.IN)
    return 0
  if LED_NO == 3:
    gpio.setup(GPIO_TWO,gpio.OUT)
    gpio.output(GPIO_TWO,gpio.HIGH)
    gpio.setup(GPIO_FOUR,gpio.OUT)
    gpio.output(GPIO_FOUR,gpio.LOW)
    gpio.setup(GPIO_ONE,gpio.IN)
    gpio.setup(GPIO_THREE,gpio.IN)
    return 0
  if LED_NO == 4:
    gpio.setup(GPIO_TWO,gpio.OUT)
    gpio.output(GPIO_TWO,gpio.HIGH)
    gpio.setup(GPIO_ONE,gpio.OUT)
    gpio.output(GPIO_ONE,gpio.LOW)
    gpio.setup(GPIO_THREE,gpio.IN)
    gpio.setup(GPIO_FOUR,gpio.IN)
    return 0
  if LED_NO == 5:
    gpio.setup(GPIO_FOUR,gpio.OUT)
    gpio.output(GPIO_FOUR,gpio.HIGH)
    gpio.setup(GPIO_TWO,gpio.OUT)
    gpio.output(GPIO_TWO,gpio.LOW)
    gpio.setup(GPIO_ONE,gpio.IN)
    gpio.setup(GPIO_THREE,gpio.IN)
    return 0
  if LED_NO == 6:
    gpio.setup(GPIO_THREE,gpio.OUT)
    gpio.output(GPIO_THREE,gpio.HIGH)
    gpio.setup(GPIO_TWO,gpio.OUT)
    gpio.output(GPIO_TWO,gpio.LOW)
    gpio.setup(GPIO_ONE,gpio.IN)
    gpio.setup(GPIO_FOUR,gpio.IN)
    return 0
  if LED_NO == 7:
    gpio.setup(GPIO_THREE,gpio.OUT)
    gpio.output(GPIO_THREE,gpio.HIGH)
    gpio.setup(GPIO_FOUR,gpio.OUT)
    gpio.output(GPIO_FOUR,gpio.LOW)
    gpio.setup(GPIO_ONE,gpio.IN)
    gpio.setup(GPIO_TWO,gpio.IN)
    return 0
  if LED_NO == 8:
    gpio.setup(GPIO_TWO,gpio.OUT)
    gpio.output(GPIO_TWO,gpio.HIGH)
    gpio.setup(GPIO_THREE,gpio.OUT)
    gpio.output(GPIO_THREE,gpio.LOW)
    gpio.setup(GPIO_ONE,gpio.IN)
    gpio.setup(GPIO_FOUR,gpio.IN)
    return 0
  if LED_NO == 9:
    gpio.setup(GPIO_FOUR,gpio.OUT)
    gpio.output(GPIO_FOUR,gpio.HIGH)
    gpio.setup(GPIO_THREE,gpio.OUT)
    gpio.output(GPIO_THREE,gpio.LOW)
    gpio.setup(GPIO_ONE,gpio.IN)
    gpio.setup(GPIO_TWO,gpio.IN)
    return 0
  if LED_NO == 10:
    gpio.setup(GPIO_ONE,gpio.OUT)
    gpio.output(GPIO_ONE,gpio.HIGH)
    gpio.setup(GPIO_THREE,gpio.OUT)
    gpio.output(GPIO_THREE,gpio.LOW)
    gpio.setup(GPIO_TWO,gpio.IN)
    gpio.setup(GPIO_FOUR,gpio.IN)
    return 0
  if LED_NO == 11:
    gpio.setup(GPIO_THREE,gpio.OUT)
    gpio.output(GPIO_THREE,gpio.HIGH)
    gpio.setup(GPIO_ONE,gpio.OUT)
    gpio.output(GPIO_ONE,gpio.LOW)
    gpio.setup(GPIO_TWO,gpio.IN)
    gpio.setup(GPIO_FOUR,gpio.IN)
    return 0

# Get hour LED to display
def getHourLed():
  h_number = int(time.strftime("%I"))
  if int(time.strftime("%M")) >= 58:
    h_number += 1
  
  if h_number >= 12:
    return h_number - 12
  else:
    return h_number

# Get minute LED to display
def getMinuteLed():
  m_number = int(math.floor((int(time.strftime("%M"))+2)/5))
  if m_number >= 12:
     return m_number - 12
  else:
    return m_number

# Set Hour LED and record
def setHourLed(h_number):
  global h_set
  setSingleLed(GPIO_HOUR_ONE, GPIO_HOUR_TWO, GPIO_HOUR_THREE, GPIO_HOUR_FOUR, h_number)
  h_set = h_number
  print ("Hour hand is " + str(h_number))

# Set Minute LED and record
def setMinuteLed(m_number):
  global m_set
  setSingleLed(GPIO_MIN_ONE, GPIO_MIN_TWO, GPIO_MIN_THREE, GPIO_MIN_FOUR, m_number)
  m_set = m_number
  print ("Minute hand is " + str(m_number))

# Spin Hour LED (when a new hour arrives)
def rotateHourLed(amount):
  passes_left = amount
  current_hour_led = h_set
  pass_point = getHourLed()
  
  while passes_left > 0:
    current_hour_led += 1
    if current_hour_led >= 12:
      current_hour_led -= 12
    setHourLed(current_hour_led)
    if current_hour_led == pass_point:
      passes_left -= 1
    time.sleep(0.083)

# Gets if the clock has struck a new hour
def isNewHour():
  global old_hour
  if int(time.strftime("%I")) == old_hour:
    return False
  else:
    old_hour = int(time.strftime("%I"))
    return True

# Sets up the GPIO mode
def init():
  gpio.setmode(gpio.BOARD)
  gpio.setwarnings(False)

# Set everything and spin the LEDs if it is time
def main():
  print("\nThe time is " + time.strftime("%I:%M"))
  if isNewHour():
    rotateHourLed(int(time.strftime("%I")))
  setHourLed(getHourLed())
  setMinuteLed(getMinuteLed())

# Set random LEDs
def aprilFool():
  setHourLed(int(random.randrange(0,11,1)))
  setMinuteLed(int(random.randrange(0,11,1)))

def test():
  hierarchialLevel = 0
  testingHour = False
  
  while hierarchialLevel >= 0:
    
    print("############################################")
    
    if hierarchialLevel == 0:
      print ("Test which set of LEDs?")
      print ("0 - Hour LEDs\n1 - Minute LEDs\n2 - Exit")
      inSet = input("> ")
      try:
        choice = int(inSet)
      except:
        print ("Sorry, that wasn't a valid option.")
        continue
        
      if choice == 0:
        testingHour = True
        hierarchialLevel = 1
      elif choice == 1:
        testingHour = False
        hierarchialLevel = 1
      elif choice == 2:
        hierarchialLevel = -1
        print ("Exitting...")
        sys.exit(0)
      else:
        print ("Sorry, that wasn't a valid option.")
        continue
        
    if hierarchialLevel == 1:
      print ("Which LED would you like to be turned on? (0-11)")
      print ("Alternatively, press Enter to go back.")
      inNum = input("> ")
      
      if not inNum.strip():
        hierarchialLevel = 0
        continue
      else:
        try:
          choice = int(inNum)
        except:
          print ("Sorry, that wasn't a valid option.")
          continue
          
        if choice < 0 or choice > 11:
          print ("Sorry, that wasn't a valid option.")
        elif testingHour:
          setSingleLed(GPIO_HOUR_ONE,GPIO_HOUR_TWO,GPIO_HOUR_THREE,GPIO_HOUR_FOUR,choice)
        elif not testingHour:
          setSingleLed(GPIO_MIN_ONE,GPIO_MIN_TWO,GPIO_MIN_THREE,GPIO_MIN_FOUR,choice)
        else:
          print ("Sorry, that wasn't a valid option.")
          continue

init()

# Main loop
while True:
  try:
    if sys.argv[1].strip().lower() == "test":
      testing = True
    else:
      testing = False
  except:
    testing = False
  
  if not testing:
    if time.strftime("%d %m") == "01 04":
      aprilFool()
    else:
      main()
    time.sleep(0.5)
  
  if testing:
    test()
    break

Setting up the rest

Now we have to make the piclock program start when the raspberry pi boots up. I first put my clockcontroller.py in /opt/piclock, and I created /opt/piclock/start.sh as so:

#!/bin/sh
python3 /opt/piclock/clockcontroller.py
exit 0

Then we have to give it the necessary permissions so it can be executed:

chmod +x /opt/piclock/start.sh

Then I created /etc/systemd/system/piclock.service with the following contents:

[Unit]
Description=PiClock
DefaultDependencies=false

[Service]
Type=simple
ExecStart=python3 /opt/piclock/start.sh
WorkingDirectory=/opt/piclock

[Install]
WantedBy=local-fs.target

Then executing the following made PiClock start on bootup.

systemctl enable piclock

I also disabled a whole bunch of services which I don't need to make bootup faster:

sudo bash -c 'systemctl disable fake-hwclock && systemctl disable networking && systemctl disable kbd && systemctl disable raspi-config && systemctl disable ntp && systemctl disable dphys-swapfile'

Now, the LEDs should light up every time we reboot the Pi.

If you would like to create a PiClock of your own, without having to configure the Raspbian image, you can flash the below image onto an SD card. It is configured to run on a model A+.

Desktop Setup

Intel® Core™ i5-4690K Processor @ 4.59GHz

ASUS GeForce® GTX 750 TI GTX750TI-OC-2GD5

ASUS Z97-A Motherboard

DeepCool Tesseract BF Mid Tower Computer Case

Kingston 8GB DDR3 Memory

Corsair CX500M

Sandisk 128GB SSD

Western Digital WD Blue 1TB HDD

IOGEAR GBU521 USB Bluetooth 4.0 Micro Adapter


Desk Setup

AOC I2367FH 23" 1920x1080 Monitor

Dell 1704FPTT 17" 1280x1024 Monitor

Logitech MX Master Wireless Mouse

Logitech Wireless Solar Keyboard K750R

JBL Charge 2+ Speakers


Laptop

Microsoft Surface Pro 4

Intel® Core™ i7-6650U Processor @ 2.20GHz

Intel Iris Graphics 540

256GB SSD

12.3" 2736x1824 Display


Phone

Oneplus Two 64GB

200

OK