Mired in code
Check-in [48da261cc6]
Not logged in
Public Repositories
mwm's Repositories

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add my binary clock for Arduino.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:48da261cc63325ed9b368238588f03f9517b27b7
User & Date: mwm@mired.org 2013-03-16 03:56:32
Context
2013-03-17
22:47
Update to latest version of the Watch code. check-in: 8c3827ffa1 user: mwm@mired.org tags: trunk
2013-03-16
03:56
Add my binary clock for Arduino. check-in: 48da261cc6 user: mwm@mired.org tags: trunk
2011-12-31
02:13
Published version of the plus 1 analysis code. check-in: ca484881b2 user: mwm@mired.org tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added Arduino/Watch/Makefile.





















>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
ARDMK_DIR=/usr/local/arduino-mk
ARDUINO_DIR=/usr/local/arduino
ARDUINO_PORT=/dev/ttyU?
ifndef BOARD_TAG
BOARD_TAG=uno
endif

ARDUINO_LIBS = Time EEPROM

include $(ARDMK_DIR)/Arduino.mk

Added Arduino/Watch/README.txt.























>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
This is an Arduino "sketch". If you already use Arduino, you should be
able to load the Watch.ino file into the IDE and compile and load
it. Read the start of Watch.ino for information on how to connect the
LEDs and switches required.

Personally, I use the arduino-mk project, which lets me use GNU make
to build and upload projects, allowing them to be built with my
favorite IDE rather than having to use the Arduino IDE (meaning I get
builds done with -j10 without having to figure out who to do that in
the Arduino IDE, so they're much faster). You'll want to edit the
Makefile to set the ARDUINO_* and BOARD_TAG variables properly.

Added Arduino/Watch/Watch.ino.





















































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#include <Time.h>
#include <EEPROM.h>

// Define the two input pins, and the state which means they are being pressed.
#define MODE		19
#define MODE_DOWN	LOW
#define SET		0
#define SET_DOWN	LOW

/*
 * Sets of pins used for the LED displays.
 * These are LSB first, with a trailing 0 to indicate the last LED in the set.
 * Note that this means we can't drive an LED with pin 0. Use it for a switch.
 */
static int dow_pins[] = {4, 3, 2, 0} ;
static int ampm_pins[] = {18, 17, 0} ;
static int hour_pins[] = {9, 8, 7, 6, 5, 0} ;
static int minute_pins[] = {15, 14, 13, 12, 11, 10, 0} ;
static int second_pins[] = {16, 0} ;

/*
 * We use a single blinking LED for seconds. By making it an RGB LED
 * and setting the ampm pins to the other two color pins in it, we can
 * change the color of the blink to indicate 24 hour mode, am, or pm.
 * Define AMPMRGB if that is the case.
 */
//#define AMPMRGB

// Define the delays on button actions
#define DEBOUNCE	50	// debounce time
#define AUTO_DELAY	100	// Automatic counter
#define LONG_DELAY	250	// Delay befoer starting automatic counter

// We want to use the internal pullup resistors if we can.
#define INPUT_TYPE(down) (down == LOW ? INPUT_PULLUP : INPUT)

/*
 * Now the structure used to drive the display.
 * Tuples consisting of the pin arrays from above and a function to extract
 * the displayed value from a time_t. Terminated by a pair of NULL pointers.
 */
typedef struct {
  int *pins ;			// The pin array for this display
  int (*fetch)(time_t) ;	// Function to fetch value from time_t
  void (*adjuster)(time_t) ;	// Adjust this value
} Display ;

// Forwards for the table below.
static void adjust_dow(time_t) ;
static void adjust_ampm(time_t) ;
static void adjust_hour(time_t) ;
static void adjust_minute(time_t) ;

// Value fetchers not from the time library.
static int hour_value(time_t) ;
static int ampm_value(time_t) ;

/*
 * And now the data structure that drives it all. The final NULLs serve
 * as an end of data marker, and the "normal mode" indicator.
 * The blinker_pins and "normal" row are both set to BLINKER, 
 */
Display displays[] = {{dow_pins, weekday, adjust_dow} ,
		      {ampm_pins, ampm_value, adjust_ampm} ,
		      {hour_pins, hour_value, adjust_hour} ,
		      {minute_pins, minute, adjust_minute} ,
		      {second_pins, second, NULL} ,
		      {NULL, NULL, NULL} ,	// End of list marker.
} ;
#define MODE_MOD	(sizeof(displays) / sizeof(Display) - 1)
// Index to entry used for running mode
#define NORMAL		(MODE_MOD - 1)


// Operating mode globals (oh well).
static int mode = NORMAL ;
static int ampm = false ;

// EEPROM location we store am/pm setting.
#define AMPM	0

void
setup() {
  // Input pins set for input. 
  pinMode(MODE, INPUT_TYPE(MODE_DOWN)) ;
  pinMode(SET, INPUT_TYPE(SET_DOWN)) ;

  // And now all those output pins!
  for (Display *dsp = displays; dsp->pins; dsp++) {
    for (int *pin = dsp->pins; *pin; pin++) {
      pinMode(*pin, OUTPUT) ;
    }
  }

  ampm = EEPROM.read(AMPM) ;
  if (ampm == 255) {
    ampm = false ;
  }
}


// Forwards used in loop.
static void blink(int) ;
static void display_time(time_t) ;
static void display_value(int *, int) ;

void
loop() {
  int set_value, mode_value ;
  static int old_mode_value = !MODE_DOWN ;
  static int old_set_value = !SET_DOWN ;

  // Blink the LEDs for the current mode when we change modes.
  mode_value = digitalRead(MODE) ;
  if (mode_value == MODE_DOWN && old_mode_value != MODE_DOWN) {
    mode = (mode + 1) % MODE_MOD ;
    delay(DEBOUNCE) ;
    blink(mode) ;	// Now set the RGB blinker for our mode
  }
  old_mode_value = mode_value ;

  // If set is down, adjust the items for mode
  set_value = digitalRead(SET) ;
  if (set_value == SET_DOWN && displays[mode].adjuster != NULL) {
    /*
     * Delay after the button press so you can increment by tapping, then
     * a shorter delay to autoincrement at reasonable speed.
     */
    delay(old_set_value == SET_DOWN ? AUTO_DELAY : LONG_DELAY) ;
    displays[mode].adjuster(now()) ;
  }
  old_set_value = set_value ;

  display_time(now()) ;
  delay(1) ;
}


// If we can adjust something, blink it to indicate that we can.
static void
blink(int m) {
  Display *dsp = &displays[m] ;
  
  if (dsp->adjuster) {
    for (int i = 0; i < 4; i++) {
      display_value(dsp->pins, 0) ;
      delay(125) ;
      display_value(dsp->pins, ~0) ;
      delay(125) ;
    }
  display_value(dsp->pins, dsp->fetch(now())) ;
  }
}


// Hour & ampm values depends on whether or not we have a 12 or 24 hour clock.
static int
hour_value(time_t t) {
  return ampm ? hourFormat12(t) : hour(t) ;
}

#ifdef AMPMRGB		// Invert bits for RGB LED.
#define FIXAMPM ~
#else
#define FIXAMPM
#endif
static int
ampm_value(time_t t) {
  return FIXAMPM(ampm ? isPM() + 1 : 0) ;
}  

// Send the current time to the available displays.
static void
display_time(time_t t) {
  for (Display *dsp = displays; dsp->pins; dsp++) {
    display_value(dsp->pins, dsp->fetch(t)) ;
  }
}

// Display the bits in a value, one bit per led
static void
display_value(int *array, int value) { 
  while (*array) {
    digitalWrite(*array++, value & 1) ;
    value >>= 1 ;
  }
}

/*
 * Routines for adjusting settings.
 * Each is called when the SET button is pressed to advance the selected
 * objects to the next state.
 *
 * Time settings always set the seconds back to 0 to avoid the clock
 * advancing while we're setting it. To get the seconds "right", set minutes
 * to the current minute, then advance it when your reference clock advances.
 */
static void
adjust_dow(time_t t) {
  setTime(hour(t), minute(t), 0, (day(t) % 7) + 1, month(t), year(t)) ;
}

// adjusting ampm adjust our display mode, not the time, and is saved to EEPROM
static void
adjust_ampm(time_t) {
  ampm = !ampm ;
  EEPROM.write(AMPM, ampm) ;
}

static void
adjust_hour(time_t t) {
  setTime((hour(t) + 1) % 24, minute(t), 0, day(t), month(t), year(t)) ;
}

static void
adjust_minute(time_t t) {
  setTime(hour(t), (minute(t) + 1) % 60, 0, day(t), month(t), year(t)) ;
}