Things used in this project

Hardware components:
Img 9622 copy 300x420
O Watch Base Kit
×1
Software apps and online services:
Ide web
Arduino IDE

Code

animated_wordclock.inoArduino
/*
 * Animated Word Watch
 *
 * Demonstrates the use of the Arduino Zero Real-Time-Clock (RTC) library to make a simple 8 x 8 'Word Clock'
 * display of the current hours and minutes.
 * 
 * This type of display only shows the current minutes (to within 5 minutes) and current hour, using a grid of 8 x 8 letters. The letters
 * needed to display the current time (for example, 'HALF PAST TEN') are 'lit up' (drawn with a bright color like yellow), and the
 * unnneeded letters are 'turned off' (drawn with a dull color like gray).
 * 
 * Since the TinyScreen is 64 pixels high, the 8 x 8 grid of letters will take up most of the display, since the
 * smallest font provided on the O-Watch is 7 pixels tall (plus 1 pixel gap between rows): (7 + 1) * 8 = 64 pixels needed 
 * to display 8 rows. That doesn't leave any room for the date, day of the week, or seconds. It's also a bit hard to read
 * since the letters are pretty, well, tiny.
 *
 * Uses Arduino Zero RTC library https://www.arduino.cc/en/Reference/RTC
 * Maintained by Arturo Guadalupi https://github.com/arduino-libraries/RTCZero
 *
 * This code is for the TinyScreen+ board by TinyCircuits used by O Watch http://tiny-circuits.com 
 *
 * This example is based on the Simple Watch code created by O Watch on 6 March 2016 http://theowatch.com
 * Modified to use RTC, to set the display brightness based on time of day, and to use __DATE__ and __TIME__
 * to preset the watch's date and time by J Koger 13 March 2016
 * 
 * Modified to display time as an 8 x 8 character 'Micro Word Clock' by J Koger 16 March 2016, using a tweaked
 * version of the code from Daniel Rojas' https://github.com/formatc1702/Micro-Word-Clock
 * 
 * Modified to display an animated digital rain effect by Bob S on 19 March 2016
 * 
*/

#include <TinyScreen.h> //include TinyScreen library
#include <RTCZero.h> //include the Arduino Zero's Real Time Clock library
#include <stdio.h>  // include the C++ standard IO library

/* Create an rtc object */
RTCZero rtc;

// We'll dynamically change these values to set the current initial time
// No need to change them here.
byte seconds = 0;
byte minutes = 45;
byte hours = 9;

// We'll dynamically change these values to set the current initial date
// The preset values are only examples.
byte days = 13;
byte months = 3;
byte years = 16;

int brightness = 15; // We'll set it, 3 - 15 based on time of day

TinyScreen display = TinyScreen(TinyScreenPlus); //Create the TinyScreen object

// Here we define several colors for the gradient on the droplet trail, from bright to dark
#define DROPLETTRAILLENGTH 9
const uint16_t droplettrail[DROPLETTRAILLENGTH] = { 0x9FF6, 0x07E7, 0x0606, 0x0545, 0x0464, 0x0323, 0x0282, 0x0141, 0x0080 };

// Our colors for letters that are off and
// for letters that are lit.
#define LETTER_OFF  droplettrail[DROPLETTRAILLENGTH-2]
#define LETTER_LIT  droplettrail[1]

// This is the 8 x 8 grid of letters used to display the current time.
// We'll only 'light up' the letters needed to show the current
// time, and draw all the other ones in a 'dark' color.

char wordClock[8][9] = { 
 "HATWENTY",
 "FIFVTEEN",
 "LF*PASTO",
 "NINEIGHT",
 "ONETHREE",
 "TWELEVEN",
 "FOURFIVE",
 "SIXSEVEN"
};

// This is an array used to determine which letters should be 'lit'
// to display the current minutes (or, really, the current 5-minute
// segment. There is one grid for each segment, and since there are 12
// 5-minute segments in an hour, there are 12 grids in the array.
//
// For example, if the current time is 10:06, then it is roughly
// 5 minutes past 10. So, we'll want the letters F I V E and P A S T
// to be lit. Look in the second grid for 'five past'; it has 1's for
// each of the wanted letters and 0's for all of the unused letters.
//
// The lit/unlit letter indicators are stored as bits within bytes
// (a byte holds 8 bits). We could use a byte for each letter, but
// bits only need 1/8 the space. And it'll give us an excuse to have
// some fun with bit comparisons later on!

unsigned char letterMinutes[12][8] ={
  { // o'clock
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  , { // five past
    0B00000000,
    0B11010100,
    0B00011110,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  , { // ten past
    0B00000000,
    0B00001101,
    0B00011110,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // fifteen past
    0B00000000,
    0B11101111,
    0B00011110,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // twenty past
    0B00111111,
    0B00000000,
    0B00011110,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // twentyfive past
    0B00111111,
    0B11010100,
    0B00011110,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // half past
    0B11000000,
    0B00000000,
    0B11011110,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // twentyfive to
    0B00111111,
    0B11010100,
    0B00000011,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // twenty to
    0B00111111,
    0B00000000,
    0B00000011,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // fifteen to
    0B00000000,
    0B11101111,
    0B00000011,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // ten to
    0B00000000,
    0B00001101,
    0B00000011,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // five to
    0B00000000,
    0B11010100,
    0B00000011,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
};

// These are the corresponding
// grids to determine which letters
// should be 'lit' to show the
// current hour, 1 grid for each of
// the 12 hours.

unsigned char letterHours[12][8] ={
  { // twelve
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B11110110,
    0B00000000,
    0B00000000
  }
  ,{ // one
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B11100000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // two
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B11000000,
    0B01000000,
    0B00000000
  }
  ,{ // three
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00011111,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // four
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B11110000,
    0B00000000
  }
  ,{ // five
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00001111,
    0B00000000
  }
  ,{ // six
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B11100000
  }
  ,{ // seven
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00011111
  }
  ,{ // eight
    0B00000000,
    0B00000000,
    0B00000000,
    0B00011111,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // nine
    0B00000000,
    0B00000000,
    0B00000000,
    0B11110000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000
  }
  ,{ // ten
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000001,
    0B00000001,
    0B00000001,
    0B00000000,
    0B00000000
  }
  ,{ // eleven
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00000000,
    0B00111111,
    0B00000000,
    0B00000000
  }
};



void setup()
{
  char s_month[5];
  int tmonth, tday, tyear, thour, tminute, tsecond;
  static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";

  display.begin();                            //Initializes TinyScreen board
  display.setFlip(1);                         //Flips the TinyScreen rightside up for O Watch
  display.setBitDepth(1);                     // allow 16bit colors BGR565
  display.on();                               //Turns TinyScreen display on
  display.fontColor(LETTER_OFF, TS_16b_Black); //Set the font color, font background
  display.setFont(thinPixel7_10ptFontInfo);   //Set the font type


  rtc.begin(); // initialize RTC
  
  // Set the time and date. Change this to your current date and time.
  // setTime(16,19,00,12,3,2016);    //values in the order hr,min,sec,day,month,year

  // Let's be lazy and let the compiler set the current date and time for us.
  // This will be a few seconds off due to the time it takes to compile the
  // .ino file and upload the app. But pretty close.

  // __DATE__ is a C++ preprocessor string with the current date in it.
  // It will look something like 'Mar  13  2016'.
  // So we need to pull those values out and convert the month string to a number.
  sscanf(__DATE__, "%s %d %d", s_month, &tday, &tyear);

  // Similarly, __TIME__ will look something like '09:34:17' so get those numbers.
  sscanf(__TIME__, "%d:%d:%d", &thour, &tminute, &tsecond);

  // Find the position of this month's string inside month_names, do a little
  // pointer subtraction arithmetic to get the offset, and divide the
  // result by 3 since the month names are 3 chars long.
  tmonth = (strstr(month_names, s_month) - month_names) / 3;

  months = tmonth + 1;  // The RTC library expects months to be 1 - 12.
  days = tday;
  years = tyear - 2000; // The RTC library expects years to be from 2000.
  hours = thour;
  minutes = tminute;
  seconds = tsecond;

  rtc.setTime(hours, minutes, seconds);
  rtc.setDate(days, months, years);
  
}

// animated digital rain affect
void displayTime() {

  hours = rtc.getHours();
  minutes = rtc.getMinutes();
  seconds = rtc.getSeconds();
  
  // set brightness
  if (hours <= 12)
    brightness = hours + 3; // 0 hours = 3 brightness, noon = 15
  else if (hours >= 18)
    brightness = (24 - hours) * 2 + 2;  // 23 hours = 4 brightness, 18 hours = 14
  else
    brightness = 15; // full brightness all afternoon
  if (brightness < 3)
    brightness = 3;
  if (brightness > 15)
    brightness = 15;
  display.setBrightness(brightness);       

  display.clearScreen(); 
  display.on();   
  
  // Convert 24-hour time into the 12-hour format for
  // the word clock.
  int wordHours, wordMinutes;
  wordHours = hours;
  if (minutes >= 35)   // After half-past the hour, the minute words start referencing the next hour.
    wordHours++;
  wordHours %= 12;    // Now 0 - 11, which is what we need for lookups in the grid.

  // Convert minutes to 5-minute segments
  wordMinutes = minutes / 5;

  // Get the letter-selection grids for the current hour and 5-minute segment.
  unsigned char *thisLetterMinute = letterMinutes[wordMinutes];
  unsigned char *thisLetterHour = letterHours[wordHours];
                                
  // the droplet in each column starts at a different position, mostly offscreen
  randomSeed(minutes*seconds);
  signed char droplet[8];
  for (int column = 0; column < 8; column++) {
    droplet[column] = - random(2*DROPLETTRAILLENGTH);
  }

  // keep track of how many times a droplet goes offscreen
  unsigned int dropletOffscreen[8] = {0,0,0,0,0,0,0,0};
  // total times droplets go offscreen
  const int dropletRuns = 3;

  bool notdone = true;
  while( notdone ) {
    // move droplets down the rows of the columns
    for (int row = 0; row < 8; row++) 
    {
      unsigned char letterColumn = B10000000;
      for (int column = 0; column < 8; column++, letterColumn = letterColumn >> 1)
      {
        
        // Set the drawing cursor to the position of the current letter.
        display.setCursor((column * 10) + 10, (row * 8));

        if (row <= (droplet[column] - DROPLETTRAILLENGTH)) {     
          // leave a ghost of the unlit letters for the final run, when highlighting the time
          if (dropletOffscreen[column] >= dropletRuns) {
            // Check to see if the current letter should be off or lit.
            if (((thisLetterMinute[row] & letterColumn) == 0) &&
                ((thisLetterHour[row] & letterColumn) == 0)) {
              // ghost of non-used letters
              display.fontColor(LETTER_OFF, TS_16b_Black);
            } else {
              //  highlight the letters needed to display the time
              display.fontColor(LETTER_LIT, TS_16b_Black);
            }
  
          } else {
            // hidden, after the trail
            display.fontColor(TS_16b_Black, TS_16b_Black); 
          } 
        } else if (row <= droplet[column] && row > (droplet[column] - DROPLETTRAILLENGTH)) {
          // set the color for the droplet or its trail
          display.fontColor(droplettrail[droplet[column] - row ], TS_16b_Black);
        } else {
            // hidden, before the head
            display.fontColor(TS_16b_Black, TS_16b_Black); 
        }

        // randomize until the last frames, then display the correct word clock face
        if (dropletOffscreen[column] >= dropletRuns && row < droplet[column]) {
          display.print(wordClock[row][column]);
        } else {
          display.print(wordClock[random(8)][random(8)]);
        }
      }
    }
    
    // move the droplets to the next row, count any that moved offscreen
    for (int column = 0; column < 8; column++) {
      droplet[column] ++;
      
      // if droplet has gone offscreen, and it hasn't completed it's runs, then increment its counter
      if (droplet[column] > (7 + DROPLETTRAILLENGTH)) {
        
        if (dropletOffscreen[column] < dropletRuns) {
          // we have more runs to complete, reset the droplet to the top
          droplet[column] = - random(DROPLETTRAILLENGTH);
        }
        
        // count it
        dropletOffscreen[column] ++;
      } 
      
      // check bounds
      if (dropletOffscreen[column] > dropletRuns){
        // hold the end position
        droplet[column] = (7 + DROPLETTRAILLENGTH);
      }
    }
    
    // if all droplets are done, break from the while loop
    notdone = false;
    for (int column = 0; column < 8; column++) {
      if ( dropletOffscreen[column] <= dropletRuns ){
        // not done yet
        notdone = true;
      }
    }
    
    // delay between frames
    delay(45);
  }

  // hold display of the time for a few seconds
  delay(5000);
  display.off();
}


void loop()
{
   int button = display.getButtons();
   if (button) {
      displayTime();
   }
   delay(10);
}

Credits

Thanks to J Koger.

Replications

Did you replicate this project? Share it!

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Word Clock on the O-Watch
Easy
  • 493
  • 9

Protip

We'll build on the Simple RTC Watch to turn our O-Watch into a Mini Word Clock, which uses an 8 x 8 grid of letters to spell out the time.

Getting Started with O Watch
Easy
  • 516
  • 8

Full instructions

Assemble the O Watch, configure the Arduino IDE and load your first program.

Simple Watch Using RTC
Easy
  • 992
  • 4

Full instructions

The O-Watch Simple Watch code tweaked to use the Arduino Zero's built-in RTC, plus a couple other tweaks.

2048 Sliding Tile Game for the O Watch
Easy
  • 241
  • 4

Full instructions

This is a sliding tile game for the O Watch based on the simple and addicting game 2048 by Gabriele Cirulli.

Googly Eye O-Watch
Easy
  • 96
  • 2

Protip

Inspired by Mike Mak's Eye Clock, now the O-Watch can show the time with two googly eyes: hours (left eye) and minutes (right eye).

Measuring Humidity and Temperature with the O Watch
Easy
  • 240
  • 3

Learn how to measure the humidity and temperature on your O Watch.

ProjectsCommunitiesContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login
Respect project
Feedback