arduino sketch: yellow+blue light if there is no alarm / running RTC.
[fuzzy_alarm_clock.git] / arduino_sketch / fuzzy_alarm_clock_ds1307 / fuzzy_alarm_clock_ds1307.pde
blob12ff74524c57b9f818ab42e1b5d51264aa98ecf6
1 /*
2  * fuzzy_alarm_clock arduino sketch
3  * copyright (C) 2011 Elena Grandi, Diego Roversi
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */ 
19 #include <Wire.h>
21 #include <RealTimeClockDS1307.h>
23 // minutes of "dawn" before alarm
24 #define TIN 30
25 // "dawn" + "daylight"
26 #define TDAY 40
27 // "dawn" + "daylight" + blue blinding light
28 #define TOUT 60
30 // number of available alarms; max 10 for storage in the DS1307 ram 
31 #define NALARMS 4
33 // pins and addressed
34 #define RPIN 5
35 #define YPIN 6
36 #define BPIN 9
38 #define APIN0 3
39 #define APIN1 11
41 #define IINT 0
42 #define IPIN 2 
44 int st = 0; // alarm status (minutes from alarm - TIN)
45 char alarms[NALARMS][5]; // alarm settings
46 char cmin; // current minute
47 signed char a = -1; // current alarm
48 bool dbg = false; // print debug informations
49 bool ringing = false; // sound the alarm
50 bool pressed = false; // stop button status
52 #define NFREQ 9
53 int freq[] = { 255, 192, 128, 96, 64, 96, 128, 192, 255 }; // frequencies for the alarm
55 void setup () {
56     Serial.begin(9600);
57     Wire.begin();
59     pinMode(RPIN,OUTPUT);
60     pinMode(YPIN,OUTPUT);
61     pinMode(BPIN,OUTPUT);
63     pinMode(APIN0,INPUT);
64     pinMode(APIN1,INPUT);
65     TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
66     TCCR2B = _BV(WGM22) | _BV(CS22);
68     attachInterrupt(IINT, button_pressed, HIGH);
70     analogWrite(RPIN,128);
71     digitalWrite(YPIN,LOW);
72     digitalWrite(BPIN,LOW);
73    
74     // if the RTC is already running read alarms and status, 
75     // otherwise set everything to a sane default
76     if ( RTC.readData(0x00) >> 7 ) {
77         for ( int i = 0 ; i < NALARMS ; i ++ ) {
78             for ( int j = 0; j < 5 ; j ++ ) {
79                 alarms[i][j] = 0;
80             }
81         }
82         st = 0;
83         a = -1;
84         save_status();
85     } else {
86         st = RTC.readData(0x08);
87         a = RTC.readData(0x09);
88         cmin = RTC.readData(0x0a);
89         // This only works if the arduino had no power for a 
90         // "short" time. This is by design. :D
91         if ( a >= 0 ) {
92             RTC.readClock();
93             st = st + (RTC.getMinutes() - cmin) % 60;
94             cmin = RTC.getMinutes();
95             save_status();
96         }
97         for ( int i = 0; i < NALARMS ; i ++ ) {
98             for ( int j = 0; j < 5 ; j ++ ) {
99                 alarms[i][j] = RTC.readData(0x0b + i*5 + j);
100             }
101         }
102     }
103     reset_leds();
106 void loop () {
107   
108   // read commands from serial
109   check_serial();
110   
111   // read time, check alarms
112   check_time();
114   if ( dbg ) {
115       s_print_time();
116       Serial.print("st: ");
117       Serial.print(st,DEC);
118       Serial.print(", a: ");
119       Serial.print(a,DEC);
120       Serial.print(", cmin: ");
121       Serial.print(cmin,DEC);
122       Serial.print(", ringing: ");
123       Serial.print(ringing);
124       Serial.print(", pressed: ");
125       Serial.println(pressed);
126   }
127   
128   // act on status: LEDs and buzzer
129   if ( st > 0 ) {
130       set_leds();
131   }
132   if ( st == TIN ) {
133       if ( RTC.getSeconds() < 5 ) {
134           ringing = true;
135       }
136       if ( pressed ) {
137           ringing = false;
138       }
139   } else {
140       ringing = false;
141       pressed = false;
142   }
143   if ( ringing ) {
144       ring_alarm();
145   }
146   // wait about till the next second
148   delay(1000);
149   
152 void save_status() {
153     RTC.writeData(0x08,st);
154     RTC.writeData(0x09,a);
155     RTC.writeData(0x0a,cmin);
158 int has_alarm() {
159     if ( RTC.readData(0x00) >> 7 ) {
160         return false;
161     }
162     for ( int i = 0; i < NALARMS ; i ++ ) {
163         // we can skip checking the hour/minute field
164         for ( int j = 0; j < 3 ; j ++ ) {
165             if ( alarms[i][j] != 0 ) {
166                 return true;
167             }
168         }
169     }
170     return false;
173 // ****** Serial interface management *************************************** //
175 void check_serial() {
176     int rec = Serial.read();
177     switch (rec) {
178         case 'a':
179             s_set_alarm();
180             break;
181         case 's':
182             s_set_time();
183             break;
184         case 'p':
185             s_print_alarms();
186             break;
187         case 't':
188             s_print_time();
189             break;
190         case 'd':
191             s_toggle_debug();
192             break;
193         case 'l':
194             s_led_test();
195             break;
196         case 'h':
197             s_print_help();
198             break;
199         case 'r':
200             s_reset_alarms();
201             break;
202     }
205 void s_set_alarm() {
206     int i = s_read_dig();
207     for ( int j = 0; j < 5 ; j++) {
208         alarms[i][j] = s_read_2hex();
209     }
210     for ( int j = 0; j < 5 ; j++ ) {
211         RTC.writeData(0x0b + i*5 + j,alarms[i][j]);
212     }
213     Serial.print("Alarm ");
214     Serial.print(i,DEC);
215     Serial.println(" set.");
216     reset_leds();
219 void s_set_time() {
220     RTC.start();
221     RTC.setYear(s_read_2dig());
222     RTC.setMonth(s_read_2dig());
223     RTC.setDate(s_read_2dig());
224     RTC.setDayOfWeek(s_read_dig());
225     RTC.setHours(s_read_2dig());
226     RTC.setMinutes(s_read_2dig());
227     RTC.setSeconds(s_read_2dig());
228     RTC.setClock();
229     Serial.print("Time set: ");
230     s_print_time();
231     reset_leds();
234 int s_read_dig() {
235     int rec;
236     rec = Serial.read();
237     while ( rec == -1 ) {
238         rec = Serial.read();
239     }
240     return rec - 48;
243 int s_read_2dig() {
244     int n;
245     n = s_read_dig() * 10;
246     n = n + s_read_dig();
247     return n;
250 int s_read_hex() {
251     int rec;
252     rec = Serial.read();
253     while ( rec == -1 ) {
254         rec = Serial.read();
255     }
256     if ( rec >= 48 && rec < 58 ) {
257         return rec - 48;
258     } else if ( rec >= 65 && rec < 71 ) {
259         return rec - 55;
260     } else if ( rec >= 97 && rec < 103 ) {
261         return rec - 87;
262     } else {
263         return 0;
264     }
267 int s_read_2hex() {
268     int n;
269     n = s_read_hex() * 16;
270     n = n + s_read_hex();
271     return n;
274 void s_print_alarms() {
275     for ( int i = 0; i < NALARMS ; i++) {
276         Serial.print(i,DEC);
277         Serial.print(" - ");
278         for ( int j = 0; j < 5 ; j++) {
279             Serial.print(alarms[i][j],DEC);
280             Serial.print(" ");
281         }
282         Serial.println("");
283     }
286 void s_print_time() {
287     RTC.readClock();
288     Serial.print(RTC.getYear(),DEC);
289     Serial.print("/");
290     Serial.print(RTC.getMonth(),DEC);
291     Serial.print("/");
292     Serial.print(RTC.getDate(),DEC);
293     Serial.print(" (");
294     Serial.print(RTC.getDayOfWeek(),DEC);
295     Serial.print(") ");
296     Serial.print(RTC.getHours(),DEC);
297     Serial.print(":");
298     Serial.print(RTC.getMinutes(),DEC);
299     Serial.print(":");
300     Serial.println(RTC.getSeconds(),DEC);
303 void s_toggle_debug() {
304     if ( dbg ) {
305         dbg = false;
306     } else {
307         dbg = true;
308     }
311 void s_led_test() {
312     if ( a < 0 ) {
313         Serial.println("Testing LEDs");
314         digitalWrite(RPIN,HIGH);
315         digitalWrite(YPIN,HIGH);
316         digitalWrite(BPIN,HIGH);
317         delay(1000);
318         digitalWrite(RPIN,HIGH);
319         digitalWrite(YPIN,LOW);
320         digitalWrite(BPIN,LOW);
321     }
324 void s_reset_alarms() {
325     for ( int i = 0 ; i < NALARMS ; i ++ ) {
326         for ( int j = 0; j < 5 ; j ++ ) {
327             alarms[i][j] = 0;
328         }
329     }
330     st = 0;
331     a = -1;
332     save_status();
333     reset_leds();
336 void s_print_help() {
337     Serial.println("");
338     Serial.println("  a<s> - set an alarm");
339     Serial.println("         <s> is nhhhhhhhhhh");
340     Serial.println("         alarm ID");
341     Serial.println("         one bit (in hex) for day of week");
342     Serial.println("         hex values for month, day");
343     Serial.println("         hex values for hour, minutes");
344     Serial.println("         (relative to start-of-dawn time)");
345     Serial.println("  s<s> - set the clock");
346     Serial.println("         <s> is yymmgguHHMMSS");
347     Serial.println("  p    - print the alarms");
348     Serial.println("  t    - print the clock");
349     Serial.println("  d    - toggle printing of debug informations");
350     Serial.println("  r    - reset status and alarms");
351     Serial.println("  l    - test LEDs");
352     Serial.println("  h    - print this help");
355 void button_pressed() {
356     pressed = true;
359 // ****** Time management *************************************************** //
361 // Set the current time
362 void set_time(int y,int m,int d, int w, int hh, int mm, int ss) {
363     RTC.setYear(y);
364     RTC.setMonth(m);
365     RTC.setDate(m);
366     RTC.setDayOfWeek(w);
367     RTC.setHours(hh);
368     RTC.setMinutes(mm);
369     RTC.setSeconds(ss);
370     RTC.switchTo24h();
371     RTC.setClock();
374 void check_time() {
375     RTC.readClock();
377     int mm = RTC.getMinutes();
378     int hour = RTC.getHours();
379     int wday = RTC.getDayOfWeek();
380     int day = RTC.getDate();
381     int month = RTC.getMonth();
383     if ( a < 0 ) {
384         for ( int i = 0; i < NALARMS ; i ++ ) {
385             // check alarm i
386             if ( ( alarms[i][0] & ( 1 << (wday - 1) ) ) || 
387                     (month == alarms[i][1] && day == alarms[i][2]) ) {
388                 // this is alarm day!
389                 if ( hour == alarms[i][3] && mm == alarms[i][4]) {
390                     // this is alarm hour!
391                     a = i;
392                     st = 1;
393                     cmin = mm;
394                     save_status();
395                     if ( ( alarms[i][0] & 128 ) == 0 ) {
396                         // this alarm won't be repeated
397                         alarms[i] = { 0,0,0,0,0 };
398                         for ( int j = 0; j < 5 ; j++ ) {
399                             RTC.writeData(0x0b + i*5 + j,0);
400                         }
401                     }
402                     break;
403                 }
404             } 
405         }
406     } else {
407         if ( cmin != mm ) {
408             cmin = mm;
409             st++;
410             save_status();
411         } 
412     }
416 // ****** LED management **************************************************** //
418 void set_leds() {
419   if ( st > 0 && st <= TIN) {
420       int y = int(float(st*255)/TIN);
421       int r = 255 - y;
422       analogWrite(RPIN,r);
423       analogWrite(YPIN,y);
424   } else if ( st > TIN && st < TDAY ) {
425       analogWrite(RPIN,0);
426       analogWrite(YPIN,255);
427       analogWrite(BPIN,0);
428   } else if (st >= TDAY && st < TOUT) {
429       analogWrite(RPIN,0);
430       analogWrite(YPIN,0);
431       analogWrite(BPIN,255);
432   } else if (st == TOUT) {
433       // reset stuff
434       st = 0;
435       a = -1;
436       save_status();
437       reset_leds();
438   } 
441 void reset_leds() {
442     if ( a < 0 ) {
443         if ( has_alarm() ) {
444             analogWrite(RPIN,128);
445             analogWrite(YPIN,0);
446             analogWrite(BPIN,0);
447         } else {
448             analogWrite(RPIN,0);
449             analogWrite(YPIN,16);
450             analogWrite(BPIN,16);
451         }
452     }
455 // PC speaker
457 void ring_alarm() {
459     pinMode(APIN1,OUTPUT);
460     for (int i=0; i<NFREQ ; i++) {
461         OCR2A = freq[i];
462         delay(50);
463     }
464     pinMode(APIN1,INPUT);
468 // vim: set filetype=c: