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