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 //OCR0A = 255; // 1+255 = 256 -> 12.8us @ 20 MHz
116 TCNT0 = 0; // reseting timer
117 TIMSK0 = 1<<OCF0A; // enable Interrupt
121 void stop_debounce_timer()
124 TCCR0B = 0; // no clock source
125 TIMSK0 = 0; // disable timer interrupt
128 ISR(TIMER0_COMPA_vect)
130 if(((debounce_state & DEBOUNCE_OPEN) && manual_open_pressed()) ||
131 ((debounce_state & DEBOUNCE_CLOSE) && manual_close_pressed())) {
136 debounce_state |= DEBOUNCE_FINISHED;
138 debounce_cnt = DEBOUNCE_DELAY;
141 boolean manual_open()
143 if(manual_open_pressed()) {
144 if(debounce_state & DEBOUNCE_CLOSE) {
145 stop_debounce_timer();
146 debounce_state = DEBOUNCE_IDLE;
150 if(debounce_state == DEBOUNCE_IDLE) {
151 debounce_state = DEBOUNCE_OPEN;
152 start_debounce_timer();
154 else if(debounce_state & DEBOUNCE_FINISHED) {
155 stop_debounce_timer();
156 debounce_state = DEBOUNCE_IDLE;
160 else if(debounce_state & DEBOUNCE_OPEN) {
161 stop_debounce_timer();
162 debounce_state = DEBOUNCE_IDLE;
168 boolean manual_close()
170 if(manual_close_pressed()) {
171 if(debounce_state & DEBOUNCE_OPEN) {
172 stop_debounce_timer();
173 debounce_state = DEBOUNCE_IDLE;
177 if(debounce_state == DEBOUNCE_IDLE) {
178 debounce_state = DEBOUNCE_CLOSE;
179 start_debounce_timer();
181 else if(debounce_state & DEBOUNCE_FINISHED) {
182 stop_debounce_timer();
183 debounce_state = DEBOUNCE_IDLE;
187 else if(debounce_state & DEBOUNCE_CLOSE) {
188 stop_debounce_timer();
189 debounce_state = DEBOUNCE_IDLE;
195 //********************************************************************//
206 DDRB = 0x0F; // set PortB 3:0 as output
210 byte step_table(byte step)
212 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
228 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
229 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
235 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
236 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
240 byte led_table(byte led)
242 switch(led) { // xxxx xx00, leave RxD and TxD to 0
255 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
256 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
261 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
262 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
267 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
268 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
269 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
272 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
273 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
279 void start_step_timer()
281 // timer 1: 2 ms, between stepper output state changes
282 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
283 TCCR1B = 1<<WGM12 | 1<<CS12; //
284 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
285 //OCR1A = 155; // (1+155)*256 = 40000 -> 2 ms @ 20 MHz
286 TCNT1 = 0; // reseting timer
287 TIMSK1 = 1<<OCIE1A; // enable Interrupt
290 void start_wait_timer()
292 // timer1: 250 ms, minimal delay between subsequent open/close
293 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
295 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
296 //OCR1A = 19530; // (1+19530)*256 = 5000000 -> 250 ms @ 20 MHz
297 TCNT1 = 0; // reseting timer
298 TIMSK1 = 1<<OCIE1A; // enable Interrupt
301 void start_error_timer()
303 // timer1: 500 ms, blinking leds with 1 Hz
304 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
305 TCCR1B = 1<<WGM12 | 1<<CS12; //
306 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
307 //OCR1A = 39061; // (1+39061)*256 = 10000000 -> 500 ms @ 20 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;
367 Serial.print("Status: ");
369 Serial.print("opened");
371 Serial.print("closed");
372 Serial.println(", idle");
375 else if(current_state == ERROR) {
379 else { // timer is useless stop it
385 if(led_delay_cnt >= LED_DELAY) {
388 PORTD = led_table(next_led);
390 if(current_state == OPENING) {
396 else if(current_state == CLOSING) {
404 //********************************************************************//
406 void reset_heartbeat()
408 digitalWrite(HEARTBEAT_PIN, HIGH);
414 digitalWrite(HEARTBEAT_PIN, LOW);
419 digitalWrite(HEARTBEAT_PIN, HIGH);
422 void init_heartbeat()
424 pinMode(HEARTBEAT_PIN, OUTPUT);
426 // timer 2: ~10 ms, timebase for heartbeat signal
427 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
428 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
429 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
430 //OCR2A = 194; // (1+194)*1024 = 199680 -> ~10 ms @ 20 MHz
431 TCNT2 = 0; // reseting timer
432 TIMSK2 = 1<<OCIE2A; // enable Interrupt
436 // while running this gets called every ~10ms
437 ISR(TIMER2_COMPA_vect)
440 if(heartbeat_cnt == HEARTBEAT_DURATION)
442 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
448 //********************************************************************//
450 void reset_after_error()
457 current_state = IDLE;
461 current_state = CLOSING;
464 Serial.println("Ok, closing now");
472 current_state = OPENING;
481 current_state = CLOSING;
487 Serial.print("Status: ");
489 Serial.print("opened");
491 Serial.print("closed");
495 switch(current_state) {
496 case IDLE: Serial.println(", idle"); break;
497 case OPENING: Serial.println(", opening"); break;
498 case CLOSING: Serial.println(", closing"); break;
499 case WAIT: Serial.println(", waiting"); break;
500 default: Serial.println(", <undefined state>"); break;
516 current_state = IDLE;
518 // make sure door is locked after reset
523 current_state = CLOSING;
530 if(Serial.available()) {
531 char command = Serial.read();
533 if(current_state == ERROR && command != CMD_RESET) {
534 Serial.println("Error: last open/close operation took too long!");
536 else if (command == CMD_RESET) {
539 else if (command == CMD_OPEN) {
540 if(current_state == IDLE) {
542 Serial.println("Already open");
545 Serial.println("Ok");
549 Serial.println("Error: Operation in progress");
551 else if (command == CMD_CLOSE) {
552 if(current_state == IDLE) {
554 Serial.println("Already closed");
557 Serial.println("Ok");
561 Serial.println("Error: Operation in progress");
563 else if (command == CMD_TOGGLE) {
564 if(current_state == IDLE) {
569 Serial.println("Ok");
572 Serial.println("Error: Operation in progress");
574 else if (command == CMD_STATUS)
577 Serial.println("Error: unknown command");
579 if(manual_open() && !is_opened() && (current_state == IDLE || current_state == ERROR)) {
580 Serial.println("open forced manually");
583 if(manual_close() && !is_closed() && (current_state == IDLE || current_state == ERROR)) {
584 Serial.println("close forced manually");
587 if (current_state == IDLE) {