Close

MakeTime with friends!

A project log for MakeTime

Arduino-compatible development platform whose primary function is a clock

mihai.cuciucmihai.cuciuc 10/18/2021 at 19:070 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(10, color);
      strip.setPixelColor(11, color);
      strip.setPixelColor(12, color);
      strip.setPixelColor(13, color);
      break;
    case '4':
      for (i = 18; i <= 22; i++)
      {
        strip.setPixelColor(i, color);
      }
      for (i = 6; i <= 12; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '5':
      strip.setPixelColor(18, color);
      strip.setPixelColor(20, color);
      strip.setPixelColor(21, color);
      strip.setPixelColor(22, color);
      strip.setPixelColor(23, color);
      strip.setPixelColor(0, color);
      strip.setPixelColor(1, color);
      for (i = 6; i <= 12; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '6':
      strip.setPixelColor(0, color);
      for (i = 6; i <= 23; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '7':
      strip.setPixelColor(22, color);
      strip.setPixelColor(23, color);
      strip.setPixelColor(0, color);
      strip.setPixelColor(1, color);
      strip.setPixelColor(2, color);
      for (i = 5; i <= 9; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '8':
      strip.setPixelColor(21, color);
      strip.setPixelColor(22, color);
      strip.setPixelColor(23, color);
      strip.setPixelColor(0, color);
      strip.setPixelColor(1, color);
      strip.setPixelColor(2, color);
      strip.setPixelColor(3, color);
      for (i = 9; i <= 15; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '9':
      for (i = 18; i <= 23; i++)
      {
        strip.setPixelColor(i, color);
      }
      for (i = 0; i <= 12; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    default:
      break;
  }
  strip.show();
}

Arduino sketch - Slave

#include <Adafruit_NeoPixel.h>
#include <SoftwareSerial.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

void setup()
{
  pinMode(2, INPUT);
  digitalWrite(2, HIGH);

  strip.begin();
  strip.show();

  Serial.begin(115200);
  while (!Serial)
    ;

  mySerial.begin(57600);
}

void loop()
{
  uint8_t incomingByte;
  static uint8_t used = 0;
  
  if (mySerial.available() > 0)
  {
    incomingByte = mySerial.read();
    
    if (incomingByte == 0)
    {
      used = 0;
    }
    else
    {
      if (used == 0)
      {
        displayDigit(incomingByte, 0x00060606);
        used = 1;
        incomingByte = 0;
      }
    }

    mySerial.write(incomingByte);
  }
}

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(10, color);
      strip.setPixelColor(11, color);
      strip.setPixelColor(12, color);
      strip.setPixelColor(13, color);
      break;
    case '4':
      for (i = 18; i <= 22; i++)
      {
        strip.setPixelColor(i, color);
      }
      for (i = 6; i <= 12; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '5':
      strip.setPixelColor(18, color);
      strip.setPixelColor(20, color);
      strip.setPixelColor(21, color);
      strip.setPixelColor(22, color);
      strip.setPixelColor(23, color);
      strip.setPixelColor(0, color);
      strip.setPixelColor(1, color);
      for (i = 6; i <= 12; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '6':
      strip.setPixelColor(0, color);
      for (i = 6; i <= 23; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '7':
      strip.setPixelColor(22, color);
      strip.setPixelColor(23, color);
      strip.setPixelColor(0, color);
      strip.setPixelColor(1, color);
      strip.setPixelColor(2, color);
      for (i = 5; i <= 9; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '8':
      strip.setPixelColor(21, color);
      strip.setPixelColor(22, color);
      strip.setPixelColor(23, color);
      strip.setPixelColor(0, color);
      strip.setPixelColor(1, color);
      strip.setPixelColor(2, color);
      strip.setPixelColor(3, color);
      for (i = 9; i <= 15; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    case '9':
      for (i = 18; i <= 23; i++)
      {
        strip.setPixelColor(i, color);
      }
      for (i = 0; i <= 12; i++)
      {
        strip.setPixelColor(i, color);
      }
      break;
    default:
      break;
  }
  strip.show();
}

Discussions