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)
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;
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
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
49 #define CMD_TOGGLE 't'
50 #define CMD_STATUS 's'
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
59 //********************************************************************//
63 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
64 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
66 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
67 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
72 if(digitalRead(LIMIT_OPENED_PIN))
80 if(digitalRead(LIMIT_CLOSED_PIN))
88 byte get_ajar_status()
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;
98 return ajar_last_state;
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();
112 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
113 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
115 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
116 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
118 debounce_state = DEBOUNCE_IDLE;
119 debounce_cnt = DEBOUNCE_DELAY;
122 boolean manual_open_pressed()
124 if(digitalRead(MANUAL_OPEN_PIN))
130 boolean manual_close_pressed()
132 if(digitalRead(MANUAL_CLOSE_PIN))
138 void start_debounce_timer() // this breaks millis() function, but who cares
140 debounce_cnt = DEBOUNCE_DELAY;
142 TCCR0A = 0; // no prescaler, WGM = 0 (normal)
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
151 void stop_debounce_timer()
154 TCCR0B = 0; // no clock source
155 TIMSK0 = 0; // disable timer interrupt
158 ISR(TIMER0_COMPA_vect)
160 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
161 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
166 debounce_state |= DEBOUNCE_FINISHED;
168 debounce_cnt = DEBOUNCE_DELAY;
171 boolean manual_open()
173 if(manual_open_pressed()) {
174 if(debounce_state & DEBOUNCE_CLOSE) {
175 stop_debounce_timer();
176 debounce_state = DEBOUNCE_IDLE;
180 if(debounce_state == DEBOUNCE_IDLE) {
181 debounce_state = DEBOUNCE_OPEN;
182 start_debounce_timer();
184 else if(debounce_state & DEBOUNCE_FINISHED) {
185 stop_debounce_timer();
186 debounce_state = DEBOUNCE_IDLE;
190 else if(debounce_state & DEBOUNCE_OPEN) {
191 stop_debounce_timer();
192 debounce_state = DEBOUNCE_IDLE;
198 boolean manual_close()
200 if(manual_close_pressed()) {
201 if(debounce_state & DEBOUNCE_OPEN) {
202 stop_debounce_timer();
203 debounce_state = DEBOUNCE_IDLE;
207 if(debounce_state == DEBOUNCE_IDLE) {
208 debounce_state = DEBOUNCE_CLOSE;
209 start_debounce_timer();
211 else if(debounce_state & DEBOUNCE_FINISHED) {
212 stop_debounce_timer();
213 debounce_state = DEBOUNCE_IDLE;
217 else if(debounce_state & DEBOUNCE_CLOSE) {
218 stop_debounce_timer();
219 debounce_state = DEBOUNCE_IDLE;
225 //********************************************************************//
236 DDRB = 0x0F; // set PortB 3:0 as output
240 byte step_table(byte step)
242 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
258 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
259 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
265 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
266 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
270 byte led_table(byte led)
272 switch(led) { // xxxx xx00, leave RxD and TxD to 0
285 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
286 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
291 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
292 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
297 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
298 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
299 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
302 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
303 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
309 void start_step_timer()
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
320 void start_wait_timer()
322 // timer1: 250 ms, minimal delay between subsequent open/close
323 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
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
331 void start_error_timer()
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
342 void stop_timer() // stop the timer
345 TCCR1B = 0; // no clock source
346 TIMSK1 = 0; // disable timer interrupt
349 ISR(TIMER1_COMPA_vect)
351 // check if limit switch is active
352 if((current_state == OPENING && is_opened()) ||
353 (current_state == CLOSING && is_closed()))
357 if(current_state == OPENING)
362 current_state = WAIT;
367 if(current_state == OPENING || current_state == CLOSING) {
369 if(timeout_cnt >= MOVING_TIMEOUT) {
372 current_state = ERROR;
373 Serial.println("Error: open/close took too long!");
380 if(current_state == OPENING) { // next step (open)
381 PORTB = step_table(next_step);
386 else if(current_state == CLOSING) { // next step (close)
387 PORTB = step_table(next_step);
393 else if(current_state == WAIT) { // wait after last open/close finished -> idle
396 current_state = IDLE;
397 Serial.print("Status: ");
399 Serial.print("opened");
401 Serial.print("closed");
402 Serial.print(", idle");
403 if(get_ajar_status() == SHUT)
404 Serial.println(", shut");
406 Serial.println(", ajar");
409 else if(current_state == ERROR) {
413 else { // timer is useless stop it
419 if(led_delay_cnt >= LED_DELAY) {
422 PORTD = led_table(next_led);
424 if(current_state == OPENING) {
430 else if(current_state == CLOSING) {
438 //********************************************************************//
440 void reset_heartbeat()
442 digitalWrite(HEARTBEAT_PIN, HIGH);
448 digitalWrite(HEARTBEAT_PIN, LOW);
453 digitalWrite(HEARTBEAT_PIN, HIGH);
456 void init_heartbeat()
458 pinMode(HEARTBEAT_PIN, OUTPUT);
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
470 // while running this gets called every ~10ms
471 ISR(TIMER2_COMPA_vect)
474 if(heartbeat_cnt == HEARTBEAT_DURATION)
476 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
482 //********************************************************************//
484 void reset_after_error()
491 current_state = IDLE;
495 current_state = CLOSING;
498 Serial.println("Ok, closing now");
506 current_state = OPENING;
515 current_state = CLOSING;
519 void print_status(byte as)
521 Serial.print("Status: ");
523 Serial.print("opened");
525 Serial.print("closed");
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;
537 Serial.println(", shut");
539 Serial.println(", ajar");
554 current_state = IDLE;
556 // make sure door is locked after reset
561 current_state = CLOSING;
564 Serial.println("init complete");
569 if(Serial.available()) {
570 char command = Serial.read();
572 if(current_state == ERROR && command != CMD_RESET) {
573 Serial.println("Error: last open/close operation took too long!");
575 else if (command == CMD_RESET) {
578 else if (command == CMD_OPEN) {
579 if(current_state == IDLE) {
581 Serial.println("Already open");
584 Serial.println("Ok");
588 Serial.println("Error: Operation in progress");
590 else if (command == CMD_CLOSE) {
591 if(current_state == IDLE) {
593 Serial.println("Already closed");
596 Serial.println("Ok");
600 Serial.println("Error: Operation in progress");
602 else if (command == CMD_TOGGLE) {
603 if(current_state == IDLE) {
608 Serial.println("Ok");
611 Serial.println("Error: Operation in progress");
613 else if (command == CMD_STATUS)
614 print_status(get_ajar_status());
616 Serial.println("Error: unknown command");
618 if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
619 Serial.println("open forced manually");
622 if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
623 Serial.println("close forced manually");
626 if(current_state == IDLE) {
636 byte a = get_ajar_status();
637 if(a != ajar_last_state) {