I always say that I'm a morning person in theory. I have had trouble getting up in the morning since I was in high school. Getting up is even harder if you frequently have to get up (even for part of the year) before the sun rises! Enter the sunrise alarm clock. This wall-mounted light will simulate a sunrise 15 minutes before your alarm. Follow along and see how I built it!
Project Summary (TL;DR)The sunrise alarm clock has 40 RGB LEDs. These LEDs are individually addressable, so it can display some neat animations. The main purpose of the light is to simulate a sunrise that occurs over 15 minutes before your alarm. Here is a short demo video of the alarm in action and some photos of the setup:
A Raspberry Pi hosts a webserver which is accessible only on my local network. I can connect to this webserver using my laptop, phone, etc by entering the PI's IP address and port into my web browser (ex. 192.168.0.16:4000). This webserver is how I configure the light and the alarm. The raspberry pi has an RS232 connection to the microcontroller that drives the LED ring. The microcontroller has a set of commands that the RPI uses to configure the current mode being displayed on the LED ring.
This sunrise alarm clock is part of a bigger project. This will actually be an add-on module to an internet connected alarm clock. I want to create an alarm clock base that will sit on my nightstand and replace the Raspberry Pi. This base would have a real-time clock, WIFI capabilities, a display to show the time, and a speaker. The sunrise light module will plug in to the back of the alarm clock so the alarm clock can trigger the sunrise before the alarm goes off.
EnclosureThe enclosure was designed with the help of my friend, and was 3D printed using white PLA filament. I am very new to 3D printing; this was actually my first 3D print ever!
The front of the enclosure has an area for the LED ring to fit in, and an area for a 6-inch diameter, 3mm thick piece of plexiglass to fit in. The LED ring and the plexiglass are distanced from each other to allow for some diffusion.
The back of the enclosure has a hollow space for electronics. The enclosure is designed to mount flush to the wall. There is a small keyhole for mounting, but it is not a standard size (we guestimated). There are also spots where a barrel jack adapter and an audio jack can fit into. The barrel jack adapter is friction fit (or can be glued). The audio jack is designed to be fastened using a nut.
I have included the STL file if anyone would like to print this. I also have the STEP file attached so anyone can edit the design for their own needs.
I ordered the plexiglass from a small laser-cutting company. I also got a neat sun decal from a small etsy shop. The decal is actually a sun catcher for a window but I thought the design was nice for this project.
Electronics DesignI ordered the LED ring from my favourite electronics shop at the time, Elmwood Electronics. Unfortunately in the time since I ordered the ring, the shop has gone out of business. :(
The ring is neo-pixel compatible, but I don't think there is a 40 pixel ring offered by Adafruit. After some googling I was able to find some very similar rings online. In case anyone wants to reproduce this project, I have included a screenshot of the original listing:
Make sure the ring is neopixel compatible because I use the Adafruit neopixel library in my code. Also, make sure the outside diameter of the ring is the same or else you would have to edit the enclosure.
I used an Itsy Bitsy M4 Express to drive my LED ring because I had it lying around. This board is small and it is easy to program because it runs Adafruit's "Circuit Python". Circuit Python lets you write python code for your microcontroller and program the board by simply by copying the script over like it's a flash drive.
To communicate with the Raspberry Pi I used an RS-232 interface because I wanted an easy, reliable interface for this project. I bought MAX3232 transceiver breakout boards from Sparkfun and connected them to the serial ports of the RPI and the ItsyBitsy.
The Itsy Bitsy has 3.3v logic, but has a special level-shifted pin that can do pulse width modulation (pin 5). I used this pin to control the LED ring. I followed Adafruit's NeoPixel hookup guide to connect the LEDs to the microcontroller safely.
The microcontroller and the LED ring are powered from a 5v source. A ~1000uF electrolytic capacitor is placed in between the voltage and ground to protect the LED ring. I used parallel 470uF capacitors for this. See the NeoPixel hookup guide for more info.
Pin 5 of the microcontroller is connected to the IN pin of the LED ring with a 470 ohm resistor in series.
The RS232 breakout has a "logic" side and "RS232" side. Take care to connect the LOGIC pins of the RS232 breakout boards to the microcontroller and RPI to avoid damaging the GPIO pins:
RX on the PI/microcontroller goes to RX1OUT
TX on the PI/microcontroller goes to TX1IN
If you're reproducing this project, make sure to google the pinout of your Raspberry Pi to find the UART0 TX and RX pins.
I used a 3.5mm audio cable to connect the two RS232 transceiver boards. I made sure to connect the RX1IN pin of each transceiver to the TX1OUT pin of the other transceiver when connecting the audio jacks. Generally I plug in the serial (audio) cable before I power the system.
The raspberry pi is not pictured in this schematic because it only has the RS232 transceiver and the audio jack hooked up to it.
I soldered all the components together using a scrap piece of perf-board:
The microcontroller used to drive the LED ring is running Circuit Python. This means that the code for this project is a simple python script.
The script first initializes the neopixels and enters an infinite loop. Inside the loop, the code checks to see if there are bytes in the UART buffer. If there are bytes present, a line is read out. The received bytes are then parsed and if they begin with the header "GB23", they are treated as a command. Depending on the command the LEDs are configured in a different way:
GB23 rainbow
This command enables the "rainbow cycle" mode.
GB23 off
This command turns off the LEDs.
GB23 colour [red] [green] [blue]
This command accepts decimal RGB values (0-255) and uses them to set the LED ring to a particular colour
GB23 sunrise
This enables the sunrise mode.
GB23 brightness [brightness]
This command accepts a float in between 0.0 and 1.0. This float represents the brightness of the LEDs.
GB23 moonlight
This enables a shimmery moonlight mode.
After the UART checking and command handling is complete, the system gets the time using a millisecond tick counter. The code will then update the LEDs using various functions depending on the current time and the mode. The loop will then repeat.
A new mode can be added by adding a command to the command parser, and creating a function that can run periodically to update the LEDs. This function would be added to the mode if statement at the bottom of the script.
# Update the LEDs depending on mode
if(mode == 1):
curr_frame = rainbow_cycle(curr_frame)
elif(mode == 2 and (abs(curr_time - frame_time)) >= SUNRISE_PERIOD_MS):
curr_frame = sunrise(curr_frame)
frame_time = curr_time
elif(mode == 3 and (abs(curr_time - frame_time)) >= MOONLIGHT_PERIOD_MS):
curr_frame = moonlight_shimmer(curr_frame)
frame_time = curr_time
# ADD A MODE HERE
elif(mode == 4 and (abs(curr_time - frame_time)) >= YOUR_MODE_PERIOD_MS):
curr_frame = YOUR_FUNCTION(curr_frame)
frame_time = curr_time
Installing Libraries and Code on the MCUThe code is designed to run on a board with Circuit Python 8.0.5.
Here is a guide on how to update the version of Circuit Python on your board.
Here is a webpage on how to obtain and install libraries on a Circuit Python board. From the libraries 8.x package you will need:
- Adafruit dotstar
- Neopixel
- Simpleio
The code can be cloned from the repository linked below. The code can be programmed on a board simply by copying the "code.py" file over to the Itsy Bitsy when it's connected to your PC.
3D printing was not my only first for this project. This was also my first time doing any sort of web development; because of this I will not go too in-depth on the web app. I will include an overview of the app and some of the resources I used. The repository for the web app is separate from the microcontroller repo and can be found below.
I created this webapp using Flask. Flask allows you to create a web application with a convenient python backend. These are some of the resources I used to learn about Flask:
RPI Flask Application Tutorial
Simple Flask App Youtube Tutorial
The web app is laid out with a mode section, an alarm section and a raw command section:
I used various HTML features to get input from the user such as a colour picker, a slider, and a time input box. CSS was used to style the page.
"app.py" is the back end of the application, providing functionality for each button. Each button links to a different URL and has a python function associated with it. The majority of the buttons simply gather some user input from the form, send a command to the sunrise alarm clock, and render the HTML file (potentially with some information for the user).
The "Set Alarm" and "Disable Alarm" buttons are special because the alarm functionality is not instantaneous; an alarm must be set and run in the background. When "Set Alarm" is pressed, an additional process is spawned that sleeps until the system reaches the alarm time. After the alarm time is reached "GB23 sunrise" is sent to the microcontroller. This functionality will repeat indefinitely until the process is killed by either setting the alarm again or pressing "Disable Alarm". The code for the alarm process is contained in "sunrise_alarm.py".
While the alarm is set, the web application can continue to handle requests and change the LED mode. When the alarm occurs, whatever LED mode is active will be overwritten by the sunrise mode.
The web application uses the following tools that need to be installed on the raspberry pi:
Flask
The framework used to build the web application.
Pyserial
A package that allows python to send and receive over the serial port.
Gunicorn
This is an easy to use, robust web server which will actually run the flask application on your Raspberry Pi.
Screen
Screen allows you to create a terminal session(s) on your Raspberry Pi that will persist, even when you disconnect from the Pi. This allows you to SSH into the Pi, run an application, and disconnect without quitting the application.
Deploying The Web App on a Raspberry PiThis section will outline the steps you can use to deploy the webapp on your Raspberry Pi.
I prefer to have a new operating system installation on my Rasperry Pi to get it into a known state. If you don't want to re-image your RPI, skip to step 3.
Note: Re-imaging the Raspberry Pi will delete any existing data you have on the SD card.
1. Install raspbian on a SD card from your PC using Pi imager tool: https://www.raspberrypi.com/software/
2. Insert the SD card into the Pi, connect a monitor, mouse, and keyboard, and go through the configuration.
3. Open a terminal on the Pi desktop and enter "sudo raspi-config".
4. Under "Interface options", enable SSH.
5. Also under Interface Options > Serial Port: disable the login shell and enable the serial port.
6. Select "Finish" and Reboot the PI.
7. Find the IP address of your PI and configure it to be static, so it doesn't change: Static IP Configuration Tutorial. I also had to reserve the PI's IP by logging on to my internet router.
8. Now you shouldn't need the monitor, mouse and keyboard attached to the Pi as long as your PC is on the same network as your Pi. Open a Powershell/terminal window on your PC and SSH into the PI using the IP, the username, and password. (default user: pi, password: raspberry) (ex. "ssh pi@192.168.0.16")
The next few steps are based on this tutorial. Reference it for more details.
9. Create or navigate to a directory to store the application.
10. Create a new python virtual environment and activate it. (See tutorial above.)
11. Install flask using "pip install flask".
12. Install pyserial using "pip install pyserial".
13. Install gunicorn using "pip install gunicorn".
14. Install screen using this tutorial.
15. Copy all the files from the webapp repo flat into your directory. I used FTP from my PC, but feel free to use GIT.
16. Run "screen" and hit enter.
17. Make sure you're in the folder with app.py and run the following command: "sudo gunicorn -w 1 -b 0.0.0.0:4000 --timeout 120 app:app". Your app should now be accessible at your RPI's IP address over port 4000.
18. Type ctrl + a then d to detatch from the screen instance.
You can exit SSH connection now and your server should keep running. If you need to get back to the session type "screen -x".
ConclusionThanks for reading my sunrise alarm clock project blog! Stay tuned for the second part of this project :)
Comments