2 #include <avr/interrupt.h>
4 //********************************************************************//
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
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;
20 #define LIMIT_OPENED_PIN 18 // A4: limit switch for open
21 #define LIMIT_CLOSED_PIN 19 // A5: limit switch for close
23 #define AJAR_PIN 14 // input pin for reed relais (door ajar/shut)
24 boolean ajar_last_state = false;
25 boolean ajar_state_changed = false;
27 #define MANUAL_OPEN_PIN 12 // keys for manual open and close
28 #define MANUAL_CLOSE_PIN 13 //
29 #define DEBOUNCE_DELAY 6250 // * 16us = 100ms
30 #define DEBOUNCE_IDLE 0 // currently no debouncing
31 #define DEBOUNCE_OPEN 1 // debouncing open key
32 #define DEBOUNCE_CLOSE 2 // debouncing close key
33 #define DEBOUNCE_FINISHED 4 // debouncing finished
37 #define IDLE 0 // close and open may be called
38 #define OPENING 1 // opening, only 's' command is allowed
39 #define CLOSING 2 // closing, onyl 's' command is allowed
40 #define WAIT 3 // wait some time after open or close and hold last step
41 #define ERROR 4 // an error occured
45 #define CMD_TOGGLE 't'
46 #define CMD_STATUS 's'
49 #define STEPPER_OFF 0x30
50 byte current_state = IDLE; // current state of internal state machine
51 byte next_step = 0; // step counter 0 .. 3
52 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
53 int timeout_cnt = 0; // counts up to MOVING_TIMEOUT
55 //********************************************************************//
59 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
60 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
62 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
63 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
68 if(digitalRead(LIMIT_OPENED_PIN))
76 if(digitalRead(LIMIT_CLOSED_PIN))
86 pinMode(AJAR_PIN, INPUT); // set pin to input
87 digitalWrite(AJAR_PIN, HIGH); // turn on pullup resistors
88 ajar_last_state = digitalRead(AJAR_PIN);
91 boolean get_ajar_status() // shut = true, ajar = false
93 if(digitalRead(AJAR_PIN))
103 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
104 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
106 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
107 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
109 debounce_state = DEBOUNCE_IDLE;
110 debounce_cnt = DEBOUNCE_DELAY;
113 boolean manual_open_pressed()
115 if(digitalRead(MANUAL_OPEN_PIN))
121 boolean manual_close_pressed()
123 if(digitalRead(MANUAL_CLOSE_PIN))
129 void start_debounce_timer() // this breaks millis() function, but who cares
131 debounce_cnt = DEBOUNCE_DELAY;
133 TCCR0A = 0; // no prescaler, WGM = 0 (normal)
135 OCR0A = 255; // 1+255 = 256 -> 16us @ 16 MHz
136 //OCR0A = 255; // 1+255 = 256 -> 12.8us @ 20 MHz
137 TCNT0 = 0; // reseting timer
138 TIMSK0 = 1<<OCF0A; // enable Interrupt
142 void stop_debounce_timer()
145 TCCR0B = 0; // no clock source
146 TIMSK0 = 0; // disable timer interrupt
149 ISR(TIMER0_COMPA_vect)
151 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
152 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
157 debounce_state |= DEBOUNCE_FINISHED;
159 debounce_cnt = DEBOUNCE_DELAY;
162 boolean manual_open()
164 if(manual_open_pressed()) {
165 if(debounce_state & DEBOUNCE_CLOSE) {
166 stop_debounce_timer();
167 debounce_state = DEBOUNCE_IDLE;
171 if(debounce_state == DEBOUNCE_IDLE) {
172 debounce_state = DEBOUNCE_OPEN;
173 start_debounce_timer();
175 else if(debounce_state & DEBOUNCE_FINISHED) {
176 stop_debounce_timer();
177 debounce_state = DEBOUNCE_IDLE;
181 else if(debounce_state & DEBOUNCE_OPEN) {
182 stop_debounce_timer();
183 debounce_state = DEBOUNCE_IDLE;
189 boolean manual_close()
191 if(manual_close_pressed()) {
192 if(debounce_state & DEBOUNCE_OPEN) {
193 stop_debounce_timer();
194 debounce_state = DEBOUNCE_IDLE;
198 if(debounce_state == DEBOUNCE_IDLE) {
199 debounce_state = DEBOUNCE_CLOSE;
200 start_debounce_timer();
202 else if(debounce_state & DEBOUNCE_FINISHED) {
203 stop_debounce_timer();
204 debounce_state = DEBOUNCE_IDLE;
208 else if(debounce_state & DEBOUNCE_CLOSE) {
209 stop_debounce_timer();
210 debounce_state = DEBOUNCE_IDLE;
216 //********************************************************************//
227 DDRB = 0x0F; // set PortB 3:0 as output
231 byte step_table(byte step)
233 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
249 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
250 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
256 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
257 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
261 byte led_table(byte led)
263 switch(led) { // xxxx xx00, leave RxD and TxD to 0
276 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
277 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
282 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
283 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
288 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
289 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
290 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
293 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
294 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
300 void start_step_timer()
302 // timer 1: 2 ms, between stepper output state changes
303 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
304 TCCR1B = 1<<WGM12 | 1<<CS12; //
305 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
306 //OCR1A = 155; // (1+155)*256 = 40000 -> 2 ms @ 20 MHz
307 TCNT1 = 0; // reseting timer
308 TIMSK1 = 1<<OCIE1A; // enable Interrupt
311 void start_wait_timer()
313 // timer1: 250 ms, minimal delay between subsequent open/close
314 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
316 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
317 //OCR1A = 19530; // (1+19530)*256 = 5000000 -> 250 ms @ 20 MHz
318 TCNT1 = 0; // reseting timer
319 TIMSK1 = 1<<OCIE1A; // enable Interrupt
322 void start_error_timer()
324 // timer1: 500 ms, blinking leds with 1 Hz
325 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
326 TCCR1B = 1<<WGM12 | 1<<CS12; //
327 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
328 //OCR1A = 39061; // (1+39061)*256 = 10000000 -> 500 ms @ 20 MHz
329 TCNT1 = 0; // reseting timer
330 TIMSK1 = 1<<OCIE1A; // enable Interrupt
333 void stop_timer() // stop the timer
336 TCCR1B = 0; // no clock source
337 TIMSK1 = 0; // disable timer interrupt
340 ISR(TIMER1_COMPA_vect)
342 // check if limit switch is active
343 if((current_state == OPENING && is_opened()) ||
344 (current_state == CLOSING && is_closed()))
348 if(current_state == OPENING)
353 current_state = WAIT;
358 if(current_state == OPENING || current_state == CLOSING) {
360 if(timeout_cnt >= MOVING_TIMEOUT) {
363 current_state = ERROR;
364 Serial.println("Error: open/close took too long!");
371 if(current_state == OPENING) { // next step (open)
372 PORTB = step_table(next_step);
377 else if(current_state == CLOSING) { // next step (close)
378 PORTB = step_table(next_step);
384 else if(current_state == WAIT) { // wait after last open/close finished -> idle
387 current_state = IDLE;
388 Serial.print("Status: ");
390 Serial.print("opened");
392 Serial.print("closed");
393 Serial.print(", idle");
394 if(get_ajar_status())
395 Serial.println(", shut");
397 Serial.println(", ajar");
400 else if(current_state == ERROR) {
404 else { // timer is useless stop it
410 if(led_delay_cnt >= LED_DELAY) {
413 PORTD = led_table(next_led);
415 if(current_state == OPENING) {
421 else if(current_state == CLOSING) {
429 //********************************************************************//
431 void reset_heartbeat()
433 digitalWrite(HEARTBEAT_PIN, HIGH);
439 digitalWrite(HEARTBEAT_PIN, LOW);
444 digitalWrite(HEARTBEAT_PIN, HIGH);
447 void init_heartbeat()
449 pinMode(HEARTBEAT_PIN, OUTPUT);
451 // timer 2: ~10 ms, timebase for heartbeat signal
452 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
453 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
454 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
455 //OCR2A = 194; // (1+194)*1024 = 199680 -> ~10 ms @ 20 MHz
456 TCNT2 = 0; // reseting timer
457 TIMSK2 = 1<<OCIE2A; // enable Interrupt
461 // while running this gets called every ~10ms
462 ISR(TIMER2_COMPA_vect)
464 boolean a = get_ajar_status();
466 if(heartbeat_cnt == HEARTBEAT_DURATION) {
468 if(a != ajar_last_state)
469 ajar_state_changed = true;
471 } else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
474 if(a != ajar_last_state)
475 ajar_state_changed = true;
480 //********************************************************************//
482 void reset_after_error()
489 current_state = IDLE;
493 current_state = CLOSING;
496 Serial.println("Ok, closing now");
504 current_state = OPENING;
513 current_state = CLOSING;
519 Serial.print("Status: ");
521 Serial.print("opened");
523 Serial.print("closed");
527 switch(current_state) {
528 case IDLE: Serial.print(", idle"); break;
529 case OPENING: Serial.print(", opening"); break;
530 case CLOSING: Serial.print(", closing"); break;
531 case WAIT: Serial.print(", waiting"); break;
532 default: Serial.print(", <undefined state>"); break;
534 if(get_ajar_status())
535 Serial.println(", shut");
537 Serial.println(", ajar");
552 current_state = IDLE;
554 // make sure door is locked after reset
559 current_state = CLOSING;
562 Serial.println("init complete");
567 if(Serial.available()) {
568 char command = Serial.read();
570 if(current_state == ERROR && command != CMD_RESET) {
571 Serial.println("Error: last open/close operation took too long!");
573 else if (command == CMD_RESET) {
576 else if (command == CMD_OPEN) {
577 if(current_state == IDLE) {
579 Serial.println("Already open");
582 Serial.println("Ok");
586 Serial.println("Error: Operation in progress");
588 else if (command == CMD_CLOSE) {
589 if(current_state == IDLE) {
591 Serial.println("Already closed");
594 Serial.println("Ok");
598 Serial.println("Error: Operation in progress");
600 else if (command == CMD_TOGGLE) {
601 if(current_state == IDLE) {
606 Serial.println("Ok");
609 Serial.println("Error: Operation in progress");
611 else if (command == CMD_STATUS)
614 Serial.println("Error: unknown command");
616 if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
617 Serial.println("open forced manually");
620 if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
621 Serial.println("close forced manually");
624 if(current_state == IDLE) {
634 if(ajar_state_changed) {
635 ajar_state_changed = false;