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 MANUAL_OPEN_PIN 12 // keys for manual open and close
24 #define MANUAL_CLOSE_PIN 13 //
25 #define DEBOUNCE_DELAY 6250 // * 16us = 100ms
26 #define DEBOUNCE_IDLE 0 // currently no debouncing
27 #define DEBOUNCE_OPEN 1 // debouncing open key
28 #define DEBOUNCE_CLOSE 2 // debouncing close key
29 #define DEBOUNCE_FINISHED 4 // debouncing finished
33 #define IDLE 0 // close and open may be called
34 #define OPENING 1 // opening, only 's' command is allowed
35 #define CLOSING 2 // closing, onyl 's' command is allowed
36 #define WAIT 3 // wait some time after open or close and hold last step
37 #define ERROR 4 // an error occured
41 #define CMD_STATUS 's'
44 #define STEPPER_OFF 0x30
45 byte current_state = IDLE; // current state of internal state machine
46 byte next_step = 0; // step counter 0 .. 3
47 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
48 int timeout_cnt = 0; // counts up to MOVING_TIMEOUT
50 //********************************************************************//
54 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
55 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
57 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
58 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
63 if(digitalRead(LIMIT_OPENED_PIN))
71 if(digitalRead(LIMIT_CLOSED_PIN))
81 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
82 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
84 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
85 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
87 debounce_state = DEBOUNCE_IDLE;
88 debounce_cnt = DEBOUNCE_DELAY;
91 boolean manual_open_pressed()
93 if(digitalRead(MANUAL_OPEN_PIN))
99 boolean manual_close_pressed()
101 if(digitalRead(MANUAL_CLOSE_PIN))
107 void start_debounce_timer() // this breaks millis() function, but who cares
109 debounce_cnt = DEBOUNCE_DELAY;
111 TCCR0A = 0; // no prescaler, WGM = 0 (normal)
113 OCR0A = 255; // 1+255 = 256 -> 16us @ 16 MHz
114 TCNT0 = 0; // reseting timer
115 TIMSK0 = 1<<OCF0A; // enable Interrupt
119 void stop_debounce_timer()
122 TCCR0B = 0; // no clock source
123 TIMSK0 = 0; // disable timer interrupt
126 ISR(TIMER0_COMPA_vect)
128 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
129 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
134 debounce_state |= DEBOUNCE_FINISHED;
136 debounce_cnt = DEBOUNCE_DELAY;
139 boolean manual_open()
141 if(manual_open_pressed()) {
142 if(debounce_state & DEBOUNCE_CLOSE) {
143 stop_debounce_timer();
144 debounce_state = DEBOUNCE_IDLE;
148 if(debounce_state == DEBOUNCE_IDLE) {
149 debounce_state = DEBOUNCE_OPEN;
150 start_debounce_timer();
152 else if(debounce_state & DEBOUNCE_FINISHED) {
153 stop_debounce_timer();
154 debounce_state = DEBOUNCE_IDLE;
158 else if(debounce_state & DEBOUNCE_OPEN) {
159 stop_debounce_timer();
160 debounce_state = DEBOUNCE_IDLE;
166 boolean manual_close()
168 if(manual_close_pressed()) {
169 if(debounce_state & DEBOUNCE_OPEN) {
170 stop_debounce_timer();
171 debounce_state = DEBOUNCE_IDLE;
175 if(debounce_state == DEBOUNCE_IDLE) {
176 debounce_state = DEBOUNCE_CLOSE;
177 start_debounce_timer();
179 else if(debounce_state & DEBOUNCE_FINISHED) {
180 stop_debounce_timer();
181 debounce_state = DEBOUNCE_IDLE;
185 else if(debounce_state & DEBOUNCE_CLOSE) {
186 stop_debounce_timer();
187 debounce_state = DEBOUNCE_IDLE;
193 //********************************************************************//
204 DDRB = 0x0F; // set PortB 3:0 as output
208 byte step_table(byte step)
210 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
226 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
227 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
233 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
234 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
238 byte led_table(byte led)
240 switch(led) { // xxxx xx00, leave RxD and TxD to 0
253 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
254 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
259 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
260 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
265 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
266 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
267 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
270 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
271 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
277 void start_step_timer()
279 // timer 1: 2 ms, between stepper output state changes
280 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
281 TCCR1B = 1<<WGM12 | 1<<CS12; //
282 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
283 TCNT1 = 0; // reseting timer
284 TIMSK1 = 1<<OCIE1A; // enable Interrupt
287 void start_wait_timer()
289 // timer1: 250 ms, minimal delay between subsequent open/close
290 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
292 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
293 TCNT1 = 0; // reseting timer
294 TIMSK1 = 1<<OCIE1A; // enable Interrupt
297 void start_error_timer()
299 // timer1: 500 ms, blinking leds with 1 Hz
300 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
301 TCCR1B = 1<<WGM12 | 1<<CS12; //
302 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
303 TCNT1 = 0; // reseting timer
304 TIMSK1 = 1<<OCIE1A; // enable Interrupt
307 void stop_timer() // stop the timer
310 TCCR1B = 0; // no clock source
311 TIMSK1 = 0; // disable timer interrupt
314 ISR(TIMER1_COMPA_vect)
316 // check if limit switch is active
317 if((current_state == OPENING && is_opened()) ||
318 (current_state == CLOSING && is_closed()))
322 if(current_state == OPENING)
327 current_state = WAIT;
332 if(current_state == OPENING || current_state == CLOSING) {
334 if(timeout_cnt >= MOVING_TIMEOUT) {
337 current_state = ERROR;
338 Serial.println("Error: open/close took too long!");
345 if(current_state == OPENING) { // next step (open)
346 PORTB = step_table(next_step);
351 else if(current_state == CLOSING) { // next step (close)
352 PORTB = step_table(next_step);
358 else if(current_state == WAIT) { // wait after last open/close finished -> idle
361 current_state = IDLE;
364 else if(current_state == ERROR) {
368 else { // timer is useless stop it
374 if(led_delay_cnt >= LED_DELAY) {
377 PORTD = led_table(next_led);
379 if(current_state == OPENING) {
385 else if(current_state == CLOSING) {
393 //********************************************************************//
395 void reset_heartbeat()
397 digitalWrite(HEARTBEAT_PIN, HIGH);
403 digitalWrite(HEARTBEAT_PIN, LOW);
408 digitalWrite(HEARTBEAT_PIN, HIGH);
411 void init_heartbeat()
413 pinMode(HEARTBEAT_PIN, OUTPUT);
415 // timer 2: ~10 ms, timebase for heartbeat signal
416 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
417 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
418 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
419 TCNT2 = 0; // reseting timer
420 TIMSK2 = 1<<OCIE2A; // enable Interrupt
424 // while running this gets called every ~10ms
425 ISR(TIMER2_COMPA_vect)
428 if(heartbeat_cnt == HEARTBEAT_DURATION)
430 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
436 //********************************************************************//
438 void reset_after_error()
445 current_state = IDLE;
449 current_state = CLOSING;
452 Serial.println("Ok, closing now");
460 current_state = OPENING;
469 current_state = CLOSING;
475 Serial.print("Status: ");
477 Serial.print("opened");
479 Serial.print("closed");
483 switch(current_state) {
484 case IDLE: Serial.println(", idle"); break;
485 case OPENING: Serial.println(", opening"); break;
486 case CLOSING: Serial.println(", closing"); break;
487 case WAIT: Serial.println(", waiting"); break;
488 default: Serial.println(", <undefined state>"); break;
504 current_state = IDLE;
506 // make sure door is locked after reset
511 current_state = CLOSING;
518 if(Serial.available()) {
519 char command = Serial.read();
521 if(current_state == ERROR && command != CMD_RESET) {
522 Serial.println("Error: last open/close operation took too long!");
524 else if (command == CMD_RESET) {
527 else if (command == CMD_OPEN) {
528 if(current_state == IDLE) {
530 Serial.println("Already open");
533 Serial.println("Ok");
537 Serial.println("Error: Operation in progress");
539 else if (command == CMD_CLOSE) {
540 if(current_state == IDLE) {
542 Serial.println("Already closed");
545 Serial.println("Ok");
549 Serial.println("Error: Operation in progress");
551 else if (command == CMD_STATUS)
554 Serial.println("Error: unknown command");
556 if(manual_open() && !is_opened() && current_state == IDLE) {
557 Serial.println("open forced manually");
560 if(manual_close() && !is_closed() && current_state == IDLE) {
561 Serial.println("close forced manually");
564 if (current_state == IDLE) {