ajar-state: boolean vs. byte
[svn42.git] / firmware / tuer.pde
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3
4 //********************************************************************//
5
6 #define HEARTBEAT_PIN 15 // blinking led indicating that system is active
7 #define HEARTBEAT_DURATION 10 // *10 ms, duration of heartbeat pulse
8 #define HEARTBEAT_DELAY 200   // *10 ms, 1/heartbeat-frequency
9 int heartbeat_cnt = 0;                    
10         
11 #define LEDS_ON 0xFC
12 #define LEDS_OFF 0x00
13
14 #define LEDS_GREEN_COMMON_PIN 16
15 #define LEDS_RED_COMMON_PIN 17 
16 #define LED_DELAY 50 // *2 ms, between led shifts
17 int led_delay_cnt = 0;
18 byte next_led = 0;
19
20 #define LIMIT_OPENED_PIN 18 // A4: limit switch for open
21 #define LIMIT_CLOSED_PIN 19 // A5: limit switch for close
22
23 #define AJAR_PIN 14 // input pin for reed relais (door ajar/shut)
24 #define SHUT 0
25 #define AJAR 1
26 byte ajar_last_state = SHUT;
27
28 #define MANUAL_OPEN_PIN 12  // keys for manual open and close
29 #define MANUAL_CLOSE_PIN 13 // 
30 #define DEBOUNCE_DELAY 6250  // * 16us = 100ms
31 #define DEBOUNCE_IDLE 0     // currently no debouncing
32 #define DEBOUNCE_OPEN 1     // debouncing open key
33 #define DEBOUNCE_CLOSE 2    // debouncing close key
34 #define DEBOUNCE_FINISHED 4 // debouncing finished
35 byte debounce_state;
36 int debounce_cnt = 0;
37
38 #define IDLE 0      // close and open may be called
39 #define OPENING 1   // opening, only 's' command is allowed
40 #define CLOSING 2   // closing, onyl 's' command is allowed
41 #define WAIT 3      // wait some time after open or close and hold last step
42 #define ERROR 4     // an error occured
43
44 #define CMD_OPEN 'o'
45 #define CMD_CLOSE 'c'
46 #define CMD_TOGGLE 't'
47 #define CMD_STATUS 's'
48 #define CMD_RESET 'r'
49
50 #define STEPPER_OFF 0x30
51 byte current_state = IDLE;  // current state of internal state machine
52 byte next_step = 0;         // step counter 0 .. 3
53 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
54 int timeout_cnt = 0;        // counts up to MOVING_TIMEOUT
55
56 //********************************************************************//
57
58 void init_limits()
59 {
60   pinMode(LIMIT_OPENED_PIN, INPUT);      // set pin to input
61   digitalWrite(LIMIT_OPENED_PIN, HIGH);  // turn on pullup resistors  
62
63   pinMode(LIMIT_CLOSED_PIN, INPUT);      // set pin to input
64   digitalWrite(LIMIT_CLOSED_PIN, HIGH);  // turn on pullup resistors  
65 }
66
67 boolean is_opened()
68 {
69   if(digitalRead(LIMIT_OPENED_PIN))
70      return false;
71      
72   return true;
73 }
74
75 boolean is_closed()
76 {
77   if(digitalRead(LIMIT_CLOSED_PIN))
78      return false;
79      
80   return true;
81 }
82
83 //**********//
84
85 byte get_ajar_status()
86 {
87   if(digitalRead(AJAR_PIN) == LOW)
88     return SHUT;
89   
90   return AJAR;
91 }
92
93 void init_ajar()
94 {
95   pinMode(AJAR_PIN, INPUT);      // set pin to input
96   digitalWrite(AJAR_PIN, HIGH);  // turn on pullup resistors  
97   ajar_last_state = get_ajar_status();
98 }
99
100 //**********//
101
102 void init_manual()
103 {
104   pinMode(MANUAL_OPEN_PIN, INPUT);      // set pin to input
105   digitalWrite(MANUAL_OPEN_PIN, HIGH);  // turn on pullup resistors  
106
107   pinMode(MANUAL_CLOSE_PIN, INPUT);     // set pin to input
108   digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors  
109
110   debounce_state = DEBOUNCE_IDLE;
111   debounce_cnt = DEBOUNCE_DELAY;
112 }
113
114 boolean manual_open_pressed()
115 {
116   if(digitalRead(MANUAL_OPEN_PIN))
117      return false;
118      
119   return true;
120 }
121
122 boolean manual_close_pressed()
123 {
124   if(digitalRead(MANUAL_CLOSE_PIN))
125      return false;
126      
127   return true;
128 }
129
130 void start_debounce_timer()  // this breaks millis() function, but who cares
131 {
132   debounce_cnt = DEBOUNCE_DELAY;
133
134   TCCR0A = 0;         // no prescaler, WGM = 0 (normal)
135   TCCR0B = 1<<CS00;   // 
136   OCR0A = 255;        // 1+255 = 256 -> 16us @ 16 MHz
137   //OCR0A = 255;        // 1+255 = 256 -> 12.8us @ 20 MHz
138   TCNT0 = 0;          // reseting timer
139   TIMSK0 = 1<<OCF0A;  // enable Interrupt
140
141 }
142
143 void stop_debounce_timer()
144 {
145   // timer0
146   TCCR0B = 0; // no clock source
147   TIMSK0 = 0; // disable timer interrupt
148 }
149
150 ISR(TIMER0_COMPA_vect)
151 {
152   if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
153      ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
154     if(debounce_cnt) {
155       debounce_cnt--;
156       return;
157     }
158     debounce_state |= DEBOUNCE_FINISHED;
159   }
160   debounce_cnt = DEBOUNCE_DELAY;
161 }
162
163 boolean manual_open()
164 {
165   if(manual_open_pressed()) {
166     if(debounce_state & DEBOUNCE_CLOSE) {
167       stop_debounce_timer();
168       debounce_state = DEBOUNCE_IDLE;
169       return false;
170     }
171
172     if(debounce_state == DEBOUNCE_IDLE) {
173       debounce_state = DEBOUNCE_OPEN;
174       start_debounce_timer();
175     }
176     else if(debounce_state & DEBOUNCE_FINISHED) {
177       stop_debounce_timer();
178       debounce_state = DEBOUNCE_IDLE;
179       return true;
180     }
181   }
182   else if(debounce_state & DEBOUNCE_OPEN) {
183     stop_debounce_timer();
184     debounce_state = DEBOUNCE_IDLE;
185   }
186
187   return false;
188 }
189
190 boolean manual_close()
191 {
192   if(manual_close_pressed()) {
193     if(debounce_state & DEBOUNCE_OPEN) {
194       stop_debounce_timer();
195       debounce_state = DEBOUNCE_IDLE;
196       return false;
197     }
198
199     if(debounce_state == DEBOUNCE_IDLE) {
200       debounce_state = DEBOUNCE_CLOSE;
201       start_debounce_timer();
202     }
203     else if(debounce_state & DEBOUNCE_FINISHED) {
204       stop_debounce_timer();
205       debounce_state = DEBOUNCE_IDLE;
206       return true;
207     }
208   }
209   else if(debounce_state & DEBOUNCE_CLOSE) {
210     stop_debounce_timer();
211     debounce_state = DEBOUNCE_IDLE;
212   }
213
214   return false;
215 }
216
217 //********************************************************************//
218
219 void reset_stepper()
220 {
221   next_step = 0;
222   PORTB = STEPPER_OFF;
223   timeout_cnt = 0;
224 }
225
226 void init_stepper()
227 {
228   DDRB = 0x0F; // set PortB 3:0 as output
229   reset_stepper();
230 }
231
232 byte step_table(byte step)
233 {
234   switch(step) { // 0011 xxxx, manual keys pull-ups stay active
235   case 0: return 0x33;
236   case 1: return 0x36;
237   case 2: return 0x3C;
238   case 3: return 0x39;
239   }
240   return STEPPER_OFF;
241 }
242
243 //**********//
244
245 void reset_leds()
246 {
247   led_delay_cnt = 0;
248   next_led = 0;
249   PORTD = LEDS_OFF;
250   digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
251   digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
252 }
253
254 void init_leds()
255 {
256   DDRD = 0xFC;
257   pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
258   pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
259   reset_leds();
260 }
261
262 byte led_table(byte led)
263 {
264   switch(led) {  // xxxx xx00, leave RxD and TxD to 0
265   case 0: return 0x04;
266   case 1: return 0x08;
267   case 2: return 0x10;
268   case 3: return 0x20;
269   case 4: return 0x40;
270   case 5: return 0x80; 
271   }
272   return LEDS_OFF;
273 }
274
275 void leds_green()
276 {
277   digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
278   digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
279 }
280
281 void leds_red()
282 {
283   digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
284   digitalWrite(LEDS_RED_COMMON_PIN, LOW);
285 }
286
287 void leds_toggle()
288 {
289   if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
290     digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
291     digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
292   }
293   else {
294     digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
295     digitalWrite(LEDS_RED_COMMON_PIN, LOW);
296   }
297 }
298
299 //**********//
300
301 void start_step_timer()
302 {
303   // timer 1: 2 ms, between stepper output state changes
304   TCCR1A = 0;                    // prescaler 1:256, WGM = 4 (CTC)
305   TCCR1B = 1<<WGM12 | 1<<CS12;   // 
306   OCR1A = 124;        // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
307   //OCR1A = 155;        // (1+155)*256 = 40000 -> 2 ms @ 20 MHz
308   TCNT1 = 0;          // reseting timer
309   TIMSK1 = 1<<OCIE1A; // enable Interrupt
310 }
311
312 void start_wait_timer()
313 {
314   // timer1: 250 ms, minimal delay between subsequent open/close
315   TCCR1A = 0;         // prescaler 1:256, WGM = 0 (normal)
316   TCCR1B = 1<<CS12;   // 
317   OCR1A = 15624;      // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
318   //OCR1A = 19530;      // (1+19530)*256 = 5000000 -> 250 ms @ 20 MHz  
319   TCNT1 = 0;          // reseting timer
320   TIMSK1 = 1<<OCIE1A; // enable Interrupt
321 }
322
323 void start_error_timer()
324 {
325   // timer1: 500 ms, blinking leds with 1 Hz
326   TCCR1A = 0;                  // prescaler 1:256, WGM = 4 (CTC)
327   TCCR1B = 1<<WGM12 | 1<<CS12; // 
328   OCR1A = 31249;      // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
329   //OCR1A = 39061;      // (1+39061)*256 = 10000000 -> 500 ms @ 20 MHz
330   TCNT1 = 0;          // reseting timer
331   TIMSK1 = 1<<OCIE1A; // enable Interrupt
332 }
333
334 void stop_timer() // stop the timer
335 {
336   // timer1
337   TCCR1B = 0; // no clock source
338   TIMSK1 = 0; // disable timer interrupt
339 }
340
341 ISR(TIMER1_COMPA_vect)
342 {
343       // check if limit switch is active
344   if((current_state == OPENING && is_opened()) ||
345      (current_state == CLOSING && is_closed())) 
346   {
347     stop_timer();
348     reset_leds();
349     if(current_state == OPENING)
350       leds_green();
351     else
352       leds_red();
353     PORTD = LEDS_ON;
354     current_state = WAIT;
355     start_wait_timer();
356     return;
357   }
358       
359   if(current_state == OPENING || current_state == CLOSING) {
360     timeout_cnt++;
361     if(timeout_cnt >= MOVING_TIMEOUT) {
362       reset_stepper();
363       stop_timer();
364       current_state = ERROR;
365       Serial.println("Error: open/close took too long!");
366       start_error_timer();
367       leds_green();
368       PORTD = LEDS_ON;
369     }
370   }
371
372   if(current_state == OPENING) { // next step (open)
373     PORTB = step_table(next_step);
374     next_step++;
375     if(next_step >= 4)
376       next_step = 0;
377   }
378   else if(current_state == CLOSING) { // next step (close)
379     PORTB = step_table(next_step);
380     if(next_step == 0)
381       next_step = 3;
382     else
383       next_step--;
384   }
385   else if(current_state == WAIT) { // wait after last open/close finished -> idle
386     stop_timer();
387     reset_stepper();
388     current_state = IDLE;
389     Serial.print("Status: ");
390     if(is_opened())
391       Serial.print("opened");
392     else if(is_closed())
393       Serial.print("closed");
394     Serial.print(", idle");
395     if(get_ajar_status() == SHUT)
396       Serial.println(", shut");
397     else
398       Serial.println(", ajar");
399     return;
400   }
401   else if(current_state == ERROR) {
402     leds_toggle();
403     return;
404   }
405   else { // timer is useless stop it
406     stop_timer();
407     return;
408   }
409
410   led_delay_cnt++;
411   if(led_delay_cnt >= LED_DELAY) {
412     led_delay_cnt = 0;    
413
414     PORTD = led_table(next_led);
415
416     if(current_state == OPENING) {
417       if(next_led == 0)
418         next_led = 5;
419       else
420         next_led--;
421     }
422     else if(current_state == CLOSING) {
423       next_led++;
424       if(next_led >= 6)
425         next_led = 0;
426     }
427   }
428 }
429
430 //********************************************************************//
431
432 void reset_heartbeat()
433 {
434   digitalWrite(HEARTBEAT_PIN, HIGH);
435   heartbeat_cnt = 0;
436 }
437
438 void heartbeat_on()
439 {
440   digitalWrite(HEARTBEAT_PIN, LOW);
441 }
442
443 void heartbeat_off()
444 {
445   digitalWrite(HEARTBEAT_PIN, HIGH);
446 }
447
448 void init_heartbeat()
449 {
450   pinMode(HEARTBEAT_PIN, OUTPUT);
451   reset_heartbeat();
452   // timer 2: ~10 ms, timebase for heartbeat signal
453   TCCR2A = 1<<WGM21;                    // prescaler 1:1024, WGM = 2 (CTC)
454   TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; // 
455   OCR2A = 155;        // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
456   //OCR2A = 194;        // (1+194)*1024 = 199680 -> ~10 ms @ 20 MHz
457   TCNT2 = 0;          // reseting timer
458   TIMSK2 = 1<<OCIE2A; // enable Interrupt
459   heartbeat_on();
460 }
461
462 // while running this gets called every ~10ms
463 ISR(TIMER2_COMPA_vect)
464 {
465   heartbeat_cnt++;
466   if(heartbeat_cnt == HEARTBEAT_DURATION)
467     heartbeat_off();
468   else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
469     heartbeat_on();
470     heartbeat_cnt = 0;
471   }
472 }
473
474 //********************************************************************//
475
476 void reset_after_error()
477 {
478   stop_timer();
479   reset_leds();
480
481   leds_red();
482   if(is_closed()) {
483     current_state = IDLE;
484     PORTD = LEDS_ON;
485   }
486   else {
487     current_state = CLOSING;
488     start_step_timer();
489   }
490   Serial.println("Ok, closing now");
491 }
492
493 void start_open()
494 {
495   reset_stepper();
496   reset_leds();
497   leds_green();
498   current_state = OPENING;
499   start_step_timer();
500 }
501
502 void start_close()
503 {
504   reset_stepper();
505   reset_leds();
506   leds_red();
507   current_state = CLOSING;
508   start_step_timer();
509 }
510
511 void print_status()
512 {
513   Serial.print("Status: ");
514   if(is_opened())
515     Serial.print("opened");
516   else if(is_closed())
517     Serial.print("closed");
518   else
519     Serial.print("<->");
520
521   switch(current_state) {
522   case IDLE: Serial.print(", idle"); break;
523   case OPENING: Serial.print(", opening"); break;
524   case CLOSING: Serial.print(", closing"); break;
525   case WAIT: Serial.print(", waiting"); break;
526   default: Serial.print(", <undefined state>"); break;
527   }
528   if(get_ajar_status() == SHUT)
529     Serial.println(", shut");
530   else
531     Serial.println(", ajar");
532 }
533
534 //**********//
535
536 void setup()
537 {
538   init_limits();
539   init_ajar();
540   init_stepper();
541   init_leds();
542   init_heartbeat();
543
544   Serial.begin(9600);
545
546   current_state = IDLE;
547
548       // make sure door is locked after reset
549   leds_red();
550   if(is_closed())
551     PORTD = LEDS_ON;
552   else {
553     current_state = CLOSING;
554     start_step_timer();
555   }
556   Serial.println("init complete");
557 }
558
559 void loop()
560 {
561   if(Serial.available()) {
562     char command = Serial.read();
563
564     if(current_state == ERROR && command != CMD_RESET) {
565       Serial.println("Error: last open/close operation took too long!");
566     }
567     else if (command == CMD_RESET) {
568       reset_after_error();
569     }
570     else if (command == CMD_OPEN) {
571       if(current_state == IDLE) {
572         if(is_opened())
573           Serial.println("Already open");
574         else {
575           start_open();
576           Serial.println("Ok");
577         }
578       }
579       else
580         Serial.println("Error: Operation in progress");
581     }
582     else if (command == CMD_CLOSE) {
583       if(current_state == IDLE) {
584         if(is_closed())
585           Serial.println("Already closed");
586         else {
587           start_close();
588           Serial.println("Ok");
589         }
590       }
591       else
592         Serial.println("Error: Operation in progress");
593     }
594     else if (command == CMD_TOGGLE) {
595       if(current_state == IDLE) {
596         if(is_closed())
597           start_open();
598         else
599           start_close();
600         Serial.println("Ok");
601       }
602       else
603         Serial.println("Error: Operation in progress");
604     }
605     else if (command == CMD_STATUS)
606       print_status();
607     else
608       Serial.println("Error: unknown command");
609   }
610   if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
611     Serial.println("open forced manually");
612     start_open();
613   }
614   if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
615     Serial.println("close forced manually");
616     start_close();
617   }
618   if(current_state == IDLE) {
619     if(is_opened()) {
620       leds_green();
621       PORTD = LEDS_ON;
622     }
623     if(is_closed()) {
624       leds_red();
625       PORTD = LEDS_ON;
626     }
627   }
628   byte a = get_ajar_status();
629   if(a != ajar_last_state)
630   {
631     print_status();
632     ajar_last_state = a;
633   }
634 }