For our final OPS project, we're providing you with two new parts, and a fully completed project to build—an RGB clock lamp featuring several lighting animations to choose from, the ability to dim or turn off automatically at night (while keeping track of what month it is so it knows when sunset and sunrise are, as well as daylight savings time), and a battery so it won't lose time when you unplug your Arduino.
If you're looking for the full sketch download, it's in the Code section below :)
Let's introduce the two new (and quite powerful) components we're using in this project.
Addressable LEDs are a powerful tool for lighting displays and projects because they allow us precise control over each LED's (or pixel’s) color and brightness in a strip using only a single data connection to the entire chain—rather than driving each red, green, and blue LED with its own PWM pin from our MCU. They do this by incorporating a small PWM controller inside of each pixel, which can communicate with other pixels in the strip (and with our MCU) using a simple one wire serial communication protocol.
In the grand scheme of electronics history, Neopixels and individually addressable LEDs are relatively new. The WS2812B chip, which powers the Neopixels used in this project, was introduced in 2012 as successor to the earlier WS2811 (itself only being around since 2008). Before these chips came about, the idea for individually addressable lights existed, but it hadn't been miniaturized or standardized into such a convenient little device. In 2013, Adafruit helped popularize Neopixels in the DIY electronics community by providing user-friendly libraries, tutorials, and accessories.
If you want to learn lots more about Neopixels including an introduction to the Neopixel code library, and best practices for using them in your own projects such as power supply sizing and cirucit protection, check out Adafruit's Neopixel Überguide!
The Neopixel strings we gave you for this project don't look like your typical LED strip; they're what's called "fairy light" style LED strings, and it can make identifying the pins of the LED strip a little difficult at first.
Start by placing the end of your LED strip with the pins on the right, and the LED s facing up. You should see some small black dots on the top wire; this is the 5V wire. The middle wire is the data input pin, which you'll need to connect in series with a 470Ω resistor to your MCU, and the wire opposite the 5V one is the GND. In case you were curious, the resistor is there to limit current caused by voltage spikes on the data line that could damage your first pixel (possible at MCU startup due to transient voltages and noise in the MCU or PCB present during startup).
Caution: Be very careful plugging and unplugging these pins on your breadboard; they are fragile and can break off of the wires easily!
If they do break off, you'll have to solder the pins back on (which you can do in the ECE Makerspace if you have CODA'd into ECE and completed the makerspace safety training).
Real-Time Clock (RTC) modules are chips that can keep track of the current time, even when the main power supply is turned off. RTCs typically provide the current time in hours, minutes, and seconds, keep track of the date, and some might also have settable alarms and other customizable time-related features that you can use in your program to make certain code execute on set schedules.
During normal operation, an RTC is powered by the same power source as your MCU, but when the main power source is off (i.e. when you unplug your Arduino), everything but a small oscillator inside the RTC chip shuts down. This low-power oscillator (which keeps track of time) stays on, powered by a small battery (usually a coin-cell), until the main power source is restored. Then, the rest of the RTC module powers back on and starts communicating with the MCU via serial again.
The DS3231, which is used on the RTC module we've given you for this project, is a widely used RTC chip that offers high accuracy (it'll only lose or gain about a minute per year when running on battery) and low power consumption (it'll last 5-8 years on a single CR2032 battery, depending on the quality of the battery). It features an integrated temperature-compensated MEMS oscillator for precise timekeeping and can operate on a supply voltage of 2.3V to 5.5V (A CR2032 is 3.3V nominally). In addition to the time and date, the DS3231 has range of neat features such as automatic leap year correction, two settable time-of-day alarms, and a precise square wave output which you could use in precision timing circuits.
You might've also noticed a smaller 8-pin chip on the RTC module we gave you. This is the AT24C32, a small EEPROM memory chip which we can use to store data (such as user settings, calibration values, etc.) to be retained when we turn our MCU on and off. We won't be using it for this project.
The RTC module uses a single I2C bus (a common serial communication protocol) to communicate with an MCU. Both the DS3231 and AT24C32 are connected to the same I2C bus on the module, so if you wanted to use the EEPROM module in a future project, you wouldn't need to add any new connections.
From the image above, we can see our RTC module has six pins:
32K - This is the 32.768kHz precision square wave output from the DS3231, which we mentioned earlier. We won't be using it for this project.
SQW - This is a configurable output on the DS3231, which can either produce a 1kHz precision square wave, or it can produce interrupt signals (an on/off signal we read with a digital input on our MCU) for the two alarms built into the DS3231 (the pin will be set to HIGH when either alarm goes off, and only goes back back low when you send the serial command to reset the alarm). We won't be using it for this project.
SCL - This is the I2C bus clock pin. Connect it to A5 on your Arduino Nano (remember seeing SCL and SDA on the Nano pinout way back in module 1?)
SDA - This is the I2C bus data pin. Connect it to A4 on your Arduino Nano.
VCC - This is the power connection for the RTC module; connect it to 5V.
GND - Ground; it connects to ground.
Now that we know what we're working with, let's build our clock circuit!
This project will use up most (if not all) of your OPS kit jumper wires, depending on how you build it. If you run out, try to think of other ways you can arrange your breadboard to use fewer wires (we promise, it's doable with just 15).
If you're CODA'd into the ECE department, and you've taken the makerspace safety training, you can cut your own jumper wires to use in the ECE Makerspace!
You could even cut custom jumper wires exactly to length so that they'll all lay flat on your breadboard, which can make even highly complicated breadboard circuits clean and easy to understand.
If you want to use the 3D printable case for you clock (3D printer files and instructions are below), then you'll want to arrange your circuit similar to what's shown in the pictures below.
This is so you'll be able to reach the potentiometer and buttons easily inside the edge of the case, have enough slack in the Neopixel wire to reach from the bottom of your clock face to your breadboard, and so that your Arduino's USB connector will line up correctly with the hole in the case.
If you're not able to connect to the RTC module but it's power LED is on, you might have your SDA and SCL wires swapped; SDA should go to A4, and SCL should go to A5.
You might've noticed there are no pull-up or pull-down resistors connected to the buttons. That's because our Arduino's MCU actually has built in pull-up resistors inside the chip, which we'll be using for this project. They make wiring much easier, don't they?
We'll enable the built in pull-up resistors in code using pinMode([pin], INPUT_PULLUP);
Note: the ATMega328P microcontroller, which is used in the Arduino Nano, only has built in pull-up resistors; not built in pull-downs. So if you tried to use INPUT_PULLDOWN, it wouldn't do anything. However, other more modern MCUs do have both pull-up and pull-down resistors.
If you want to show off all the animation patterns on your clock, you can connect a jumper from pin 12 to GND to put the clock into demo mode (not shown in the schematic above).
This will cycle to the next animation every 10 seconds. Removing the jumper at any point will stop the automatic animation cycling and leave the clock on whatever animation it's currently on. (Added in V1.1)
Now that the circuit's built, lets program it. Like we promised, you're getting all the code you need to get your clock working. You can download it all below:
Once you've downloaded the code, you'll need to unpack the contents of the OPSClock.zip file into a new folder inside your Arduino projects directory (usually found in your PC's Documents folder). You should have the OPSClock.V1.ino file, LEDHandler.h file, menus.h file, and memTools file all in the same folder; if they're not all there, the Arduino IDE won't be able to find the other .h files when it tries to compile your sketch.
Alternatively, you can create a new sketch in the Arduino IDE, copy all of the contents of the OPSCLOCK.V1.ino file into the skecth, and then use the three dot menu in the top right of the IDE to add the above .h files (the names have to match exactly), then copy paste the contents of those .h files into the IDE as well.
The program has some built in error codes. If your Neopixels light up red followed by some number of white LEDs, that means your clock has entered an error state. Read the code comments in the top of the OPSClock.V1.ino file to see what each error state means and how to debug your circuit accordingly!
Animations list (in the order they're cycled):
None - only illuminate the hour and minute hand pixels
Rainbow - Uniform rainbow color animation
Rainbow Round CW - clockwise rainbow cycle animation
Rainbow Round CCW - counter-clockwise rainbow cycle animation
Rainbow Waves - Waves of color washing across the clock face in random directions
WolfPack Waves - Red and white color waves washing across the clock face
Thunderstorm - Varying shades of blue and gray with occasional flashes of lightning
Fireplace - Firelight emanating from the bottom with varying shades of red and orange
Aurora Borealis - Northern lights with varying shades of green, cyan, and purple
Xmas Wreath - A green wreath with twinkling red lights
Halloween - Orange and purple lights with occasional flashes of green
The potentiometer controls the LED brightness.
The three buttons allow you to select between lighting patterns and navigate menus to set the time and date of your clock:
Select button:
When not in a menu, long press it (hold the button down for about a second) to enter the time and date menu, or short press it to toggle the clock face pixels (added in V1.1)
If you're in a menu, short pressing the select button will cycle between settings, and long pressing the select button will save your settings and exit the menu
Down button
Short pressing it when not in a menu will cycle to the next clock animation
Short pressing it in a menu will increase the current setting by one
Long pressing it in a menu will increase the current setting by ten
Up button
Short pressing it when not in a menu will cycle to the previous clock animation
Short pressing it in a menu will decrease the current setting by one
Long pressing it in a menu will decrease the current setting by ten
The Time menu:
Short pressing the select button will cycle through the following settings (in order):
Hour selection (the LEDs will blink out the current hour, and be the color of the hour hand pixel)
Minute selection (the LEDs will blink out the current minute, and be the color of the minute hand pixel)
Second selection (the LEDs will quickly blink out the current second, and be white)
Year (LEDs are red and blink out the last two digits of the year)
Month (LEDs are green)
Day (LEDs are blue) (Short pressing mode again here cycles back to Hour again)
Long pressing the select button at any point will save the current time and date you have selected into the RTC module and then return to the clock face animation.
If you don't feel like trying to read through all the code and figure out what it's doing, here are some quick ways to modify the program to customize the clock to your liking:
If you want to change the color of the hour and minute hand pixels
Locate the int hourColor[3] and int minuteColor[3] arrays inside of LEDHandler.h (they're inside the Clock class definition)
Change the three array values to the red/green/blue value of whatever color you want
If you want the clock to dim, turn off animations, or turn off completely at night:
Locate the nightSetting variable inside of LEDHandler.h (inside the Clock class definition)
Change it from on to either dim, noAnim, noAnimDim, or off
If you want the clock to dim, you can change the amount it dims to (0-255) by changing nightDimValue (just below the nightSetting variable)
If you want to change the nighttime hours for the clock:
Inside the if statement toward the end of the setup() function in the sketch file you will find two sets of variables, one set for when the month is between October (10) and March (3), and another for the rest of the year. For each set of variables:
Change the clock.nightTime.startHour to whatever you want night mode to start in the evening (the hour should be 0-24, not 0-12)
Change the clock.nightTime.endHour and to whatever time you want the night mode to stop in the morning
If you want to change the default animation that plays when the clock starts:
Locate animationMode currentAnimation inside of LEDHandler.h
Change it to whatever animation you prefer (you can find a list of the animation names in the animationMode enum near the top of the Clock class)
If you want to change something else:
Most of the configuration for the clock face and it's animations is in the top of the Clock class definition in LEDHandler.h
More general settings variables such as noise filtering for the potentiometer, how long to hold a button for long presses, and etc. are located above the setup() function.
Read the comments to see what each variable controls and experiment with them until you're happy
This project uses two external libraries, which you'll have to install. The Adafruit Neopixel library and the Adafruit RTCLib library. To install these, click the library manager button on the left panel of the Arduino IDE, then search for "adafruit neopixel" and "adafruit rtclib" then click the install button next to each one. You might have to scroll some through the search results to find the correct ones.
Once the libraries are installed, the compiler should have no trouble finding them when it reads the #include <Adafruit_Neopixel.h> and #include <RTCLib.h> lines in the sketch. If you get any compiler errors on these lines, go back and make sure you installed the right libraries.
These are the libraries you're looking for:
If you attended the bonus meeting on November 14th, you might recall us showing off some new software for programming microcontrollers. You don't need to install or use any of it to get this project up and running (the clock code will compile and upload just fine using the Arduino IDE), but here's more information if you're interested.
The software Will was using is Microsoft VSCode code (which you may see again in ECE 209 and 309). By itself, VSCode is only a code editor; it doesn't have the rest of the tools needed to make it a full IDE. For that, we use the PlatformIO extension which adds various compilers and upload tools allowing us to compile code for lots of different microcontrollers. Finally, VSCode also supports the GitHub Copilot extension, which gives you AI code autocompletion and an AI assistant right there in your programming environment to help debug issues and speed the development process. These are all very powerful tools, and can help you create far more advanced projects than what we've done in OPS so far. Will developed the code for the clock using this software.
Both VSCode and PlatformIO are completely free, and GitHub Copilot is free for students once you link your student ID with a GitHub account (note: it won't work with the NCSU GitHub account you used for E115; you have to make a regular GitHub account). You can find more information on how to install and set all of this stuff up via the links above, and there are lots of tutorials about each of them on YouTube as well.
The clock face is fairly simple to build, with one important caveat. Simply tape your Neopixel string around the edge of your foamboard disc, then (optionally) glue a clock dial onto the center of the foamboard. You might notice your LED's don't perfectly align with the hour marks on the clock face dial, if they don't, carefully remove the tape and move the LEDs left or right, re-taping them one by one, until they match up.
If you're using a white clock face, you can face the LEDs outward so that they'll illuminate the wall and area around the clock, or inward so that they'll diffuse through the foamboard. If you're using black foamboard, you'll need to face the LEDs outward.
The important caveat is this:
Make sure your Neopixel string starts with its first LED on the 6-o-clock tick, and wraps around the clock face clockwise (making the 5-o-clock tick the last LED in the string).
You can make your own!
All you need is to cut out a 370mm diameter circle (if you're using a circle cutting tool, set the radius to 185mm) into some 4mm (or 1/4 inch) foam core board. In freedom units, that's 14.57 inches in diameter (7.28 inch radius).
It's very important that your disc's diameter match these measurements as closely as possible so that the Neopixel string will align correctly once it's wrapped around the edge. The diameter being too small or too large will result in the LEDs not correctly aligning with the hours on the clock face.
Here is a PDF containing various clock faces you can print out and use. Note, the faces we brough in class (and this PDF) were printed on 11x17 paper, but if you don't have access to a large format printer and don't mind your clock dial being slightly smaller, you can just print out whichever face you want on normal paper by telling it to shrink the pages to fit when you print from this PDF.
We've designed a 3D printable case for this project that your breadboard can slide into, with a slot on top for holding your clock stand.
If you haven't already, you can do the Hill Library Makerspace training module for free online (even if you aren't CODA'd) via REPORTER. That will grant you access to use the 3D printers there to print your case (printing is free to students). Or, if you are CODA'd into the ECE department, you can complete the ECE MakerSpace 3D printer training (after you have completed the MakerSpace safety training) and use the printers there instead (also free, but only available ECE students).
Breadboard clock case 3D print files
The case and stand piece should both print exactly the way the files import into the slicing software (with the stand fin laying down flat, and the breadboard case printing on it's back wall.
Your breadboard should slot in between the two rails on the bottom of the case. This is a friction fit, so it may take some effort, but once it's in it shouldn't come out.
If it feels too tight or too loose, you may try scaling the model up/down by a percent or two in the slicing software then re-printing it (or just taping the breadboard in)
The support fin is also a friction fit part; it should slide into the slot on the top of the case and fit snugly. If it doesn't fit, you can try sanding the sides down with sand paper, and if it's too loose you can try gluing it in or wrapping the sides with tape to make it fit tightly.
When you go to place your breadboard in the slots, your Arduino's USB connector will hit the right wall of the case. That's normal; just flex the case wall outward some so that the USB connector will slide in, then it should fit into the groove as you slide the breadboard further back, and eventually it will pop into of the hole on the side of the case.
The slot on top of the case is the same width as standard foam core board (the same stuff your clock face disc is made of), so you can cut out your own stand to fit into the slot if you desire.
Or, you can 3D print the simple stand we provided in addition to the case. The one we provide tilts the clock face back to about 80° to keep your clock balanced so it won't tip forward as easily.
To use the 3D printed stand fin, simply tape or glue the back of your clock to the angled edge of the stand. Make sure everything lines up before you do, and make sure there's enough slack on the end of your Neopixel string to reach from the first LED (at 6-o-clock) down to plug into your breadboard!