Close
0%
0%

MakeTime

Arduino-compatible development platform whose primary function is a clock

Public Chat
Similar projects worth following
MakeTime is a clock that uses a ring of 24 RGB LEDs to display time. Hours and minutes are displayed using different colors. As it uses just 24 LEDs, its granularity is 2.5 minutes. The time can be set using its single button, located in the center of the clock face.

MakeTime is also an Arduino-compatible development platform, and is designed with hacking in mind. In addition to the addressable LEDs, the button and the DS3231 RTC, you can access many of the Arduino pins on an optional expansion header.

Time - it is our most precious resource. We invest some of it everytime we take a decision: to meet friends, to cook, to spend time with our loved ones, to work late, to watch a movie or to go for a run. And yet unlike our savings account it sometimes feels like the more of it we spend, the more of it we have. We MakeTime.

MakeTime can be simple: it shows you the time. Much like on an analog clock you read the time on a ring, with colours showing hours and minutes. Its 24 LEDs dynamically adapt to the surrounding light level, being perfectly visible in direct sunlight while staying discreet in the dark. Since it only uses 24 LEDs its resolution for showing minutes is decreased to 2.5 minutes, making it easier to read time at a glance and giving you more time to focus in a world surrounded by blinky lights competing for your attention. MakeTime is preloaded with many colour themes to make it your own.

User Interface

All interactions with MakeTime happen through the single button on the clock face. Three actions are recognized:

 - click (less than 1 second)

 - press (more than 1 second, less than 10 seconds)
 - factory defaults (more than 10 seconds)

To set the time you press the button and then you can click to advance the hour. Pressing the button again allows you to set the minutes, as in the picture below. The picture shows the setting for 10:14.

Pressing the button again in the minutes menu allows you to change colour themes. All these menus have timeouts of 5 seconds during which if no action is taken you get back to the normal clock face, with your settings confirmed.

Arduino compatibility

MakeTime can be complex: it is an Arduino-compatible development platform that you can hack to implement as much functionality as you need. Your freedom to hack and expand is not limited to software, though. The available schematics and the expansion header that can be populated on the PCB allows you to add sensors, communication and actuators to your heart's desire. There are two versions of the 3D model for the back, one of which provides access to the expansion header for prototyping. Project updates show different design ideas with sample code for teaching MakeTime new tricks.

You will need to program the Arduino bootloader for the ATmega328P before connecting via USB to the Arduino environment. Use the following fuse bits:

 - Extended: 0xFC

 - High: 0xDA

 - Low: 0xFF

Hardware

MakeTime has a custom PCB that is Arduino-compatible, i.e. it uses the ATmega328P and a CH340 USB-UART bridge. Timekeeping is done by a battery backed-up DS3231, while the time is shown using WS2812 addressable LEDs. There's also a buzzer and an LDR that allows brightness to be lowered in low-lighting conditions. The orange "blue wire" was fixed in the latest revision of the PCB.

Enclosure

The case is designed in FreeCAD and can easily be 3D printed. I used 2 M3x5 screws for fixing the PCB to the case and 4 M3x8 screws for the back of the case.

Design files

The complete hardware project with schematic, PCB, BOM and all that is available on EasyEDA. Most of the components are on the front side because JLCPCB only allows assembly of components on one side. Thus you can order the PCBs there and have them mostly populated when they arrive.

PCB files, firmware and case sources can be found on Git.

Timelapse video

  • 1 × CR2032-BS-6-1 battery holder
  • 1 × SFN-12055PA6.5 Buzzer
  • 1 × U-D-M5DD-W-1 Micro-USB socket
  • 1 × MF-NSMF035X-2 Fuses and Accessories / PTC Resettable Fuses
  • 30 × 100nF 0603

View all 22 components

  • Adding sensors: Air Quality

    mihai.cuciuc10/23/2021 at 13:47 0 comments

    Adding a CCS811 sensor, in addition to the SHT31 MakeTime can indicate equivalent total VOC (eTVOC) levels.

    Sensor readings are transformed to ug/m^3 and the full scale displayed reads from 0 to 1000 ug/m^3. The higher the reading, the more the display color shifts from green to red.

    Arduino sketch

    #include <Adafruit_NeoPixel.h>
    #include <Wire.h>
    #include "Adafruit_CCS811.h"
    #include "Adafruit_SHT31.h"
    
    
    #define LED_PIN    9
    #define LED_COUNT 24
    
    Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
    Adafruit_CCS811 ccs;
    Adafruit_SHT31 sht31 = Adafruit_SHT31();
    
    
    void setup()
    {
      Serial.begin(9600);
    
      strip.begin();
      strip.show();
    
      if (! sht31.begin(0x44))   // Set to 0x45 for alternate i2c addr
      {
        strip.setPixelColor(12, 0x00800000);
        strip.show();
        while (1) delay(1);
      }
    
      if(!ccs.begin())
      {
        strip.setPixelColor(12, 0x00008000);
        strip.show();
        while(1);
      }
    
      while(!ccs.available());
    }
    
    // https://help.atmotube.com/technical/13-atmotube-ppm-ug/
    // https://myhealthyhome.info/assets/pdfs/TB531rev2TVOCInterpretation.pdf
    
    void loop()
    {
      float t = sht31.readTemperature();
      float h = sht31.readHumidity();
      float voc;
      uint32_t color, displayColor;
      uint8_t vocLevel, i;
    
      voc = 0;
      if(ccs.available())
      {
        if(!ccs.readData())
        {
          ccs.setEnvironmentalData(h, t);
          voc = ccs.getTVOC() * 100 * 0.0409; // [ug/m^3]
          Serial.print("TVOC [ug/m^3]: ");
          Serial.println(voc);
          vocLevel = round(voc / (1000.0/19));
          
          strip.clear();
          for (i = 0; i < 19; i++)
          {
            if (voc < 200) displayColor = 0x00003000;
            else if (voc < 300) displayColor = 0x00082800;
            else if (voc < 400) displayColor = 0x00102000;
            else if (voc < 500) displayColor = 0x00181800;
            else if (voc < 600) displayColor = 0x00201000;
            else if (voc < 700) displayColor = 0x00280800;
            else displayColor = 0x00300000;
            
            color = 0x00101010;
            if (vocLevel > i) color += displayColor;
            strip.setPixelColor((i + 15) % 24, color);
          }
          strip.show();
        }
      }
    
      delay(0.5);
    }

  • MakeTime with friends!

    mihai.cuciuc10/18/2021 at 19:07 0 comments

    How about using multiple MakeTime units into a single display? Let's show the clock on 4 units, where each displays a single digit. This will require one of the units to act as a master, holding the time and displaying one of the digits, and the other three will be slaves and receive what they should display. Unfortunately having all LEDs on a circle makes for some very approximate fonts. Here's a timelapse running from 09:00 to 10:00.

    Hardware

    MakeTime has the expansion header on the back, giving access to some of the Arduino pins. +5V and GND are also present on this header, so we can steal power from the master unit to deliver to the other three. For the protocol I used a software UART (using the Arduino SoftwareSerial library) that I daisy-chained the software serial ports, using pin D3 for TX and D10 for RX. Below is a diagram of which pins are easily accessible on the back for extension. On GitHub there is an alternate back piece for the case that provides access to these pins.


    The whole 4-digit clock with the connecting wires is represented in the following schematic.

    The time shown in this example is 17:34

    Protocol

    The master transmits a zero to signal that data is coming and then the character corresponding to each of the 3 digits the slaves should display. Each slave displays the first character it receives after one or more zeroes have been received. It then forwards all bytes, but changes the byte it displayed into a zero. Thus, to display 17:34 the master displays '1' and transmits:

    0x00, 0x37, 0x33, 0x34

    The first slave receives this, displays the first char after 0x00 which is 0x37 ('7') and retransmits:

    0x00, 0x00, 0x33, 0x34

    The second slave displays '3' and retransmits:

    0x00, 0x00, 0x00, 0x34.

    Arduino sketch - Master

    #include <Adafruit_NeoPixel.h>
    #include <Wire.h>
    #include <SoftwareSerial.h>
    #include <ds3231.h>
    
    #define LED_PIN    9
    #define LED_COUNT 24
    
    Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
    
    SoftwareSerial mySerial(10, 3); // RX, TX
    
    ts t; //ts is a struct findable in ds3231.h
    uint8_t hh, mm;
    
    #define DIGIT_DISPLAY_MS    750
    #define DIGIT_GAP_MS        100
    #define HM_GAP_MS           750
    #define REPEAT_MS           2000
    
    
    void setup()
    {
      pinMode(2, INPUT);
      digitalWrite(2, HIGH);
    
      strip.begin();
      strip.show();
    
      Wire.begin(); //start i2c (required for connection)
      DS3231_init(DS3231_INTCN); //register the ds3231 (DS3231_INTCN is the default address of ds3231, this is set by macro for no performance loss)
    
      Serial.begin(115200);
      while (!Serial)
        ;
    
      mySerial.begin(57600);
    }
    
    void loop()
    {
      if (digitalRead(2) == 0)
      {
        // I was in a rush. If you click the button it sets the hardcoded time, here 21:30
        DS3231_get(&t);
        t.hour = 21;
        t.min = 30;
        DS3231_set(t);
        delay(1000);
      }
      
      runClock();
      showClock();
    }
    
    void showClock()
    {
      static unsigned long lastRefresh = 0;
    
      if ((millis() - lastRefresh) >= 500)
      {
        strip.clear();
        displayDigit('0' + hh/10, 0x00060606);
        strip.show();
        mySerial.write((uint8_t)0);
        delay(10);
        mySerial.write((uint8_t)('0' + (hh%10)));
        delay(10);
        mySerial.write((uint8_t)('0' + (mm/10)));
        delay(10);
        mySerial.write((uint8_t)('0' + (mm%10)));
        lastRefresh = millis();      
      }
    }
    
    
    void runClock()
    {
      static unsigned long lastRefresh = 0;
    
      if ((millis() - lastRefresh) >= 500)
      {
        lastRefresh = millis();
    
        DS3231_get(&t);
        hh = t.hour;
        mm = t.min;
      }
    }
    
    void displayDigit(uint8_t d, uint32_t color)
    {
      uint8_t i;
      strip.clear();
      switch(d)
      {
        case ' ':
          break;
        case '0':
          for (i = 0; i < 24; i++)
          {
            strip.setPixelColor(i, color);
          }
          break;
        case '1':
          strip.setPixelColor(0, color);
          strip.setPixelColor(12, color);
          break;
        case '2':
          for (i = 3; i <= 9; i++)
          {
            strip.setPixelColor(i, color);
          }
          for (i = 15; i <= 21; i++)
          {
            strip.setPixelColor(i, color);
          }
          break;
        case '3':
          strip.setPixelColor(23, color);
          strip.setPixelColor(0, color);
          strip.setPixelColor(1, color);
          strip.setPixelColor(2, color);
          strip.setPixelColor(3, color);
          strip.setPixelColor(6, color);
          strip.setPixelColor(9, color);
     strip.setPixelColor(...
    Read more »

  • Breadboard prototype

    mihai.cuciuc10/12/2021 at 18:37 0 comments

    Don't feel like committing to getting PCBs made and then soldering a handful of SMD components? How about trying it out on a breadboard first? After all, this is how it all started.

    You'll need:

     - 1x Arduino Nano

     - 1x DS3231 RTC module

     - 1x Addressable LED ring with 24 LEDs

     - 2x 10 kOhm resistors

     - 1x Pushbutton

     - 1x Light dependent resistor (LDR)

     - Breadboard, wires, soldering iron (for the LED ring as it doesn't come with anything connected to the pads)

    Use the firmware from GitHub

    Wiring diagram and a picture of what you can expect to get:


  • Adding sensors: Temperature and Humidity

    mihai.cuciuc10/10/2021 at 12:09 0 comments

    On the back of the PCB there is an expansion header that can be populated to add functionality. Adding an SHT31 allows MakeTime to measure temperature and humidity.

    The display is divided into 3 sections.

     - On the left, the temperature is shown on 10 LEDs. The first LED turns on above 16 degrees Celsius and the last one above 34 degrees Celsius.

     - On the right, humidity is shown with 10% granularity.

     - At the top a single LED turns Green/Yellow/Red to indicate comfort level, loosely based on the information in this article https://www.azosensors.com/Article.aspx?ArticleID=487


    Arduino sketch

    #include <Adafruit_NeoPixel.h>
    #include <Wire.h>
    #include "Adafruit_SHT31.h"
    
    
    #define LED_PIN    9
    #define LED_COUNT 24
    
    Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
    Adafruit_SHT31 sht31 = Adafruit_SHT31();
    
    void setup()
    {
      strip.begin();
      strip.show();
    
      if (! sht31.begin(0x44))   // Set to 0x45 for alternate i2c addr
      {
        strip.setPixelColor(12, 0x00800000);
        strip.show();
        while (1) delay(1);
      }
    }
    
    void loop()
    {
      float t = sht31.readTemperature();
      float h = sht31.readHumidity();
      uint32_t color;
      uint8_t r, b, i;
    
      if (! isnan(t))
      {
        r = round((t - 15) / 2);
      } else { 
        r = 0;
      }
      if (! isnan(h))
      {
        b = round(h / 10);
      } else { 
        b = 0;
      }
      
      strip.clear();
    
      // Draw temperature
      for (i = 0; i < 10; i++)
      {
        color = 0x00040404;
        if (r > i) color += 0x00200000;
        strip.setPixelColor(i + 13, color);
      }
    
      // Draw humidity
      for (i = 0; i < 10; i++)
      {
        color = 0x00040404;
        if (b > i) color += 0x00000020;
        strip.setPixelColor(11 - i, color);
      }
    
      // Comfort level:
      // https://www.azosensors.com/Article.aspx?ArticleID=487
      // T in [20 - 26 deg] and RH in [30 - 70 %] - Green
      // T in [19 - 27 deg] and RH in [30 - 70 %] - Yellow
      // T in [20 - 26 deg] and RH in [20 - 80 %] - Yellow
      // else - Red
      color = 0;
      if (t >= 20 && t <= 26 && h >= 30 && h <= 70) color = 0x00002000;
      else if (t >= 19 && t <= 27 && h >= 30 && h <= 70) color = 0x00202000;
      else if (t >= 20 && t <= 26 && h >= 20 && h <= 80) color = 0x00202000;
      else color = 0x00200000;
    
      strip.setPixelColor(0, color);
    
      strip.show();
    
      delay(0.5);
    }

  • Updated PCBs

    mihai.cuciuc10/02/2021 at 11:54 0 comments

    Fixed missing 100nF cap on MCU reset line. This is needed for programming the chip from the Arduino IDE and was the reason there was an orange wire on the original PCBs.

    Added footprints for 2 additional buttons on the back.

  • MakeTime as instrument display

    mihai.cuciuc09/25/2021 at 09:21 0 comments

    You can use MakeTime as an indicator for your PC's performance. Or for anything else, for that matter. Here's an example of it being used as a CPU load monitor

    or you can use more than one to make an instrument cluster

    MakeTime is simply listening on the serial port for what it should be displaying and a python script sends the stats.


    Arduino sketch

    #include <Adafruit_NeoPixel.h>
    
    #define LED_PIN    9
    #define LED_COUNT 24
    Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
    
    
    void setup()
    {
      strip.begin();
      strip.show();
      Serial.begin(115200);
      Serial.setTimeout(100);
    }
    
    void loop()
    {
      String s;
      int r, g, b, i;
      uint32_t color;
      s = Serial.readString();
      if (s != "")
      {
        sscanf(s.c_str(), "%d %d %d", &r, &g, &b);
        strip.clear();
        for (i = 0; i < 19; i++)
        {
          color = 0x00101010;
          if (r > i) color += 0x600000;
          if (g > i) color += 0x006000;
          if (b > i) color += 0x000060;    
          strip.setPixelColor((i + 15) % 24, color);
        }
        strip.show();
      }
    }

     
    Python script for CPU load monitor

    import serial
    import psutil
    
    serialPort = serial.Serial("COM6", 115200, xonxoff=False, rtscts=False, dsrdtr=False)
    
    while (1):
        cpu_percent = psutil.cpu_percent(interval=0.2)
        str = "%d 0 0" % int(cpu_percent*19 / 100.0)
        bstr = bytearray(str)
        serialPort.write(bstr)
        print cpu_percent, bstr
    
    
    serialPort.close()

View all 6 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates