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