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