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