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