{"id":839,"date":"2014-09-28T20:24:03","date_gmt":"2014-09-29T03:24:03","guid":{"rendered":"http:\/\/inkofpark.wordpress.com\/?p=839"},"modified":"2024-09-01T20:00:06","modified_gmt":"2024-09-02T02:00:06","slug":"the-giving-tree-and-an-arduino-clock","status":"publish","type":"post","link":"https:\/\/www.inkofpark.com\/?p=839","title":{"rendered":"The Giving Tree and An Arduino Clock"},"content":{"rendered":"<p>When we bought our house we hired an electrician to install ceiling fans and lights. He worked, worked, and worked. My illusions of electrician&#8217;s work dissolved. Their work is a great deal more like laying bricks than solving equations. Oddly, the truth for electricians is also the truth for electronics. <\/p>\n<p>Nearly two years after starting, more than 18~months after finishing the electronics, source code, and cardboard mock-up I finally finished my <em>clock<\/em>. I have recreated&#8212;nay, improved upon&#8212;the greatest alarm clock I have ever owned.<\/p>\n<p><a href=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20120804-pano-backyard2.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image:none;border-bottom:0;border-left:0;margin:5px;padding-left:0;padding-right:0;display:inline;border-top:0;border-right:0;padding-top:0;\" title=\"20120804-Pano-Backyard2\" border=\"0\" alt=\"20120804-Pano-Backyard2\" src=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20120804-pano-backyard2_thumb.jpg\" width=\"644\" height=\"196\" \/><\/a><\/p>\n<p>In some ways my clock was started about thirty years ago, when some foresighted person planted a lovely little apple tree in what is now my back yard. The lovely little apple tree was a gorgeous mature giant shady apple tree until the spring of 2012. The spring came warm and early but also trisected by two severe cold spells. The frost killed several of my trees major limbs, and may eventually kill the tree. <\/p>\n<p>One of the major limbs became firewood. Then one of the firewood logs became boards, and finally one of the boards became the main faces of the clock. Making a board from firewood is, no doubt, ancient. For me it was a new experience with old techniques. I used a hand powered bow saw, affixed the firewood in the vice, and ripped boards by hand. It is a tiresome process and the boards were not machined to the parallel faces you get in dimensioned lumber. No matter, hand planes helped me get one face smooth and flat. I marked a constant thickness, flipped the board over, and planed the uneven face. The finished board was smooth, clean, and beautifully figured.<\/p>\n<p>The original vision for the box of the clock had hand-cut dovetails visible on the front. However, the apple wood was unsupportive. It was brittle, and prone to fracturing on detail peices. It was also almost too small. The cherry boards I selected for the sides were not long enough to dovetail join the front and back.<\/p>\n<p><a href=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/clock_hand_drawing.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image:none;border-bottom:0;border-left:0;margin:5px;padding-left:0;padding-right:0;display:inline;border-top:0;border-right:0;padding-top:0;\" title=\"clock_hand_drawing\" border=\"0\" alt=\"clock_hand_drawing\" src=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/clock_hand_drawing_thumb.jpg\" width=\"469\" height=\"317\" \/><\/a><\/p>\n<p>The final result is lovely to me. It has a lid so the interface is not visible on the bedside, and the lid showcases the figure of the apple wood. The display is a simple red that does not keep me awake at night. The plain box design weighs enough to keep from migrating around the nightstand, and the interface is actually good.<\/p>\n<p><a href=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-06.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image:none;border-bottom:0;border-left:0;margin:5px;padding-left:0;padding-right:0;display:inline;border-top:0;border-right:0;padding-top:0;\" title=\"20140817-06\" border=\"0\" alt=\"20140817-06\" src=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-06_thumb.jpg\" width=\"464\" height=\"309\" \/><\/a><a href=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-01.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image:none;border-bottom:0;border-left:0;margin:5px;padding-left:0;padding-right:0;display:inline;border-top:0;border-right:0;padding-top:0;\" title=\"20140817-01\" border=\"0\" alt=\"20140817-01\" src=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-01_thumb.jpg\" width=\"464\" height=\"309\" \/><\/a><a href=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-05.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image:none;border-bottom:0;border-left:0;margin:5px;padding-left:0;padding-right:0;display:inline;border-top:0;border-right:0;padding-top:0;\" title=\"20140817-05\" border=\"0\" alt=\"20140817-05\" src=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-05_thumb.jpg\" width=\"464\" height=\"309\" \/><\/a><a href=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-02-2.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image:none;border-bottom:0;border-left:0;margin:5px;padding-left:0;padding-right:0;display:inline;border-top:0;border-right:0;padding-top:0;\" title=\"20140817-02-2\" border=\"0\" alt=\"20140817-02-2\" src=\"http:\/\/www.inkofpark.com\/wp-content\/uploads\/2014\/09\/20140817-02-2_thumb.jpg\" width=\"464\" height=\"309\" \/><\/a>    <\/p>\n<h1>Design and Use<\/h1>\n<p>I set out to imitate the functions of my beloved but deceased clock, see <a href=\"http:\/\/inkofpark.wordpress.com\/2009\/09\/07\/behold_a_number_pad\/\" target=\"_blank\" rel=\"noopener\">Behold! A number pad.<\/a> I started the design with a set of use cases, Set Time, Set Alarm, Turn on Alarm, and Turn off Alarm. The notes below are from the original design before I bought the first part, with no edits but formatting.<\/p>\n<h3>Set Time<\/h3>\n<p>User opens decorative lid. User sets mode switch \u201cset time\u201d mode. The display is turned to current time. User enters numbers starting with most significant digit, in 24 hour time format. After entering the first number, only that digit is displayed.<\/p>\n<p>If user enters a mistake, he hits \u201cclear (#),\u201d and the process restarts at \u201cuser enters numbers\u201d. Clear: display is blanked. To keep the setting, user leaves the \u201cset time\u201d mode. On error, the time remains unchanged. Error conditions are:<\/p>\n<ul>\n<li>User exits &quot;set time&quot; mode without entering a new time at all.<\/li>\n<li>User exits &quot;set time&quot; mode without entering a valid time.<\/li>\n<\/ul>\n<h3>Set Alarm<\/h3>\n<p>Same as \u201cSet Time,\u201d but using \u201cset alarm\u201d mode.<\/p>\n<h3>Turn on Alarm<\/h3>\n<p>User turns on alarm. Display flashes time the alarm is set for about 5 seconds. Display resumes normal operation. The \u201calarm on\u201d LED lights. When the time reaches the set alarm time, a beeper will sound. The beeper will sound until the alarm is shut off.<\/p>\n<h3>Turn off Alarm<\/h3>\n<p>Alarm LED is turned off. If the alarm is currently beeping, the beeping ceases.<\/p>\n<h1>Electronics Design<\/h1>\n<p>I ended up with a Sparkfun keypad, and care of <a href=\"http:\/\/dlnmh9ip6v2uc.cloudfront.net\/datasheets\/Components\/General\/SparkfunCOM-08653_Datasheet.pdf\" target=\"_blank\" rel=\"noopener\">Andrew&#8217;s diligent work<\/a> I was able to configure the Arduino microprocessor to use the keypad almost painlessly. I arranged the pinout as follows:<\/p>\n<table border=\"0\" cellspacing=\"0\" cellpadding=\"2\" width=\"304\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"76\">Pin<\/td>\n<td valign=\"top\" width=\"226\">Use<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">0<\/td>\n<td valign=\"top\" width=\"226\">not available<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">1<\/td>\n<td valign=\"top\" width=\"226\">not available<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">2<\/td>\n<td valign=\"top\" width=\"226\">keypad 0<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">3<\/td>\n<td valign=\"top\" width=\"226\">keypad 1<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">4<\/td>\n<td valign=\"top\" width=\"226\">keypad 2<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">5<\/td>\n<td valign=\"top\" width=\"226\">keypad 3<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">6<\/td>\n<td valign=\"top\" width=\"226\">keypad 4<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">7<\/td>\n<td valign=\"top\" width=\"226\">keypad 5<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">8<\/td>\n<td valign=\"top\" width=\"226\">keypad 6<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">9<\/td>\n<td valign=\"top\" width=\"226\">alarm on\/off switch<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">10<\/td>\n<td valign=\"top\" width=\"226\">alarm tone (PWM)<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">11<\/td>\n<td valign=\"top\" width=\"226\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">12<\/td>\n<td valign=\"top\" width=\"226\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">13<\/td>\n<td valign=\"top\" width=\"226\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">14 (A0)<\/td>\n<td valign=\"top\" width=\"226\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">15 (A1)<\/td>\n<td valign=\"top\" width=\"226\">display brightness potentiometer<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">16 (A2)<\/td>\n<td valign=\"top\" width=\"226\">&nbsp;<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">17 (A3)<\/td>\n<td valign=\"top\" width=\"226\">mode switch (3-state) alarm\/time<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">18 (A4)<\/td>\n<td valign=\"top\" width=\"226\">i2c \u2013 display and realtime clock<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"76\">19 (A5)<\/td>\n<td valign=\"top\" width=\"226\">i2c \u2013 display and realtime clock<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Pin A3 was used for analog read to determine the state of a simple resistor network. Switching the mode switch changes the resistors in a voltage divider. I designed this before I learned about the Arduino&#8217;s internal pull-up resistor, so the analog electronics are more difficult than I would make them today.<\/p>\n<p>In case anyone wants it, the code is here also.   <\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n\n#include &amp;lt;Wire.h&amp;gt;\n#include &amp;quot;Adafruit_LEDBackpack.h&amp;quot;\n#include &amp;quot;Adafruit_GFX.h&amp;quot;\n#include &amp;lt;Keypad.h&amp;gt;\n#include &amp;quot;RTClib.h&amp;quot;\n\nRTC_DS1307 RTC;\nDateTime now;\n\n#define PIN_ALARMSWITCH 9\n#define PIN_ALARMTONE 10\n#define PIN_MODE3STATE A3\n\n#define PIN_BRIGHTNESSPOT A2 \/\/ on the left\n\n#define COLON 2\n#define PM 8\n#define ALARM 4\n#define EXTRA 16\n\nint brightness( void){\n  \/\/ the analog reading is between 0 and 1024, so divide by 64 to get the\n  \/\/ values between 0 and 15\n  return analogRead( PIN_BRIGHTNESSPOT) &amp;gt;&amp;gt; 6; \n}\n\nclass ClockDisp : public Adafruit_7segment {\n  uint8_t alarmOn;\n  uint8_t pmOn;\n  uint8_t colonOn;\n  public:\n  void writeState( void);\n  void alarm( bool);\n  void pm( bool);\n  void colon( bool);\n  void toggleColon( void);\n  void printTime( DateTime now);\n  void blank( void);\n};\n\nvoid ClockDisp::blank( void){\n  print(10000, DEC);\n  writeDisplay();\n}\n\nvoid ClockDisp::writeState( void){\n  writeDigitRaw( 2, alarmOn | pmOn | colonOn );\n  writeDisplay();\n}\n\nvoid ClockDisp::alarm( bool newState ){\n  if( newState){\n    alarmOn = 4;\n  }else{\n    alarmOn = 0;\n  }\n  writeState();\n}\n\nvoid ClockDisp::pm( bool newState){\n  if( newState){\n    pmOn = 8;\n  }else{\n    pmOn = 0;\n  }\n  writeState();\n}\n\nvoid ClockDisp::colon( bool newState){\n  if( newState){\n    colonOn = 2;\n  }else{\n    colonOn = 0;\n  }\n  writeState();\n}\n\nvoid ClockDisp::toggleColon( void){\n  colonOn ^= 2;\n  writeState();\n}\n\nvoid ClockDisp::printTime( DateTime now){\n  \/\/DateTime now = RTC.now();\n  int hour = now.hour();\n  if( hour &amp;gt; 12){\n    pmOn = 8;\n    hour %= 12;\n  } else{\n   pmOn = 0;\n   if( hour == 0) hour = 12;\n  }\n  int decimalTime = hour * 100 + now.minute();\n  print( decimalTime);\n  writeState();\n}        \n        \nconst byte ROWS = 4; \/\/four rows\nconst byte COLS = 3; \/\/four columns\n\/\/define the cymbols on the buttons of the keypads\nchar keys&#x5B;ROWS]&#x5B;COLS] = {\n  {&#039;1&#039;,&#039;2&#039;,&#039;3&#039;},\n  {&#039;4&#039;,&#039;5&#039;,&#039;6&#039;},\n  {&#039;7&#039;,&#039;8&#039;,&#039;9&#039;},\n  {&#039;*&#039;,&#039;0&#039;,&#039;#&#039;}\n};\nbyte rowPins&#x5B;ROWS] = {7,2,3,5}; \/\/connect to the row pinouts of the keypad\nbyte colPins&#x5B;COLS] = {6,8,4}; \/\/connect to the column pinouts of the keypad\n\n\/\/initialize an instance of class NewKeypad\nKeypad customKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS); \n\n\/\/Adafruit_7segment matrix = Adafruit_7segment();\nClockDisp clockDisp = ClockDisp();\nint alarmNum = -1;\nbool isAlarming = 0;\nbool hasAlarmed = 0;\n\nvoid setup(){\n\n  Serial.begin(9600);\n  Serial.println(&amp;quot;Clockit&amp;quot;);\n  \n  Wire.begin();\n  RTC.begin();\n\n  if (! RTC.isrunning()) {\n    Serial.println(&amp;quot;RTC is NOT running!&amp;quot;);\n    \/\/ following line sets the RTC to the date &amp;amp; time this \n    \/\/ sketch was compiled, except on Mac.\n    RTC.adjust(DateTime(__DATE__, __TIME__));\n  }\n  \n  clockDisp.begin(0x70);\n  clockDisp.setBrightness( 10);\n  \n  clockDisp.blank();\n}\n\nvoid loop() {\n  Serial.println(&amp;quot;Top of loop&amp;quot;);\n  now = RTC.now();\n  Serial.print(&amp;quot;Brightness pin: &amp;quot;);\n  Serial.println( analogRead( PIN_BRIGHTNESSPOT));\n  clockDisp.setBrightness( brightness());\n  \/\/ Print the time, and update the colon\n  Serial.print(&amp;quot;Time decimal: &amp;quot;); Serial.println( now.hour() * 100 + now.minute());\n  clockDisp.printTime( now);\n  if( millis()%1000 &amp;lt; 500){\n    clockDisp.colon(1);\n  }else{\n    clockDisp.colon(0);\n  }\n  delay( 20);\n  \n  \/* The alarm function is actually a little bit complicated. It is\n  easy to test if the current time is equal to the alarm time. We \n  want the alarm to start beeping and not to stop until the switch \n  is thrown. However, if we immediately turned the switch back on,\n  the current time would still exceed the alarm time, and it would\n  start beebing. Therefore, I created the hasAlarmed variable, which\n  records that the alarm has gone off. hasAlarmed will reset to FALSE\n  when the time is less than the alarm time, indicated that we&#039;ve come\n  around the clock again.\n  *\/\n  \n  if( digitalRead( PIN_ALARMSWITCH)){\n    clockDisp.alarm( 1);\n    if( isAlarming){\n      if( millis()%1000 &amp;lt; 500){\n        tone( PIN_ALARMTONE, 1000);\n      }else{\n        noTone( PIN_ALARMTONE);\n      }\n    } else if( hasAlarmed){\n      if( (now.hour() * 100 + now.minute()) &amp;lt; alarmNum ){\n        hasAlarmed = 0;\n      }\n    } else {\n      \/\/ See if we need to turn on the alarm\n      if((now.hour() * 100 + now.minute()) == alarmNum){\n        isAlarming = 1;\n      }\n    }\n  }else if( isAlarming){\n    clockDisp.alarm( 0);\n    noTone( PIN_ALARMTONE);\n    isAlarming = 0;\n    hasAlarmed = 1;\n  }else{\n    clockDisp.alarm(0);\n  }\n \n  \n   \n   \/\/ Contol of setting state switch\n   int mode = analogRead( A3);\n   Serial.print(&amp;quot;Mode reading: &amp;quot;); Serial.println( mode);\n   if( mode &amp;gt; 900 ){ \/\/ set the time\n    Serial.println(&amp;quot;Setting the time&amp;quot;);\n    \/\/ blank the display  \n    clockDisp.blank();\n    int numDigits = 0;\n    int timeNum = 0;\n    \n    while( analogRead(A3) &amp;gt; 900){\n      \/\/ wait for keys, display them, check state\n      \/\/ wait for entry of time\n      char customKey = customKeypad.getKey();\n      if( customKey &amp;gt; 0){\n        if( numDigits &amp;gt; 0){\n          timeNum = timeNum*10 + (int)(customKey - &#039;0&#039;);\n        }else{\n          timeNum = (int)(customKey - &#039;0&#039;);\n        }  \n        numDigits ++;  \n        \n        clockDisp.print( timeNum);\n        clockDisp.writeDisplay();\n      }\n    }\n    \/\/ Test to see if we got a valid time:\n    if( (timeNum &amp;lt; 2500) &amp;amp;&amp;amp; (timeNum &amp;gt; 0)){\n      RTC.adjust( DateTime( now.year(), now.month(), now.day(),\n              timeNum \/ 100, timeNum%100, 0));\n    }else{\n      \/\/ An error, flash bars at the user\n      clockDisp.print( 10000);\n      clockDisp.writeDisplay();\n      delay( 300);\n    }\n  }else if( mode &amp;gt; 490){ \/\/ state 2\n    clockDisp.print( alarmNum);\n    clockDisp.writeDisplay();\n    int numDigits = 0;\n   \n    while( 1 ){\n      int ar = analogRead( A3);\n      if( (ar &amp;lt; 490) || (ar &amp;gt; 900)) break;\n\n      \/\/ wait for keys, display them, check state\n      \/\/ wait for entry of time\n      char customKey = customKeypad.getKey();\n      if( customKey &amp;gt; 0){\n        if( numDigits &amp;gt; 0){\n          alarmNum = alarmNum*10 + (int)(customKey - &#039;0&#039;);\n        }else{\n          alarmNum = (int)(customKey - &#039;0&#039;);\n        }  \n        numDigits ++;  \n        \n        \/\/clockDisp.print( (int)(customKey - &#039;0&#039;));\n        clockDisp.print( alarmNum);\n        clockDisp.writeDisplay();\n      }\n    }\n    \/\/ Test to see if we got a valid time:\n    if( (alarmNum &amp;gt;= 2500) || (alarmNum &amp;lt; 0)){\n      \/\/ Invalid time entered, flash error bars at user\n      clockDisp.print( 10000);\n      clockDisp.writeDisplay();\n      delay( 300);\n    } else{\n      hasAlarmed = 0;\n    }\n  } \n}\n\nint getDecimalTime(){\n  DateTime now = RTC.now();\n  int decimalTime = now.hour() * 100 + now.minute();\n  return decimalTime;\n}\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>When we bought our house we hired an electrician to install ceiling fans and lights. He worked, worked, and worked. My illusions of electrician&#8217;s work dissolved. Their work is a great deal more like laying bricks than solving equations. Oddly, the truth for electricians is also the truth for electronics. Nearly two years after starting, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[17,52],"class_list":["post-839","post","type-post","status-publish","format-standard","hentry","category-making","tag-arduino","tag-woodwork"],"_links":{"self":[{"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=\/wp\/v2\/posts\/839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=839"}],"version-history":[{"count":1,"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=\/wp\/v2\/posts\/839\/revisions"}],"predecessor-version":[{"id":1257,"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=\/wp\/v2\/posts\/839\/revisions\/1257"}],"wp:attachment":[{"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inkofpark.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}