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;
26 #define MANUAL_OPEN_PIN 12 // keys for manual open and close
27 #define MANUAL_CLOSE_PIN 13 //
28 #define DEBOUNCE_DELAY 6250 // * 16us = 100ms
29 #define DEBOUNCE_IDLE 0 // currently no debouncing
30 #define DEBOUNCE_OPEN 1 // debouncing open key
31 #define DEBOUNCE_CLOSE 2 // debouncing close key
32 #define DEBOUNCE_FINISHED 4 // debouncing finished
36 #define IDLE 0 // close and open may be called
37 #define OPENING 1 // opening, only 's' command is allowed
38 #define CLOSING 2 // closing, onyl 's' command is allowed
39 #define WAIT 3 // wait some time after open or close and hold last step
40 #define ERROR 4 // an error occured
44 #define CMD_TOGGLE 't'
45 #define CMD_STATUS 's'
48 #define STEPPER_OFF 0x30
49 byte current_state = IDLE; // current state of internal state machine
50 byte next_step = 0; // step counter 0 .. 3
51 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
52 int timeout_cnt = 0; // counts up to MOVING_TIMEOUT
54 //********************************************************************//
58 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
59 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
61 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
62 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
67 if(digitalRead(LIMIT_OPENED_PIN))
75 if(digitalRead(LIMIT_CLOSED_PIN))
83 boolean get_ajar_status() // shut = true, ajar = false
85 return (digitalRead(AJAR_PIN) == LOW);
90 pinMode(AJAR_PIN, INPUT); // set pin to input
91 digitalWrite(AJAR_PIN, HIGH); // turn on pullup resistors
92 ajar_last_state = get_ajar_status();
99 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
100 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
102 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
103 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
105 debounce_state = DEBOUNCE_IDLE;
106 debounce_cnt = DEBOUNCE_DELAY;
109 boolean manual_open_pressed()
111 if(digitalRead(MANUAL_OPEN_PIN))
117 boolean manual_close_pressed()
119 if(digitalRead(MANUAL_CLOSE_PIN))
125 void start_debounce_timer() // this breaks millis() function, but who cares
127 debounce_cnt = DEBOUNCE_DELAY;
129 TCCR0A = 0; // no prescaler, WGM = 0 (normal)
131 OCR0A = 255; // 1+255 = 256 -> 16us @ 16 MHz
132 //OCR0A = 255; // 1+255 = 256 -> 12.8us @ 20 MHz
133 TCNT0 = 0; // reseting timer
134 TIMSK0 = 1<<OCF0A; // enable Interrupt
138 void stop_debounce_timer()
141 TCCR0B = 0; // no clock source
142 TIMSK0 = 0; // disable timer interrupt
145 ISR(TIMER0_COMPA_vect)
147 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
148 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
153 debounce_state |= DEBOUNCE_FINISHED;
155 debounce_cnt = DEBOUNCE_DELAY;
158 boolean manual_open()
160 if(manual_open_pressed()) {
161 if(debounce_state & DEBOUNCE_CLOSE) {
162 stop_debounce_timer();
163 debounce_state = DEBOUNCE_IDLE;
167 if(debounce_state == DEBOUNCE_IDLE) {
168 debounce_state = DEBOUNCE_OPEN;
169 start_debounce_timer();
171 else if(debounce_state & DEBOUNCE_FINISHED) {
172 stop_debounce_timer();
173 debounce_state = DEBOUNCE_IDLE;
177 else if(debounce_state & DEBOUNCE_OPEN) {
178 stop_debounce_timer();
179 debounce_state = DEBOUNCE_IDLE;
185 boolean manual_close()
187 if(manual_close_pressed()) {
188 if(debounce_state & DEBOUNCE_OPEN) {
189 stop_debounce_timer();
190 debounce_state = DEBOUNCE_IDLE;
194 if(debounce_state == DEBOUNCE_IDLE) {
195 debounce_state = DEBOUNCE_CLOSE;
196 start_debounce_timer();
198 else if(debounce_state & DEBOUNCE_FINISHED) {
199 stop_debounce_timer();
200 debounce_state = DEBOUNCE_IDLE;
204 else if(debounce_state & DEBOUNCE_CLOSE) {
205 stop_debounce_timer();
206 debounce_state = DEBOUNCE_IDLE;
212 //********************************************************************//
223 DDRB = 0x0F; // set PortB 3:0 as output
227 byte step_table(byte step)
229 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
245 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
246 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
252 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
253 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
257 byte led_table(byte led)
259 switch(led) { // xxxx xx00, leave RxD and TxD to 0
272 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
273 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
278 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
279 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
284 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
285 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
286 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
289 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
290 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
296 void start_step_timer()
298 // timer 1: 2 ms, between stepper output state changes
299 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
300 TCCR1B = 1<<WGM12 | 1<<CS12; //
301 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
302 //OCR1A = 155; // (1+155)*256 = 40000 -> 2 ms @ 20 MHz
303 TCNT1 = 0; // reseting timer
304 TIMSK1 = 1<<OCIE1A; // enable Interrupt
307 void start_wait_timer()
309 // timer1: 250 ms, minimal delay between subsequent open/close
310 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
312 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
313 //OCR1A = 19530; // (1+19530)*256 = 5000000 -> 250 ms @ 20 MHz
314 TCNT1 = 0; // reseting timer
315 TIMSK1 = 1<<OCIE1A; // enable Interrupt
318 void start_error_timer()
320 // timer1: 500 ms, blinking leds with 1 Hz
321 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
322 TCCR1B = 1<<WGM12 | 1<<CS12; //
323 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
324 //OCR1A = 39061; // (1+39061)*256 = 10000000 -> 500 ms @ 20 MHz
325 TCNT1 = 0; // reseting timer
326 TIMSK1 = 1<<OCIE1A; // enable Interrupt
329 void stop_timer() // stop the timer
332 TCCR1B = 0; // no clock source
333 TIMSK1 = 0; // disable timer interrupt
336 ISR(TIMER1_COMPA_vect)
338 // check if limit switch is active
339 if((current_state == OPENING && is_opened()) ||
340 (current_state == CLOSING && is_closed()))
344 if(current_state == OPENING)
349 current_state = WAIT;
354 if(current_state == OPENING || current_state == CLOSING) {
356 if(timeout_cnt >= MOVING_TIMEOUT) {
359 current_state = ERROR;
360 Serial.println("Error: open/close took too long!");
367 if(current_state == OPENING) { // next step (open)
368 PORTB = step_table(next_step);
373 else if(current_state == CLOSING) { // next step (close)
374 PORTB = step_table(next_step);
380 else if(current_state == WAIT) { // wait after last open/close finished -> idle
383 current_state = IDLE;
384 Serial.print("Status: ");
386 Serial.print("opened");
388 Serial.print("closed");
389 Serial.print(", idle");
390 if(get_ajar_status())
391 Serial.println(", shut");
393 Serial.println(", ajar");
396 else if(current_state == ERROR) {
400 else { // timer is useless stop it
406 if(led_delay_cnt >= LED_DELAY) {
409 PORTD = led_table(next_led);
411 if(current_state == OPENING) {
417 else if(current_state == CLOSING) {
425 //********************************************************************//
427 void reset_heartbeat()
429 digitalWrite(HEARTBEAT_PIN, HIGH);
435 digitalWrite(HEARTBEAT_PIN, LOW);
440 digitalWrite(HEARTBEAT_PIN, HIGH);
443 void init_heartbeat()
445 pinMode(HEARTBEAT_PIN, OUTPUT);
447 // timer 2: ~10 ms, timebase for heartbeat signal
448 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
449 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
450 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
451 //OCR2A = 194; // (1+194)*1024 = 199680 -> ~10 ms @ 20 MHz
452 TCNT2 = 0; // reseting timer
453 TIMSK2 = 1<<OCIE2A; // enable Interrupt
457 // while running this gets called every ~10ms
458 ISR(TIMER2_COMPA_vect)
461 if(heartbeat_cnt == HEARTBEAT_DURATION)
463 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
469 //********************************************************************//
471 void reset_after_error()
478 current_state = IDLE;
482 current_state = CLOSING;
485 Serial.println("Ok, closing now");
493 current_state = OPENING;
502 current_state = CLOSING;
508 Serial.print("Status: ");
510 Serial.print("opened");
512 Serial.print("closed");
516 switch(current_state) {
517 case IDLE: Serial.print(", idle"); break;
518 case OPENING: Serial.print(", opening"); break;
519 case CLOSING: Serial.print(", closing"); break;
520 case WAIT: Serial.print(", waiting"); break;
521 default: Serial.print(", <undefined state>"); break;
523 if(get_ajar_status())
524 Serial.println(", shut");
526 Serial.println(", ajar");
541 current_state = IDLE;
543 // make sure door is locked after reset
548 current_state = CLOSING;
551 Serial.println("init complete");
556 if(Serial.available()) {
557 char command = Serial.read();
559 if(current_state == ERROR && command != CMD_RESET) {
560 Serial.println("Error: last open/close operation took too long!");
562 else if (command == CMD_RESET) {
565 else if (command == CMD_OPEN) {
566 if(current_state == IDLE) {
568 Serial.println("Already open");
571 Serial.println("Ok");
575 Serial.println("Error: Operation in progress");
577 else if (command == CMD_CLOSE) {
578 if(current_state == IDLE) {
580 Serial.println("Already closed");
583 Serial.println("Ok");
587 Serial.println("Error: Operation in progress");
589 else if (command == CMD_TOGGLE) {
590 if(current_state == IDLE) {
595 Serial.println("Ok");
598 Serial.println("Error: Operation in progress");
600 else if (command == CMD_STATUS)
603 Serial.println("Error: unknown command");
605 if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
606 Serial.println("open forced manually");
609 if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
610 Serial.println("close forced manually");
613 if(current_state == IDLE) {
623 boolean a = get_ajar_status();
624 if (a != ajar_last_state)