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