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)
25 #define MANUAL_OPEN_PIN 12 // keys for manual open and close
26 #define MANUAL_CLOSE_PIN 13 //
27 #define DEBOUNCE_DELAY 6250 // * 16us = 100ms
28 #define DEBOUNCE_IDLE 0 // currently no debouncing
29 #define DEBOUNCE_OPEN 1 // debouncing open key
30 #define DEBOUNCE_CLOSE 2 // debouncing close key
31 #define DEBOUNCE_FINISHED 4 // debouncing finished
35 #define IDLE 0 // close and open may be called
36 #define OPENING 1 // opening, only 's' command is allowed
37 #define CLOSING 2 // closing, onyl 's' command is allowed
38 #define WAIT 3 // wait some time after open or close and hold last step
39 #define ERROR 4 // an error occured
43 #define CMD_TOGGLE 't'
44 #define CMD_STATUS 's'
47 #define STEPPER_OFF 0x30
48 byte current_state = IDLE; // current state of internal state machine
49 byte next_step = 0; // step counter 0 .. 3
50 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
51 int timeout_cnt = 0; // counts up to MOVING_TIMEOUT
53 //********************************************************************//
57 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
58 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
60 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
61 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
66 if(digitalRead(LIMIT_OPENED_PIN))
74 if(digitalRead(LIMIT_CLOSED_PIN))
84 pinMode(AJAR_PIN, INPUT); // set pin to input
85 digitalWrite(AJAR_PIN, HIGH); // turn on pullup resistors
88 boolean get_ajar_status() // shut = true, ajar = false
90 if(digitalRead(AJAR_PIN))
100 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
101 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
103 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
104 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
106 debounce_state = DEBOUNCE_IDLE;
107 debounce_cnt = DEBOUNCE_DELAY;
110 boolean manual_open_pressed()
112 if(digitalRead(MANUAL_OPEN_PIN))
118 boolean manual_close_pressed()
120 if(digitalRead(MANUAL_CLOSE_PIN))
126 void start_debounce_timer() // this breaks millis() function, but who cares
128 debounce_cnt = DEBOUNCE_DELAY;
130 TCCR0A = 0; // no prescaler, WGM = 0 (normal)
132 OCR0A = 255; // 1+255 = 256 -> 16us @ 16 MHz
133 //OCR0A = 255; // 1+255 = 256 -> 12.8us @ 20 MHz
134 TCNT0 = 0; // reseting timer
135 TIMSK0 = 1<<OCF0A; // enable Interrupt
139 void stop_debounce_timer()
142 TCCR0B = 0; // no clock source
143 TIMSK0 = 0; // disable timer interrupt
146 ISR(TIMER0_COMPA_vect)
148 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
149 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
154 debounce_state |= DEBOUNCE_FINISHED;
156 debounce_cnt = DEBOUNCE_DELAY;
159 boolean manual_open()
161 if(manual_open_pressed()) {
162 if(debounce_state & DEBOUNCE_CLOSE) {
163 stop_debounce_timer();
164 debounce_state = DEBOUNCE_IDLE;
168 if(debounce_state == DEBOUNCE_IDLE) {
169 debounce_state = DEBOUNCE_OPEN;
170 start_debounce_timer();
172 else if(debounce_state & DEBOUNCE_FINISHED) {
173 stop_debounce_timer();
174 debounce_state = DEBOUNCE_IDLE;
178 else if(debounce_state & DEBOUNCE_OPEN) {
179 stop_debounce_timer();
180 debounce_state = DEBOUNCE_IDLE;
186 boolean manual_close()
188 if(manual_close_pressed()) {
189 if(debounce_state & DEBOUNCE_OPEN) {
190 stop_debounce_timer();
191 debounce_state = DEBOUNCE_IDLE;
195 if(debounce_state == DEBOUNCE_IDLE) {
196 debounce_state = DEBOUNCE_CLOSE;
197 start_debounce_timer();
199 else if(debounce_state & DEBOUNCE_FINISHED) {
200 stop_debounce_timer();
201 debounce_state = DEBOUNCE_IDLE;
205 else if(debounce_state & DEBOUNCE_CLOSE) {
206 stop_debounce_timer();
207 debounce_state = DEBOUNCE_IDLE;
213 //********************************************************************//
224 DDRB = 0x0F; // set PortB 3:0 as output
228 byte step_table(byte step)
230 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
246 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
247 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
253 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
254 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
258 byte led_table(byte led)
260 switch(led) { // xxxx xx00, leave RxD and TxD to 0
273 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
274 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
279 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
280 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
285 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
286 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
287 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
290 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
291 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
297 void start_step_timer()
299 // timer 1: 2 ms, between stepper output state changes
300 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
301 TCCR1B = 1<<WGM12 | 1<<CS12; //
302 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
303 //OCR1A = 155; // (1+155)*256 = 40000 -> 2 ms @ 20 MHz
304 TCNT1 = 0; // reseting timer
305 TIMSK1 = 1<<OCIE1A; // enable Interrupt
308 void start_wait_timer()
310 // timer1: 250 ms, minimal delay between subsequent open/close
311 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
313 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
314 //OCR1A = 19530; // (1+19530)*256 = 5000000 -> 250 ms @ 20 MHz
315 TCNT1 = 0; // reseting timer
316 TIMSK1 = 1<<OCIE1A; // enable Interrupt
319 void start_error_timer()
321 // timer1: 500 ms, blinking leds with 1 Hz
322 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
323 TCCR1B = 1<<WGM12 | 1<<CS12; //
324 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
325 //OCR1A = 39061; // (1+39061)*256 = 10000000 -> 500 ms @ 20 MHz
326 TCNT1 = 0; // reseting timer
327 TIMSK1 = 1<<OCIE1A; // enable Interrupt
330 void stop_timer() // stop the timer
333 TCCR1B = 0; // no clock source
334 TIMSK1 = 0; // disable timer interrupt
337 ISR(TIMER1_COMPA_vect)
339 // check if limit switch is active
340 if((current_state == OPENING && is_opened()) ||
341 (current_state == CLOSING && is_closed()))
345 if(current_state == OPENING)
350 current_state = WAIT;
355 if(current_state == OPENING || current_state == CLOSING) {
357 if(timeout_cnt >= MOVING_TIMEOUT) {
360 current_state = ERROR;
361 Serial.println("Error: open/close took too long!");
368 if(current_state == OPENING) { // next step (open)
369 PORTB = step_table(next_step);
374 else if(current_state == CLOSING) { // next step (close)
375 PORTB = step_table(next_step);
381 else if(current_state == WAIT) { // wait after last open/close finished -> idle
384 current_state = IDLE;
385 Serial.print("Status: ");
387 Serial.print("opened");
389 Serial.print("closed");
390 Serial.print(", idle");
391 if(get_ajar_status())
392 Serial.println(", shut");
394 Serial.println(", ajar");
397 else if(current_state == ERROR) {
401 else { // timer is useless stop it
407 if(led_delay_cnt >= LED_DELAY) {
410 PORTD = led_table(next_led);
412 if(current_state == OPENING) {
418 else if(current_state == CLOSING) {
426 //********************************************************************//
428 void reset_heartbeat()
430 digitalWrite(HEARTBEAT_PIN, HIGH);
436 digitalWrite(HEARTBEAT_PIN, LOW);
441 digitalWrite(HEARTBEAT_PIN, HIGH);
444 void init_heartbeat()
446 pinMode(HEARTBEAT_PIN, OUTPUT);
448 // timer 2: ~10 ms, timebase for heartbeat signal
449 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
450 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
451 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
452 //OCR2A = 194; // (1+194)*1024 = 199680 -> ~10 ms @ 20 MHz
453 TCNT2 = 0; // reseting timer
454 TIMSK2 = 1<<OCIE2A; // enable Interrupt
458 // while running this gets called every ~10ms
459 ISR(TIMER2_COMPA_vect)
462 if(heartbeat_cnt == HEARTBEAT_DURATION)
464 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
470 //********************************************************************//
472 void reset_after_error()
479 current_state = IDLE;
483 current_state = CLOSING;
486 Serial.println("Ok, closing now");
494 current_state = OPENING;
503 current_state = CLOSING;
509 Serial.print("Status: ");
511 Serial.print("opened");
513 Serial.print("closed");
517 switch(current_state) {
518 case IDLE: Serial.print(", idle"); break;
519 case OPENING: Serial.print(", opening"); break;
520 case CLOSING: Serial.print(", closing"); break;
521 case WAIT: Serial.print(", waiting"); break;
522 default: Serial.print(", <undefined state>"); break;
524 if(get_ajar_status())
525 Serial.println(", shut");
527 Serial.println(", ajar");
542 current_state = IDLE;
544 // make sure door is locked after reset
549 current_state = CLOSING;
552 Serial.println("init complete");
557 if(Serial.available()) {
558 char command = Serial.read();
560 if(current_state == ERROR && command != CMD_RESET) {
561 Serial.println("Error: last open/close operation took too long!");
563 else if (command == CMD_RESET) {
566 else if (command == CMD_OPEN) {
567 if(current_state == IDLE) {
569 Serial.println("Already open");
572 Serial.println("Ok");
576 Serial.println("Error: Operation in progress");
578 else if (command == CMD_CLOSE) {
579 if(current_state == IDLE) {
581 Serial.println("Already closed");
584 Serial.println("Ok");
588 Serial.println("Error: Operation in progress");
590 else if (command == CMD_TOGGLE) {
591 if(current_state == IDLE) {
596 Serial.println("Ok");
599 Serial.println("Error: Operation in progress");
601 else if (command == CMD_STATUS)
604 Serial.println("Error: unknown command");
606 if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
607 Serial.println("open forced manually");
610 if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
611 Serial.println("close forced manually");
614 if (current_state == IDLE) {