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 625 // * 16us = 10ms
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 if(digitalRead(HEARTBEAT_PIN))
137 digitalWrite(HEARTBEAT_PIN, LOW);
139 digitalWrite(HEARTBEAT_PIN, HIGH);
141 debounce_cnt = DEBOUNCE_DELAY;
144 boolean manual_open()
146 if(manual_open_pressed()) {
147 if(debounce_state & DEBOUNCE_CLOSE) {
148 stop_debounce_timer();
149 debounce_state = DEBOUNCE_IDLE;
153 if(debounce_state == DEBOUNCE_IDLE) {
154 debounce_state = DEBOUNCE_OPEN;
155 start_debounce_timer();
157 else if(debounce_state & DEBOUNCE_FINISHED) {
158 stop_debounce_timer();
159 debounce_state = DEBOUNCE_IDLE;
164 stop_debounce_timer();
165 debounce_state = DEBOUNCE_IDLE;
171 boolean manual_close()
173 if(manual_close_pressed()) {
174 if(debounce_state & DEBOUNCE_OPEN) {
175 stop_debounce_timer();
176 debounce_state = DEBOUNCE_IDLE;
180 if(debounce_state == DEBOUNCE_IDLE) {
181 debounce_state = DEBOUNCE_CLOSE;
182 start_debounce_timer();
184 else if(debounce_state & DEBOUNCE_FINISHED) {
185 stop_debounce_timer();
186 debounce_state = DEBOUNCE_IDLE;
191 stop_debounce_timer();
192 debounce_state = DEBOUNCE_IDLE;
198 //********************************************************************//
209 DDRB = 0x0F; // set PortB 3:0 as output
213 byte step_table(byte step)
215 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
231 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
232 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
238 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
239 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
243 byte led_table(byte led)
245 switch(led) { // xxxx xx00, leave RxD and TxD to 0
258 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
259 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
264 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
265 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
270 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
271 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
272 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
275 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
276 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
282 void start_step_timer()
284 // timer 1: 2 ms, between stepper output state changes
285 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
286 TCCR1B = 1<<WGM12 | 1<<CS12; //
287 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
288 TCNT1 = 0; // reseting timer
289 TIMSK1 = 1<<OCIE1A; // enable Interrupt
292 void start_wait_timer()
294 // timer1: 250 ms, minimal delay between subsequent open/close
295 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
297 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
298 TCNT1 = 0; // reseting timer
299 TIMSK1 = 1<<OCIE1A; // enable Interrupt
302 void start_error_timer()
304 // timer1: 500 ms, blinking leds with 1 Hz
305 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
306 TCCR1B = 1<<WGM12 | 1<<CS12; //
307 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
308 TCNT1 = 0; // reseting timer
309 TIMSK1 = 1<<OCIE1A; // enable Interrupt
312 void stop_timer() // stop the timer
315 TCCR1B = 0; // no clock source
316 TIMSK1 = 0; // disable timer interrupt
319 ISR(TIMER1_COMPA_vect)
321 // check if limit switch is active
322 if((current_state == OPENING && is_opened()) ||
323 (current_state == CLOSING && is_closed()))
327 if(current_state == OPENING)
332 current_state = WAIT;
337 if(current_state == OPENING || current_state == CLOSING) {
339 if(timeout_cnt >= MOVING_TIMEOUT) {
342 current_state = ERROR;
343 Serial.println("Error: open/close took too long!");
350 if(current_state == OPENING) { // next step (open)
351 PORTB = step_table(next_step);
356 else if(current_state == CLOSING) { // next step (close)
357 PORTB = step_table(next_step);
363 else if(current_state == WAIT) { // wait after last open/close finished -> idle
366 current_state = IDLE;
369 else if(current_state == ERROR) {
373 else { // timer is useless stop it
379 if(led_delay_cnt >= LED_DELAY) {
382 PORTD = led_table(next_led);
384 if(current_state == OPENING) {
390 else if(current_state == CLOSING) {
398 //********************************************************************//
400 void reset_heartbeat()
402 digitalWrite(HEARTBEAT_PIN, HIGH);
408 digitalWrite(HEARTBEAT_PIN, LOW);
413 digitalWrite(HEARTBEAT_PIN, HIGH);
416 void init_heartbeat()
418 pinMode(HEARTBEAT_PIN, OUTPUT);
420 // 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
431 // while running this gets called every ~10ms
432 ISR(TIMER2_COMPA_vect)
435 if(heartbeat_cnt == HEARTBEAT_DURATION)
437 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
443 //********************************************************************//
445 void reset_after_error()
452 current_state = IDLE;
456 current_state = CLOSING;
459 Serial.println("Ok, closing now");
465 Serial.println("Already open");
472 current_state = OPENING;
474 Serial.println("Ok");
480 Serial.println("Already closed");
487 current_state = CLOSING;
489 Serial.println("Ok");
494 Serial.print("Status: ");
496 Serial.print("opened");
498 Serial.print("closed");
502 switch(current_state) {
503 case IDLE: Serial.println(", idle"); break;
504 case OPENING: Serial.println(", opening"); break;
505 case CLOSING: Serial.println(", closing"); break;
506 case WAIT: Serial.println(", waiting"); break;
507 default: Serial.println(", <undefined state>"); break;
523 current_state = IDLE;
525 // make sure door is locked after reset
530 current_state = CLOSING;
537 if(Serial.available()) {
538 char command = Serial.read();
540 if(current_state == ERROR && command != CMD_RESET) {
541 Serial.println("Error: last open/close operation took to long!");
543 else if (command == CMD_RESET) {
546 else if (command == CMD_OPEN) {
547 if(current_state == IDLE)
550 Serial.println("Error: Operation in progress");
552 else if (command == CMD_CLOSE) {
553 if(current_state == IDLE)
556 Serial.println("Error: Operation in progress");
558 else if (command == CMD_STATUS)
561 Serial.println("Error: unknown command");
563 if(manual_open() && !is_opened() && current_state == IDLE) {
564 Serial.println("open forced manually");
567 if(manual_close() && !is_closed() && current_state == IDLE) {
568 Serial.println("close forced manually");
571 if (current_state == IDLE) {