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 //
26 #define IDLE 0 // close and open may be called
27 #define OPENING 1 // opening, only 's' command is allowed
28 #define CLOSING 2 // closing, onyl 's' command is allowed
29 #define WAIT 3 // wait some time after open or close and hold last step
30 #define ERROR 4 // an error occured
34 #define CMD_STATUS 's'
37 #define STEPPER_OFF 0x30
38 byte current_state = IDLE; // current state of internal state machine
39 byte next_step = 0; // step counter 0 .. 3
40 #define MOVING_TIMEOUT 1600 // *2 ms, in case limit switches don't work stop and report an error
41 int timeout_cnt = 0; // counts up to MOVING_TIMEOUT
43 //********************************************************************//
47 pinMode(LIMIT_OPENED_PIN, INPUT); // set pin to input
48 digitalWrite(LIMIT_OPENED_PIN, HIGH); // turn on pullup resistors
50 pinMode(LIMIT_CLOSED_PIN, INPUT); // set pin to input
51 digitalWrite(LIMIT_CLOSED_PIN, HIGH); // turn on pullup resistors
56 if(digitalRead(LIMIT_OPENED_PIN))
64 if(digitalRead(LIMIT_CLOSED_PIN))
74 pinMode(MANUAL_OPEN_PIN, INPUT); // set pin to input
75 digitalWrite(MANUAL_OPEN_PIN, HIGH); // turn on pullup resistors
77 pinMode(MANUAL_CLOSE_PIN, INPUT); // set pin to input
78 digitalWrite(MANUAL_CLOSE_PIN, HIGH); // turn on pullup resistors
83 if(digitalRead(MANUAL_OPEN_PIN))
89 boolean manual_close()
91 if(digitalRead(MANUAL_CLOSE_PIN))
97 //********************************************************************//
108 DDRB = 0x0F; // set PortB 3:0 as output
112 byte step_table(byte step)
114 switch(step) { // 0011 xxxx, manual keys pull-ups stay active
130 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
131 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
137 pinMode(LEDS_GREEN_COMMON_PIN, OUTPUT);
138 pinMode(LEDS_RED_COMMON_PIN, OUTPUT);
142 byte led_table(byte led)
144 switch(led) { // xxxx xx00, leave RxD and TxD to 0
157 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
158 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
163 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
164 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
169 if(digitalRead(LEDS_GREEN_COMMON_PIN) == HIGH) {
170 digitalWrite(LEDS_GREEN_COMMON_PIN, LOW);
171 digitalWrite(LEDS_RED_COMMON_PIN, HIGH);
174 digitalWrite(LEDS_GREEN_COMMON_PIN, HIGH);
175 digitalWrite(LEDS_RED_COMMON_PIN, LOW);
181 void start_step_timer()
183 // timer 1: 2 ms, between stepper output state changes
184 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
185 TCCR1B = 1<<WGM12 | 1<<CS12; //
186 OCR1A = 124; // (1+124)*256 = 32000 -> 2 ms @ 16 MHz
187 TCNT1 = 0; // reseting timer
188 TIMSK1 = 1<<OCIE1A; // enable Interrupt
191 void start_wait_timer()
193 // timer1: 250 ms, minimal delay between subsequent open/close
194 TCCR1A = 0; // prescaler 1:256, WGM = 0 (normal)
196 OCR1A = 15624; // (1+15624)*256 = 4000000 -> 250 ms @ 16 MHz
197 TCNT1 = 0; // reseting timer
198 TIMSK1 = 1<<OCIE1A; // enable Interrupt
201 void start_error_timer()
203 // timer1: 500 ms, blinking leds with 1 Hz
204 TCCR1A = 0; // prescaler 1:256, WGM = 4 (CTC)
205 TCCR1B = 1<<WGM12 | 1<<CS12; //
206 OCR1A = 31249; // (1+31249)*256 = 8000000 -> 500 ms @ 16 MHz
207 TCNT1 = 0; // reseting timer
208 TIMSK1 = 1<<OCIE1A; // enable Interrupt
211 void stop_timer() // stop the timer
214 TCCR1B = 0; // no clock source
215 TIMSK1 = 0; // disable timer interrupt
218 ISR(TIMER1_COMPA_vect)
220 // check if limit switch is active
221 if((current_state == OPENING && is_opened()) ||
222 (current_state == CLOSING && is_closed()))
226 if(current_state == OPENING)
231 current_state = WAIT;
236 if(current_state == OPENING || current_state == CLOSING) {
238 if(timeout_cnt >= MOVING_TIMEOUT) {
241 current_state = ERROR;
242 Serial.println("Error: open/close took too long!");
249 if(current_state == OPENING) { // next step (open)
250 PORTB = step_table(next_step);
255 else if(current_state == CLOSING) { // next step (close)
256 PORTB = step_table(next_step);
262 else if(current_state == WAIT) { // wait after last open/close finished -> idle
265 current_state = IDLE;
268 else if(current_state == ERROR) {
272 else { // timer is useless stop it
278 if(led_delay_cnt >= LED_DELAY) {
281 PORTD = led_table(next_led);
283 if(current_state == OPENING) {
289 else if(current_state == CLOSING) {
297 //********************************************************************//
299 void reset_heartbeat()
301 digitalWrite(HEARTBEAT_PIN, HIGH);
307 digitalWrite(HEARTBEAT_PIN, LOW);
312 digitalWrite(HEARTBEAT_PIN, HIGH);
315 void init_heartbeat()
317 pinMode(HEARTBEAT_PIN, OUTPUT);
319 // timer 2: ~10 ms, timebase for heartbeat signal
320 TCCR2A = 1<<WGM21; // prescaler 1:1024, WGM = 2 (CTC)
321 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; //
322 OCR2A = 155; // (1+155)*1024 = 159744 -> ~10 ms @ 16 MHz
323 TCNT2 = 0; // reseting timer
324 TIMSK2 = 1<<OCIE2A; // enable Interrupt
328 // while running this gets called every ~10ms
329 ISR(TIMER2_COMPA_vect)
332 if(heartbeat_cnt == HEARTBEAT_DURATION)
334 else if(heartbeat_cnt >= HEARTBEAT_DELAY) {
340 //********************************************************************//
342 void reset_after_error()
349 current_state = IDLE;
353 current_state = CLOSING;
356 Serial.println("Ok, closing now");
362 Serial.println("Already open");
369 current_state = OPENING;
371 Serial.println("Ok");
377 Serial.println("Already closed");
384 current_state = CLOSING;
386 Serial.println("Ok");
391 Serial.print("Status: ");
393 Serial.print("opened");
395 Serial.print("closed");
399 switch(current_state) {
400 case IDLE: Serial.println(", idle"); break;
401 case OPENING: Serial.println(", opening"); break;
402 case CLOSING: Serial.println(", closing"); break;
403 case WAIT: Serial.println(", waiting"); break;
404 default: Serial.println(", <undefined state>"); break;
420 current_state = IDLE;
422 // make sure door is locked after reset
427 current_state = CLOSING;
434 if(Serial.available()) {
435 char command = Serial.read();
437 if(current_state == ERROR && command != CMD_RESET) {
438 Serial.println("Error: last open/close operation took to long!");
440 else if (command == CMD_RESET) {
443 else if (command == CMD_OPEN) {
444 if(current_state == IDLE)
447 Serial.println("Error: Operation in progress");
449 else if (command == CMD_CLOSE) {
450 if(current_state == IDLE)
453 Serial.println("Error: Operation in progress");
455 else if (command == CMD_STATUS)
458 Serial.println("Error: unknown command");
460 if(manual_open() && !is_opened() && current_state == IDLE) {
461 Serial.println("open forced manually");
464 if(manual_close() && !is_closed() && current_state == IDLE) {
465 Serial.println("close forced manually");
468 if (current_state == IDLE) {