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_TOGGLE 't'
42 #define CMD_STATUS 's'
45 #define STEPPER_OFF 0x30
46 byte current_state = IDLE; // current state of internal state machine
47 byte next_step = 0; // step counter 0 .. 3
48 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
49 int timeout_cnt = 0; // counts up to MOVING_TIMEOUT
51 //********************************************************************//
55 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
56 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
58 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
59 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
64 if(digitalRead(LIMIT_OPENED_PIN))
72 if(digitalRead(LIMIT_CLOSED_PIN))
82 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
83 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
85 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
86 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
88 debounce_state = DEBOUNCE_IDLE;
89 debounce_cnt = DEBOUNCE_DELAY;
92 boolean manual_open_pressed()
94 if(digitalRead(MANUAL_OPEN_PIN))
100 boolean manual_close_pressed()
102 if(digitalRead(MANUAL_CLOSE_PIN))
108 void start_debounce_timer() // this breaks millis() function, but who cares
110 debounce_cnt = DEBOUNCE_DELAY;
112 TCCR0A = 0; // no prescaler, WGM = 0 (normal)
114 OCR0A = 255; // 1+255 = 256 -> 16us @ 16 MHz
115 TCNT0 = 0; // reseting timer
116 TIMSK0 = 1<<OCF0A; // enable Interrupt
120 void stop_debounce_timer()
123 TCCR0B = 0; // no clock source
124 TIMSK0 = 0; // disable timer interrupt
127 ISR(TIMER0_COMPA_vect)
129 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
130 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
135 debounce_state |= DEBOUNCE_FINISHED;
137 debounce_cnt = DEBOUNCE_DELAY;
140 boolean manual_open()
142 if(manual_open_pressed()) {
143 if(debounce_state & DEBOUNCE_CLOSE) {
144 stop_debounce_timer();
145 debounce_state = DEBOUNCE_IDLE;
149 if(debounce_state == DEBOUNCE_IDLE) {
150 debounce_state = DEBOUNCE_OPEN;
151 start_debounce_timer();
153 else if(debounce_state & DEBOUNCE_FINISHED) {
154 stop_debounce_timer();
155 debounce_state = DEBOUNCE_IDLE;
159 else if(debounce_state & DEBOUNCE_OPEN) {
160 stop_debounce_timer();
161 debounce_state = DEBOUNCE_IDLE;
167 boolean manual_close()
169 if(manual_close_pressed()) {
170 if(debounce_state & DEBOUNCE_OPEN) {
171 stop_debounce_timer();
172 debounce_state = DEBOUNCE_IDLE;
176 if(debounce_state == DEBOUNCE_IDLE) {
177 debounce_state = DEBOUNCE_CLOSE;
178 start_debounce_timer();
180 else if(debounce_state & DEBOUNCE_FINISHED) {
181 stop_debounce_timer();
182 debounce_state = DEBOUNCE_IDLE;
186 else if(debounce_state & DEBOUNCE_CLOSE) {
187 stop_debounce_timer();
188 debounce_state = DEBOUNCE_IDLE;
194 //********************************************************************//
205 DDRB = 0x0F; // set PortB 3:0 as output
209 byte step_table(byte step)
211 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
227 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
228 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
234 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
235 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
239 byte led_table(byte led)
241 switch(led) { // xxxx xx00, leave RxD and TxD to 0
254 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
255 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
260 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
261 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
266 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
267 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
268 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
271 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
272 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
278 void start_step_timer()
280 // timer 1: 2 ms, between stepper output state changes
281 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
282 TCCR1B = 1<<WGM12 | 1<<CS12; //
283 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
284 TCNT1 = 0; // reseting timer
285 TIMSK1 = 1<<OCIE1A; // enable Interrupt
288 void start_wait_timer()
290 // timer1: 250 ms, minimal delay between subsequent open/close
291 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
293 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
294 TCNT1 = 0; // reseting timer
295 TIMSK1 = 1<<OCIE1A; // enable Interrupt
298 void start_error_timer()
300 // timer1: 500 ms, blinking leds with 1 Hz
301 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
302 TCCR1B = 1<<WGM12 | 1<<CS12; //
303 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
304 TCNT1 = 0; // reseting timer
305 TIMSK1 = 1<<OCIE1A; // enable Interrupt
308 void stop_timer() // stop the timer
311 TCCR1B = 0; // no clock source
312 TIMSK1 = 0; // disable timer interrupt
315 ISR(TIMER1_COMPA_vect)
317 // check if limit switch is active
318 if((current_state == OPENING && is_opened()) ||
319 (current_state == CLOSING && is_closed()))
323 if(current_state == OPENING)
328 current_state = WAIT;
333 if(current_state == OPENING || current_state == CLOSING) {
335 if(timeout_cnt >= MOVING_TIMEOUT) {
338 current_state = ERROR;
339 Serial.println("Error: open/close took too long!");
346 if(current_state == OPENING) { // next step (open)
347 PORTB = step_table(next_step);
352 else if(current_state == CLOSING) { // next step (close)
353 PORTB = step_table(next_step);
359 else if(current_state == WAIT) { // wait after last open/close finished -> idle
362 current_state = IDLE;
363 Serial.print("Status: ");
365 Serial.print("opened");
367 Serial.print("closed");
368 Serial.println(", idle");
371 else if(current_state == ERROR) {
375 else { // timer is useless stop it
381 if(led_delay_cnt >= LED_DELAY) {
384 PORTD = led_table(next_led);
386 if(current_state == OPENING) {
392 else if(current_state == CLOSING) {
400 //********************************************************************//
402 void reset_heartbeat()
404 digitalWrite(HEARTBEAT_PIN, HIGH);
410 digitalWrite(HEARTBEAT_PIN, LOW);
415 digitalWrite(HEARTBEAT_PIN, HIGH);
418 void init_heartbeat()
420 pinMode(HEARTBEAT_PIN, OUTPUT);
422 // timer 2: ~10 ms, timebase for heartbeat signal
423 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
424 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
425 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
426 TCNT2 = 0; // reseting timer
427 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");
467 current_state = OPENING;
476 current_state = CLOSING;
482 Serial.print("Status: ");
484 Serial.print("opened");
486 Serial.print("closed");
490 switch(current_state) {
491 case IDLE: Serial.println(", idle"); break;
492 case OPENING: Serial.println(", opening"); break;
493 case CLOSING: Serial.println(", closing"); break;
494 case WAIT: Serial.println(", waiting"); break;
495 default: Serial.println(", <undefined state>"); break;
511 current_state = IDLE;
513 // make sure door is locked after reset
518 current_state = CLOSING;
525 if(Serial.available()) {
526 char command = Serial.read();
528 if(current_state == ERROR && command != CMD_RESET) {
529 Serial.println("Error: last open/close operation took too long!");
531 else if (command == CMD_RESET) {
534 else if (command == CMD_OPEN) {
535 if(current_state == IDLE) {
537 Serial.println("Already open");
540 Serial.println("Ok");
544 Serial.println("Error: Operation in progress");
546 else if (command == CMD_CLOSE) {
547 if(current_state == IDLE) {
549 Serial.println("Already closed");
552 Serial.println("Ok");
556 Serial.println("Error: Operation in progress");
558 else if (command == CMD_TOGGLE) {
559 if(current_state == IDLE) {
564 Serial.println("Ok");
567 Serial.println("Error: Operation in progress");
569 else if (command == CMD_STATUS)
572 Serial.println("Error: unknown command");
574 if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
575 Serial.println("open forced manually");
578 if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
579 Serial.println("close forced manually");
582 if (current_state == IDLE) {