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;
362 Serial.print("Status: ");
364 Serial.print("opened");
366 Serial.print("closed");
367 Serial.println(", idle");
370 else if(current_state == ERROR) {
374 else { // timer is useless stop it
380 if(led_delay_cnt >= LED_DELAY) {
383 PORTD = led_table(next_led);
385 if(current_state == OPENING) {
391 else if(current_state == CLOSING) {
399 //********************************************************************//
401 void reset_heartbeat()
403 digitalWrite(HEARTBEAT_PIN, HIGH);
409 digitalWrite(HEARTBEAT_PIN, LOW);
414 digitalWrite(HEARTBEAT_PIN, HIGH);
417 void init_heartbeat()
419 pinMode(HEARTBEAT_PIN, OUTPUT);
421 // timer 2: ~10 ms, timebase for heartbeat signal
422 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
423 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
424 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
425 TCNT2 = 0; // reseting timer
426 TIMSK2 = 1<<OCIE2A; // enable Interrupt
430 // while running this gets called every ~10ms
431 ISR(TIMER2_COMPA_vect)
434 if(heartbeat_cnt == HEARTBEAT_DURATION)
436 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
442 //********************************************************************//
444 void reset_after_error()
451 current_state = IDLE;
455 current_state = CLOSING;
458 Serial.println("Ok, closing now");
466 current_state = OPENING;
475 current_state = CLOSING;
481 Serial.print("Status: ");
483 Serial.print("opened");
485 Serial.print("closed");
489 switch(current_state) {
490 case IDLE: Serial.println(", idle"); break;
491 case OPENING: Serial.println(", opening"); break;
492 case CLOSING: Serial.println(", closing"); break;
493 case WAIT: Serial.println(", waiting"); break;
494 default: Serial.println(", <undefined state>"); break;
510 current_state = IDLE;
512 // make sure door is locked after reset
517 current_state = CLOSING;
524 if(Serial.available()) {
525 char command = Serial.read();
527 if(current_state == ERROR && command != CMD_RESET) {
528 Serial.println("Error: last open/close operation took too long!");
530 else if (command == CMD_RESET) {
533 else if (command == CMD_OPEN) {
534 if(current_state == IDLE) {
536 Serial.println("Already open");
539 Serial.println("Ok");
543 Serial.println("Error: Operation in progress");
545 else if (command == CMD_CLOSE) {
546 if(current_state == IDLE) {
548 Serial.println("Already closed");
551 Serial.println("Ok");
555 Serial.println("Error: Operation in progress");
557 else if (command == CMD_STATUS)
560 Serial.println("Error: unknown command");
562 if(manual_open() && !is_opened() && current_state == IDLE) {
563 Serial.println("open forced manually");
566 if(manual_close() && !is_closed() && current_state == IDLE) {
567 Serial.println("close forced manually");
570 if (current_state == IDLE) {