Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // v1.2 -adjusted numsteps for proper door opening
- // v2.0 -removed ultrasound sensor and added second IR motion detector
- // V3.0 -Simplified I/O, replaced ext interrupt with internal timer0 compare interrupt.
- // -Added Oled display
- // -Cleaned up the code. Created functions for some tasks.
- // -Added ir remote.
- // V3.1-Added graphic UI instead of text
- // V3.2-Added screen saver
- // V3.3-Added 10ms break period
- // -Added variable speed. Ended up slowing the top speed, the motor was skipping steps.
- // -Animated the door display
- #include <Adafruit_SSD1306.h>
- //oled uses I2C pins A5 SCL and A4 SDA. A reset pin must be declared in the display declaration
- //if the I2C device does not need reset, pin SDA can be used in the display declaration
- #define OLED_RESET A4
- Adafruit_SSD1306 display(OLED_RESET);
- #if (SSD1306_LCDHEIGHT != 64)
- #error("Height incorrect, please fix Adafruit_SSD1306.h!");
- #endif
- // Stepper definitions
- #define BLU 8 //definition digital 8 pins as pin to control the IN1 (ULN2003)
- #define PNK 9 //definition digital 9 pins as pin to control the IN2 (ULN2003)
- #define YEL 10 //definition digital 10 pins as pin to control the IN3 (ULN2003)
- #define ORG 11 //definition digital 11 pins as pin to control the IN4 (ULN2003)
- volatile byte stepper_control = false; //command/status reg for the stepper interrupt routine.
- // bit 0 = true = run/running
- // bit 1 = direction = 1 clockwise, 0 counterclockwise
- byte clockwise = true; //general flag for motor direction for opening.
- volatile int step_counter = 0; // number of steps to execute, steps left on read
- volatile byte micro_step = 0; //keeps track of the stepper interupt routine state
- #define FAST 1; // full speed would be 0 but the motor can't keep up
- #define MEDIUMFAST 1;
- #define MEDIUM 2;
- #define SLOW 3;
- volatile byte motor_speed = SLOW; //tells the motor interrupt routine how many ticks to skip. 0 is Fast,
- // Control panels LED definitions
- //digital pin LED_BUILTIN as an output to display motion detection status (13)
- // IR motion detection definitions
- #define PIRpin1 7 // IR motion detector input pin
- #define PIRpin2 6 // second IR sensor input pin
- // Control panels Push button
- #define DOOR_MANUAL_PIN 5
- #define CLOSE_BUTTON_PIN 4
- #define OPEN_BUTTON_PIN 3
- // miscaleneous program variables and constants
- byte manual_flag = false; // when true, inhibits automatic operations
- volatile byte ir_new_status = false;
- // Interrupt-based timers. When enabled, count down to zero and clear the enable bit when count = 0.
- // Counters are decremented before checking for zero, so they can start at 0 for a full 16 bit count.
- volatile int timer0 = 0; //door open timer
- volatile int timer1 = 0; //sensor scan timer
- int blank_screen_timer = 0; //screen saver timer. THIS TIMER IS NOT DECREMENTED IN THE INTERRUPT ROUTINE.
- // IT IS DECREMENTED IN THE SENSOR TESTING SECTION OF THE MAIN LOOP @5HZ.
- volatile byte timers_status = 0; //Each bit is a control/status flag for a counter. 1= start/running, 0 = stopped/done
- #define NUM_STEPS 1450 // number of steps before reversing direction
- #define OPEN_DELAY 5000 // 5 seconds. Minimum time door held open
- #define SENSOR_SCAN_DELAY 200 // .2 second between sensor scans
- #define DISPLAY_DELAY 3000 //
- #define SCREENSAVE_DELAY 300 // 300 * .2 secs = 60 secs.
- #define CLOSED 0
- #define OPENING 1
- #define OPENED 2
- #define CLOSING 3
- byte door_state = CLOSED;
- // Register bit masks
- #define BIT0 1
- #define BIT1 2
- #define BIT2 4
- #define BIT3 8
- #define BIT4 16
- #define BIT5 32
- #define BIT6 64
- #define BIT7 128
- //ir receiver
- #include <IRremote.h>
- int RECV_PIN = 2;//The definition of the infrared receiver pin 2
- IRrecv irrecv(RECV_PIN);
- decode_results results;
- byte ir_code; // holds the least significant byte of the received ir code
- void PrintDisplay(String text)
- {
- display.setTextSize(2);
- display.clearDisplay();
- display.setCursor(0, 0); // set the cursor to column 0, line 0
- display.print(text);
- display.display();
- }
- void DisplayStatus(void)
- {
- int door_pos;
- noInterrupts();
- door_pos = step_counter;
- interrupts();
- display.clearDisplay();
- display.setCursor(0, 0); // set the cursor to column 0, line 0
- switch(door_state)
- {
- case CLOSED:
- display.drawRect(50, 0, 40, 64, 1);
- display.fillRect(50, 0, 40, 64, 1);
- break;
- case OPENING:
- display.fillTriangle(5, 0, 5, 16, 21, 8, WHITE);
- display.fillTriangle(21, 0, 21, 16, 37, 8, WHITE);
- display.fillRect(37, 0, 2, 16, WHITE);
- display.drawRect(50, 0, 40, 64, WHITE);
- if(door_pos > (NUM_STEPS - 100))
- {
- display.fillRect(60, 0, 30, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 300))
- {
- display.fillRect(65, 0, 25, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 600))
- {
- display.fillRect(70, 0, 20, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 900))
- {
- display.fillRect(75, 0, 15, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 1200))
- {
- display.fillRect(80, 0, 10, 64, WHITE);
- }else
- {
- display.fillRect(83, 0, 7, 64, WHITE);
- }
- break;
- case OPENED:
- display.drawRect(50, 0, 40, 64, WHITE);
- display.fillRect(86, 0, 4, 64, WHITE);
- break;
- case CLOSING:
- display.fillRect(5, 0, 2, 16, WHITE);
- display.fillTriangle(7, 8, 23, 16, 23, 0, WHITE);
- display.fillTriangle(23, 8, 39, 16, 39, 0, WHITE);
- display.drawRect(50, 0, 40, 64, WHITE);
- if(door_pos > (NUM_STEPS - 100))
- {
- display.fillRect(83, 0, 7, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 300))
- {
- display.fillRect(80, 0, 10, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 600))
- {
- display.fillRect(75, 0, 15, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 900))
- {
- display.fillRect(70, 0, 20, 64, WHITE);
- }else if(door_pos > (NUM_STEPS - 1200))
- {
- display.fillRect(65, 0, 25, 64, WHITE);
- }else
- {
- display.fillRect(60, 0, 30, 64, WHITE);
- }
- break;
- }
- if(digitalRead(PIRpin1))
- {
- display.fillCircle(25, 32, 5, WHITE);
- }else
- {
- display.drawCircle(25, 32, 5, WHITE);
- }
- if(digitalRead(PIRpin2))
- {
- display.fillCircle(115, 32, 5, WHITE);
- }else
- {
- display.drawCircle(115, 32, 5, WHITE);
- }
- if (!manual_flag)
- {
- display.fillTriangle(5, 47, 5, 63, 21, 55, WHITE);
- }
- else
- {
- display.fillRect(5, 47, 16, 16, WHITE);
- }
- display.display();
- }
- int MotorStopped(void) //checks if motor is stopped. returns true if it is stopped.
- {
- if(!bitRead(stepper_control,BIT0) && (micro_step == 0)) //if motor is stopped
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- void MotorStart(int numsteps, int dir)
- {
- motor_speed = SLOW;
- step_counter = numsteps;
- bitWrite(stepper_control,BIT1, dir);
- bitSet(stepper_control,BIT0);
- }
- void MotorOff(void)
- {
- delay(10); //delay 10ms to allow motor to brake
- PORTB = PORTB & B11110000; //shut off the door motors to reduce power consumption
- }
- void setup()
- {
- // setup display
- // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
- display.begin(SSD1306_SWITCHCAPVCC, SSD1306_I2C_ADDRESS); // initialize with the I2C addr 0x3C (for the 128x64)
- display.setTextColor(WHITE);
- display.setTextSize(2);
- display.display();
- delay(DISPLAY_DELAY);
- PrintDisplay("Starting");
- // setup stepper outputs
- MotorOff(); //make sure that the port is cleared before turning on the outputs
- pinMode(ORG, OUTPUT);
- pinMode(YEL, OUTPUT);
- pinMode(PNK, OUTPUT);
- pinMode(BLU, OUTPUT);
- MotorOff(); //just in case...
- clockwise = true; //initial opening direction
- micro_step = 0; //set up the state machine to start at micro_step 0
- bitClear(stepper_control,BIT0);
- bitWrite(stepper_control,BIT1,clockwise);
- motor_speed = SLOW;
- step_counter = NUM_STEPS;
- door_state = CLOSED;
- manual_flag = true; //start in manual mode for a controlled startup and no door opening until everything is ready.
- //Init the push-buttons, LEDs and door switch inputs
- pinMode(CLOSE_BUTTON_PIN, INPUT_PULLUP);
- pinMode(OPEN_BUTTON_PIN, INPUT_PULLUP);
- pinMode(DOOR_MANUAL_PIN, INPUT_PULLUP);
- // initialize digital pin LED_BUILTIN as an output to display motion detection status (pin 13)
- pinMode(LED_BUILTIN, OUTPUT);
- digitalWrite(LED_BUILTIN,LOW);
- // init the IR sensor inputs
- pinMode(PIRpin1, INPUT_PULLUP);
- pinMode(PIRpin2, INPUT_PULLUP);
- ir_new_status = false;
- // enable the ir receiver
- irrecv.enableIRIn(); // Initialization infrared receiver
- //Init the sofware timers
- timers_status = 0;
- timer0 = 0; //door open timer
- // set the sensor scan timer
- timer1 = SENSOR_SCAN_DELAY;
- blank_screen_timer = SCREENSAVE_DELAY;
- bitSet(timers_status, BIT1); //start timer1, sensor timer.
- //Start the comparator (rtc) interrupts
- // Hardware Timer0 is already used for millis() - we'll just interrupt at 0
- // and process the interrupt in the comparator SIGNAL interrupt routine.
- OCR0A = 0x00;
- TIMSK0 |= _BV(OCIE0A); //enable comparator interrupts
- }
- void loop()
- {
- //start with handling the door state machine
- switch (door_state)
- {
- case CLOSED:
- if(ir_new_status)
- {
- if(MotorStopped()) //if motor is stopped
- {
- clockwise = true; //set the direction
- MotorStart(NUM_STEPS, clockwise);
- door_state = OPENING;
- }
- }else if (MotorStopped()) //if motor is stopped but no sensors active
- {
- if(digitalRead(OPEN_BUTTON_PIN) == LOW)
- {
- MotorStart(1, true);
- }else if(digitalRead(CLOSE_BUTTON_PIN) == LOW)
- {
- MotorStart(1, false);
- }else
- {
- MotorOff();
- }
- }
- break;
- case OPENING:
- // wait for the motor to stop
- if(MotorStopped()) //if motor is stopped
- {
- MotorOff();
- // set the timer
- bitClear(timers_status,BIT0);
- delay(2); // need this delay to allow interrupt to occur. not sure why. intermittent when no delay
- timer0 = OPEN_DELAY;
- bitSet(timers_status, BIT0);
- door_state = OPENED;
- }
- break;
- case OPENED:
- // if we get motion, restart the timer
- if(ir_new_status)
- {
- // reset the timer
- bitClear(timers_status,BIT0);
- delay(2); // need this delay to allow interrupt to occur. not sure why. intermittent when no delay
- timer0 = OPEN_DELAY;
- bitSet(timers_status, BIT0);
- }else if (!bitRead(timers_status, BIT0)) // no motion detected, wait for timer to time out
- {
- // timer timed out, reverse direction and start motor
- clockwise = false; //set the direction
- MotorStart(NUM_STEPS, clockwise);
- door_state = CLOSING;
- }
- break;
- case CLOSING:
- if(ir_new_status)
- {
- bitClear(stepper_control, BIT0); //stop the motor
- while (!MotorStopped()) //wait for motor to stop
- {
- delay(1); //waste time.
- }
- {
- clockwise = true; //flip the direction
- MotorStart((NUM_STEPS - step_counter), clockwise);
- door_state = OPENING;
- }
- }else if(MotorStopped()) //if motor is stopped
- {
- MotorOff();
- door_state = CLOSED;
- }
- break;
- }// end switch case for motor control
- //done with the door, do the sensors
- if (!bitRead(timers_status, BIT1))//Timer timed out, do a scan of inputs
- {
- if(manual_flag == false)
- {
- // check IR for motion
- ir_new_status = digitalRead(PIRpin1) || digitalRead(PIRpin2);
- digitalWrite(LED_BUILTIN,ir_new_status); //set to automatic, display the sensor status
- }else
- {
- digitalWrite(LED_BUILTIN,manual_flag); //set to manual, turn on the LED
- }
- // detect change of state in manual/auto button
- if(!digitalRead(DOOR_MANUAL_PIN)) //look for high to low transition
- {
- manual_flag = !manual_flag; //flip operation mode
- delay(200); //debounce
- blank_screen_timer = SCREENSAVE_DELAY; //reset the screen saver timer
- }
- // check for received ir commands
- if (irrecv.decode(&results))
- {
- ir_code = results.value & 0xFF;
- if (ir_code == 0xBB)
- {
- manual_flag = !manual_flag; //flip operation mode
- } else if ((ir_code == 0x1B) && manual_flag) //manual open by faking movement detection
- {
- ir_new_status = true;
- }else if ((ir_code == 0x1F) && manual_flag && (door_state == OPENED)) //manual close by faking moment detection
- {
- ir_new_status = false; // no movement
- bitClear(timers_status,BIT0); // force open delay timer timeout
- }else if ((ir_code == 0xDB) && manual_flag && (door_state == CLOSED))
- {
- MotorStart(10, true); //open the door by 10 step
- }else if ((ir_code == 0x3B) && manual_flag && (door_state == CLOSED))
- {
- MotorStart(10, false); //close the door by 10 step
- }
- irrecv.resume(); //Receiving the next value
- blank_screen_timer = SCREENSAVE_DELAY; // reset the screen saver timer
- }
- if(blank_screen_timer != 0) // screen saver timer not timed out, so display the status
- {
- DisplayStatus();
- --blank_screen_timer; //decrement timer
- }else
- {
- display.clearDisplay(); // call the screen saver.
- display.display();
- }
- // reset the sensor scan timer
- timer1 = SENSOR_SCAN_DELAY; //reset the sensor scan timer
- bitSet(timers_status, BIT1); //restart it
- }// end sensor processing
- }//end main loop
- // Interrupt is called once a millisecond,
- SIGNAL(TIMER0_COMPA_vect) {
- // stepper motor controler
- if (motor_speed == 0) //if the speed counter has reached 0, go process the motor
- {
- if(!MotorStopped())
- {
- if((step_counter != 0) || (micro_step != 0)) {
- MicroStep();
- }else
- {
- bitClear(stepper_control,BIT0); //we're done with the rotation, signal main loop by clearing the control reg.
- }
- }
- //done with the motor, now find out what to reload the motor speed counter with
- if(step_counter > (NUM_STEPS - 50) || (step_counter <= 50))
- {
- motor_speed = SLOW;
- }else if (step_counter > (NUM_STEPS - 100) || (step_counter <= 100))
- {
- motor_speed = MEDIUM;
- }else if (step_counter > (NUM_STEPS - 150) || (step_counter <= 150))
- {
- motor_speed = MEDIUMFAST;
- }else
- {
- motor_speed = FAST;
- }
- }else //,just decrement the motor speed counter
- {
- --motor_speed;
- }
- // Timers
- if(bitRead(timers_status, BIT0))
- {
- --timer0;
- if(timer0 == 0){
- bitClear(timers_status, BIT0); //bit clear the timer status bit.
- }
- }
- if(bitRead(timers_status, BIT1))
- {
- --timer1;
- if(timer1 == 0){
- bitClear(timers_status, BIT1); //bit clear the timer status bit.
- }
- }
- }
- void MicroStep()//Stepper motor rotation 1 step
- {
- // Serial.println(step_counter);
- if (bitRead(stepper_control,BIT1))
- { // Stepper motor clockwise
- switch (micro_step)
- {
- case 0:
- // org yel pink blue
- PORTB = (PORTB & B11110000) | B00000001; //read upper nibble to restore state in case they are being used
- break;
- case 1:
- PORTB = (PORTB & B11110000) | B00000011;
- break;
- case 2:
- PORTB = (PORTB & B11110000) | B00000010;
- break;
- case 3:
- PORTB = (PORTB & B11110000) | B00000110;
- break;
- case 4:
- PORTB = (PORTB & B11110000) | B00000100;
- break;
- case 5:
- PORTB = (PORTB & B11110000) | B00001100;
- break;
- case 6:
- PORTB = (PORTB & B11110000) | B00001000;
- break;
- case 7:
- PORTB = (PORTB & B11110000) | B00001001;
- step_counter = --step_counter;
- break;
- }
- micro_step = ++micro_step & 0x0007;
- } else
- { // Stepper counter-clockwise
- // org yel pnk blu
- switch (micro_step)
- {
- case 0:
- PORTB = (PORTB & B11110000) | B00001001;
- break;
- case 1:
- PORTB = (PORTB & B11110000) | B00001000;
- break;
- case 2:
- PORTB = (PORTB & B11110000) | B00001100;
- break;
- case 3:
- PORTB = (PORTB & B11110000) | B00000100;
- break;
- case 4:
- PORTB = (PORTB & B11110000) | B00000110;
- break;
- case 5:
- PORTB = (PORTB & B11110000) | B00000010;
- break;
- case 6:
- PORTB = (PORTB & B11110000) | B00000011;
- break;
- case 7:
- PORTB = (PORTB & B11110000) | B00000001;
- step_counter = --step_counter;
- break;
- }
- micro_step = ++micro_step & 0x0007;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement