So, I bought a 24 LED Neopixel Ring and was trying to decide what to do with it. I had originally planned on using the ring for a Cheerlights project but after making a Cheerlight out of it I decided this was a terrible waste of the ring. So, one weekend when visiting my local Makerspace (Medway Makers in the UK) I decided I would make a clock out of it.
What you'll needThe ESP8266 is ideal for this as has wifi so the time can be obtained over WiFi and it can also control the Neopixel Ring. I use the Wemos D1 Mini as they are very simple to use, have a micro-USB connection for power and data, cost only a few bucks and can be used very easily with the Arduino IDE. This is a very simple project with simple code and not much in the way of a circuit.
The Neopixel ring has 3 connections: 5v, Ground and Data. All you need to do is to solder 3 wires to these 3 pads on the ring. VCC and Gnd go to 5v and Ground on your D1 Mini and the Data pin can go to any output pin.
For the code you will need 3 libraries:
#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include "TimeClient.h"
The Adafruit_NeoPixel library can be downloaded from https://github.com/adafruit/Adafruit_NeoPixel
The TimeClient library is part of the Squix Weather Station code and can be downloaded from https://github.com/squix78/esp8266-weather-station
Just copy the TimeClient.cpp and TimeClient.h files into your sketch folder.
The TimeClient library works by connecting to the internet via WiFi and going to www.google.com. It then scrapes the header of the web page to obtain the time and date from the header.
The ESP8266WiFi library come with the Arduino IDE when you install the ESP8266 as a recognised board. Don't forget to install the ESP8266 files using the Arduino IDE Boards Manager before you start your project.
Step 1We will use Pin 5 on the D1 Mini for the data line.
#define PIN D5
We need to keep track of, in our code, when the last update of the time from the internet takes place and also how long it has been since one second ago (to keep track of seconds).
long lastUpdate = millis();
long lastSecond = millis();
The hours, minutes and seconds are stored in a String variable.
String hours, minutes, seconds;
As well as the current second, minute and hour which we keep track of.
int currentSecond, currentMinute, currentHour;
Character arrays are used to store your WiFi SSID and Password. Enter your own details here:
char ssid[] = "xxxxxxx"; // your network SSID (name)
char pass[] = "xxxxxxx"; // your network password
I live in London so I have 0 hours offset from UTC, except in Summer Time when it will be 1 hour. Set the offset for your own time zone.
const float UTC_OFFSET = 0;
We create a timeClient object and pass it the UTC offset.
TimeClient timeClient(UTC_OFFSET);
Then create a NeoPixel object, call it 'strip' and pass it the number of LED's and what pin we are using for the data line.
Adafruit_NeoPixel strip = Adafruit_NeoPixel(24, PIN);
Step 2In the setup function we will start off by opening serial comms for debug purposes.
void setup()
{
Serial.begin(115200);
Then the NeoPixel ring needs to be initialised and we set brightness to 50%
strip.begin();
strip.setBrightness(128);
Nothing happens on the ring until we do a .show command.
strip.show();
Next we connect to your WiFi:
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
Then update the time from the timeClient library.
timeClient.updateTime();
Next we run our own function called updateTime(); which calls the library functions to get the current hours, minutes and seconds.
updateTime() ;
Finally, before the main loop, we retrieve and store the current value of millis() so we can track of how long it has been since we last updated the time from the internet and also keep track of the seconds as they pass by so the second 'hand' can be updated.
lastUpdate = millis();
lastSecond = millis();
}
Step 3The setup function is complete. Next the main loop function will run.
void loop()
{
If 1,800,000 milliseconds (30 minutes) have passed by since we last updated the time, we update the time again from the internet.
if ((millis() - lastUpdate) > 1800000) updateTime();
The hours, minutes and seconds are updated on the display every second so we need to check if 1000 milliseconds have passed since we last updated the display. If so the code within the if statement is carried out.
if ((millis() - lastSecond) > 1000)
{
Step 4The colors of the 'hands' are:
- RED = Hours
- GREEN = Minutes
- BLUE = Seconds
The .setPixelColor command is used to set the colours of the 'hands'. The ring I used had 24 RGB LED's so the seconds and minutes get divided by 2.5 so they are displayed on the correct quadrant of the ring, the hour (24 hour format) gets divided by 2. Before the time is changed we set the current 'hands' to OFF to clear the last positions.
strip.setPixelColor(currentSecond / 2.5, 0, 0, 0);
strip.setPixelColor(currentMinute / 2.5, 0, 0, 0);
strip.setPixelColor(currentHour * 2, 0, 0, 0);
strip.show();
The current value of millis() is now stored.
lastSecond = millis();
Next, the value stored in currentSecond is increased by 1. We then check if the seconds have gone over 59 and if so set them back to 0. When that happens we also increase the minutes by 1 and do the same check. If the minutes go over 59 then we also increase the hour by 1.
currentSecond++;
if (currentSecond > 59)
{ currentSecond = 0;
currentMinute++;
if (currentMinute > 59) {
currentMinute = 0;
currentHour++;
if (currentHour > 12) currentHour = 0;
}
}
Although not necessary, I print out the current time to the serial monitor window for debugging purposes. I use a String object to generate the time string.
String currentTime = String(currentHour) + ':' + String(currentMinute) + ':' + String(currentSecond);
Serial.println(currentTime);
Now that the seconds, minutes and hours have been updated we can set the relevant 'hands' to their respective RGB values.
strip.setPixelColor(currentSecond / 2.5, 0, 0, 255);
strip.setPixelColor(currentMinute / 2.5, 0, 255, 0);
strip.setPixelColor(currentHour * 2, 255, 0, 0);
strip.show();
Finally we create our own function called updateTime(). This gets updated at the start and then again every 30 minutes after to keep accurate time.
void updateTime()
{
We obtain the hours, minutes and seconds from the timeClient library.
hours = timeClient.getHours();
minutes = timeClient.getMinutes();
seconds = timeClient.getSeconds();
and store those values as integers (They are returned as strings from the timeClient library). 24 hours are converted to 12 hour format.
currentHour = hours.toInt();
if (currentHour > 12) currentHour = currentHour - 12;
currentMinute = minutes.toInt();
currentSecond = seconds.toInt();
Finally, we want to be able to check that 30 mins have passed since the last update so we store the current value of millis() again.
lastUpdate = millis();
My clock is currently just a prototype on a breadboard, as you can see in the image. But this would look great underneath some etched white acrylic so I challenge you to use the code above to create your own ring clock and make something beautiful out of it.
Comments