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