This tutorial expects you to already have an account with balenaCloud and a Raspberry Pi provisioned. If you haven't, we have an excellent set of getting started guides to get you up and running.
Software
Before touching the hardware, let's focus on the software, understand what we want to achieve and the steps we need to take to get there.
Initially, the code can be separated into two parts, one that fetches the current Bitcoin price and runs the algorithm to decide if the price is higher or lower than the opening price, and another that controls the electronics in the traffic light via the Raspberry Pi GPIO pins.
To obtain the latest Bitcoin price we'll use the API provided by CoinDesk. The data is provided in JSON format, which you can find more about here.
Docker Container
As balenaCloud operates using Docker containers, we must first create a Dockerfile with the container configuration. The container configuration allows us to specify exactly what software components we will need for our application.
You can find the entire source code in Python on Github, but let's dive in block-by-block to understand the code.
import RPi.GPIO as GPIO
from time import sleep
import datetime
import urllib.request
import json
# Initiate GPIO with breakout pin numbering
GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False)
# Setup GPIO Pins
red = 23
green = 27
orange = 22
GPIO.setup(red, GPIO.OUT)
GPIO.setup(orange, GPIO.OUT)
GPIO.setup(green, GPIO.OUT)
We are going to use a library called RPi.GPIO in order to be able to control the GPIO pins of the Raspberry Pi Zero from our code. You can see that library being imported here
The second part of the code above enables the GPIO pins to be controlled by referencing the numbers in the connector. We decided to use pins 22, 23 and 27 to set the colors of the Traffic Light, and so we need to set those pins as digital output pins.
Now we can write the part of the code that reads and parses the information provided by the Coindesk API. The function get_price(url, hdr) reads the price in USD and returns the value with two decimal places. The three set_color_ functions set the color of the traffic light by toggling the appropriate GPIO pins.
Once we have the initial code ready, it's time to think about the main logic of the program. The opening price is the Bitcoin price at midnight, or at the time the system goes live. After that we need to fetch the current price every minute and perform a comparison to check if it is higher or lower than the opening price. If higher, we print the text in green, otherwise it's printed in red. The same logic goes into the output for the traffic light, if higher we turn the green light on, otherwise red. We've also included a 5 second transition period through the orange light to make it more obvious when the state changes.
# Initialize Values
opening_price = get_price(url, hdr)
max_price = opening_price
min_price = opening_price
current_price = opening_price
current_day = datetime.datetime.today().day
previous_day = current_day
previous_state = "green"
while True:
# Check if new day to reset opening price
current_day = datetime.datetime.today().day
if (current_day != previous_day):
previous_day = current_day
opening_price = get_price(url, hdr)
# Get current Bitcoin price
current_price = get_price(url, hdr)
# Set max and min prices of the day
if(current_price > max_price):
max_price = current_price
if(current_price < min_price):
min_price = current_price
# Show prices in terminal
message = str(current_day) + " - Current Price: $" + current_price + " - Daily Max: $" + max_price + " - Daily Min: $" + min_price + " - Opening Price: $" + opening_price
if(current_price >= opening_price):
if previous_state == "red":
# If previous state was red, we want to make it orange to show transition
previous_state = "green"
set_color_orange()
sleep(5)
prGreen(message)
set_color_green()
else:
if previous_state == "green":
# If previous state was red, we want to make it orange to show transition
previous_state = "red"
set_color_orange()
sleep(5)
prRed(message)
set_color_red()
sleep(60)
If there's anything in the code you don't understand, or would like help modifying it to fit your own project, we're always hanging out in our forums and would be happy to help out with any questions you have.
Hardware
With our Raspberry Pi Zero W up and running the container and software from the previous section, now it is time to get our hands dirty and work on the hardware.
SD Card (we recommend Sandisk Extreme Pro SD cards)
Soldering iron & wires
The first thing we need to do is disassemble the lamp and remove the electronics. Looking at the back of the lamp, we can see that it uses 3x AA batteries in series, that means 4.5V.
With everything working disconnected from the main case, it is time for the most fun part, to reverse engineer the circuit and understand how it works. Surprisingly enough the circuit is very simple, giving us a lot of room to play with it.
The device name of the microcontroller on the left has faded-away making it hard to know exactly how it works, but by exploring the rest of the circuit, we realized it is a very simple circuit with one BJT NPN transistor to drive each led.
In the original circuit, everything runs on 4.5V, but with the RPi Zero we only have 5V and 3V3, so what we decided to do here is to control the base of the transistor with 3V3 (which is the voltage of the GPIO pins) and power the led from the 5V, making it slightly brighter than the original.
From here we connect the 5V and GND pins from the RPi to the circuit, desoldered the microcontroller from the PCB and solder three wires that will go to the pins 22, 23 and 27, as chosen at the beginning of the project.
All there is left to do now is to put the circuit back inside the traffic lamp (we used some hot-glue to make sure everything stays in place) and power up the board. Once it connects to balenaCloud and downloads the latest code it will turn on the LEDs.