Things used in this project

Hardware components:
Img 9622 copy 300x420
O Watch Base Kit
×1

Code

google_eye_watch.inoArduino
Arduino code to display the Googly Eye Watch. Don't blink!
/*
 * Google Eye Watch
 *
 * Demonstrates the use of the... awwww, it's just a silly watch. Two big eyes
 * show the time: the position of the left eye's pupil shows the hour, and the right
 * eye's pupil shows the minute.
 * 
 * Inspired by Mike Mak's Eye Clock: http://www.mikemak.com/mike/main_2.php?id=11
 *
 * 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 also display a round analog clock by J Koger 14 March 2016
 * Modified to just display the hours and minutes as two googly eyes by J Koger 16 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

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.on();                               //Turns TinyScreen display on

  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);
}

// Draw a circle. Slow but works. Taken from the O-Watch TinyScreen tutorial.
// http://www.theowatch.com/learn/o-watch-tinyscreen-programming/learn-tinyscreen-demo/
// 
void drawCircle(int x0, int y0, int radius, uint8_t color)
{
  int x = radius;
  int y = 0;
  int radiusError = 1-x;
 
  while(x >= y)
  {
    //drawPixel(x,y,color);//set pixel (x,y) to specified color. This is slow because we need to send commands setting the x and y, then send the pixel data.
    display.drawPixel(x + x0, y + y0, color);
    display.drawPixel(y + x0, x + y0, color);
    display.drawPixel(-x + x0, y + y0, color);
    display.drawPixel(-y + x0, x + y0, color);
    display.drawPixel(-x + x0, -y + y0, color);
    display.drawPixel(-y + x0, -x + y0, color);
    display.drawPixel(x + x0, -y + y0, color);
    display.drawPixel(y + x0, -x + y0, color);
    y++;
    if (radiusError<0)
    {
      radiusError += 2 * y + 1;
    }
    else
    {
      x--;
      radiusError += 2 * (y - x) + 1;
    }
  }
}


// We could draw a solid circle by drawing an outer circle,
// then one that's one pixel shorter radius inside of it,
// and another inside of it, etc.
//
// But instead we'll just draw lines between all the outline pixels
// we'd otherwise draw.
void drawSolidCircle(int x0, int y0, int radius, uint8_t color)
{
  int x = radius;
  int y = 0;
  int radiusError = 1-x;
 
  while(x >= y)
  {
    //drawPixel(x,y,color);//set pixel (x,y) to specified color. This is slow because we need to send commands setting the x and y, then send the pixel data.
    // display.drawPixel(x + x0, y + y0, color);
    // display.drawPixel(y + x0, x + y0, color);
    // display.drawPixel(-x + x0, y + y0, color);
    // display.drawPixel(-y + x0, x + y0, color);
    // display.drawPixel(-x + x0, -y + y0, color);
    // display.drawPixel(-y + x0, -x + y0, color);
    // display.drawPixel(x + x0, -y + y0, color);
    // display.drawPixel(y + x0, -x + y0, color);

    display.drawLine(x + x0, -y + y0 , x + x0, y + y0, color);
    display.drawLine(y + x0, -x + y0 , y + x0, x + y0, color);
    display.drawLine(-x + x0, -y + y0 , -x + x0, y + y0, color);
    display.drawLine(-y + x0, -x + y0 , -y + x0, x + y0, color);


    y++;
    if (radiusError<0)
    {
      radiusError += 2 * y + 1;
    }
    else
    {
      x--;
      radiusError += 2 * (y - x) + 1;
    }
  }
}


// Find the endpoint of a line from a given start point at a given angle for a given length.
//
// Don't use any floating point math such as the sine() function because it
// takes a long time to run and eats battery life on a computer without
// a floating point processor, like the Arduino Zero in the O-Watch.
// Instead, use a good old-fashioned hard-coded sine lookup table and
// a couple of integer math tricks.
//
// The underlying idea from a geometry standpoint is that we need to figure
// out the equivalent X,Y endpoint of a line that goes from the center of the
// circle (0,0) out to a length of radius 1.0 (the 'unit circle') at a given
// angle and multiply the length of our hand by that X,Y endpoint, then slide
// both the center of the circle and the enpoint over so that they match our actual.
// circle's coordinates.
// The sine and cosine of the unit circle are the X,Y endpoint that we need.
//
// Modified from: https://codebender.cc/example/glcd/clockFace#AnalogClock.cpp

void findRadiusEndpoint(int xStart, int yStart, int angle, int radius, int *xEnd, int *yEnd)

// xStart, yStart are starting point of hand (center of clock face)
// angle is location of hand on dial (0-60)
// radius is length of hand
// xEnd and yEnd are pointers to ints that will be filled in with the endpoint
{
  // Sines for hand positions from 0 to 15 minutes. All of the other
  // hand position sines can be determined by flipping x and y. These
  // are all multiplied by 256 so that we can use integers to store them.
  // Cosines can be also be determined by taking the sine of the angle minus
  // 15 degrees and flipping x and y.
  static int sine[16] = {0, 27, 54, 79, 104, 128, 150, 171, 190, 201, 221, 233, 243, 250, 254, 255};
  int quadrant, x_flip, y_flip;

  // calculate which quadrant the hand lies in
  quadrant = angle/15 ;

  // We could have a bigger table of sine values that handle every hand position from 0 to 60,
  // but we can avoid using that memory by doing some quick reflections and rotations.
  switch ( quadrant )
  {
    case 0 : x_flip = 1 ; y_flip = -1 ; break ;
    case 1 : angle = abs(angle-30) ; x_flip = y_flip = 1 ; break ;
    case 2 : angle = angle-30 ; x_flip = -1 ; y_flip = 1 ; break ;
    case 3 : angle = abs(angle-60) ; x_flip = y_flip = -1 ; break ; 
    default:  x_flip = y_flip = 1; // this should not happen
  }

  *xEnd = xStart;
  *yEnd = yStart;
  // OK, here's the tricky part. Look up the sine for the hand
  // angle, then multiply it by the hand length. Then, because
  // our sine table values are all pre-multiplied by 256 so we
  // can store them as integers, we need to divide by 256 to
  // correct for that. Which is, in integer math, the same as
  // saying that we'll shift the values right by 8 bit positions.
  // Doing a shift-right is faster than doing an integer divide by 256
  // (the compiler is probably smart enough to turn that
  // divide by 256 into a right shift anyway, but let's do
  // an explicit shift because it's cool).
  //
  // The equivalent floating-point call would look something like:
  // xEnd = sine(angle) * radius;
  *xEnd += (x_flip * (( sine[angle] * radius ) >> 8));
  *yEnd += (y_flip * (( sine[15-angle] * radius ) >> 8));
}


void displayTime()
{

  display.on();                               //Turns TinyScreen display on

 
 enum
 {
    leftEyeCenterX = 23,
    leftEyeCenterY = 32,
    rightEyeCenterX = 73, // 96 - 23
    rightEyeCenterY = 32,
    eyeRadius = 22,
    pupilRadius = 16,
    backgroundColor = TS_8b_Blue,
    eyeColor = TS_8b_White,
    pupilColor = TS_8b_Black,
    pupilRadialCenter = eyeRadius - pupilRadius
  };

  display.drawRect(0, 0, 96, 64, 1, backgroundColor); // Give our googly eyes a nice blue background. Cooookeeee!!!
  
  for (int i = 0; i < 2; i++) // Display for 2*5000 milliseconds (10 seconds), update/blink eyes each period
  {
    // The second time around, this full redraw will make the eyes look like they're blinking!
    
    drawSolidCircle(leftEyeCenterX, leftEyeCenterY, eyeRadius, eyeColor);
    drawSolidCircle(rightEyeCenterX, rightEyeCenterY, eyeRadius, eyeColor);

    drawCircle(leftEyeCenterX, leftEyeCenterY, eyeRadius, pupilColor);
    drawCircle(rightEyeCenterX, rightEyeCenterY, eyeRadius, pupilColor);

    // Get the hours and minutes, one for each eye.
    hours = rtc.getHours();
    minutes = rtc.getMinutes();

    // The hour is 0 - 23. But we need it
    // to be an 'angle' of 0 - 59. So
    // convert 24-hour time to 12 hour time,
    // and then multiply by 60 / 12 = 5.
    int hourAngle;
    if (hours >= 12)
      hourAngle = hours - 12;
    else
      hourAngle = hours;
    hourAngle *= 5;
    // To give the hour hand a real
    // analog-clock feel, it shouldn't point
    // straight at the hour, but should move between
    // the hours as the minute hand sweeps around.
    // So, let's nudge it forward a smidge by
    // the minutes scaled down to the space within 1 hour, or
    // divide them by 12.
    hourAngle += minutes / 12;

    int leftPupilCenterX, leftPupilCenterY, rightPupilCenterX, rightPupilCenterY;
    
    findRadiusEndpoint(leftEyeCenterX, leftEyeCenterY, hourAngle, pupilRadialCenter, &leftPupilCenterX, &leftPupilCenterY);
    findRadiusEndpoint(rightEyeCenterX, rightEyeCenterY, minutes, pupilRadialCenter, &rightPupilCenterX, &rightPupilCenterY);
    drawSolidCircle(leftPupilCenterX, leftPupilCenterY, pupilRadius, pupilColor);
    drawSolidCircle(rightPupilCenterX, rightPupilCenterY, pupilRadius, pupilColor);
    
    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);                  //Set display brightness 0 - 15


    delay(5000); //display for 5 seconds
  }

  display.off();                               //Turns TinyScreen display off
}

void loop()
{
   if (display.getButtons())
   {
      displayTime();
   }
}

Credits

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
  • 490
  • 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.

Animated Word Clock on the O-Watch
Easy
  • 401
  • 6

A modified version of J Koger's Mini Word Clock for O-Watch, now with an animated digital rain effect.

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
  • 988
  • 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.

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