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