Coronavirus spreads through surfaces that are frequently touched like door handles - we need a solution especially for hospitals and public buildings, an automatic door opener can help to decrease the risk to be infected and is also useful for people with special needs and disabilities.
There will be two versions of the door handle, an automatic and manual one
Video with the project explanation
At first we thought it would be easier:First prototypes:
First Test with the kinematic usinga 12v motor just to improve the movement.
The stepper 28BYJ-48 seems to be too weak for this application, I am considering to use two steppers or maybe one with more torque. First I will improve the mechanism to lower the required torque and if this still not works I will change the concept to a double stepper solution driving the handle trough the opposite side of the door too.
After some tests I have changed the ratio of the bevel gears to 3:1 and used spiral teeth but the mechanism requires too much torque for this little motor. So I have included a new stepper on the opposite side and this option works quite good!
An important target is achieved because using the two 28BYJ-48 steppers, this automatic door handle can be very affordable.
The movement of the steppers are the same just in opposite direction, it means that I can use the same pins on Arduino for the both motors. This is very important because we can save 4 pins on the board for further applications.
FIRST PROTOTYPE:
The first prototype uses PIR sensors to detect the movement and trigger the handle.
I‘ve added brackets to hold the arduino board and the motor drivers. The handle is integrated on the pinion, reducing the number of parts.
SECOND PROTOTYPE:
I am replacing the PIR sensors with ultrasonic ones because then is possible to detect not only the objects but their related distance too. If we have the distance we can avoid false openings just setting the right distance to start the movement of the handles.
The following changes have been done:
1- Substitution of PIR sensors for ultrasonic sensors:
to trigger the movement with a signal in function of the object distance
2-Arduino board transferred to the Cyber Door Mover:
To save space and complexity on the Handles
3- Stepper driver transferred to the middle of the door plate.
This reduces the length of the cable wires and allows the door plate to be
symmetric.
4- Door plate is now the same for the pull and the push door side:
We can use the same design of the plate for both sides, this reduce
the complexity of the whole system.
5. Cable management:
The cables (or at least most of them) are moved to the back side of the door
plate.
THIRD PROTOTYPE:
new concept simplified and with a tiny handle for manual opening
All the cables sensors, steppers and drivers are inside of the black cover. There’s a tiny release to open the door manually in case of failure.
This system works properly but this is only a mechanism which activates the handle, but how to open the door automatically?
This could be solved with the Cyber Door Mover concept ;-)
The Cyber door mover open and close the door automatically and supplies electricity for the whole system. It has a wireless charger that can be placed next to the wall.
I have planned to use the well known 18650 batteries that can be recycled from old notebooks.
The first prototype will run with a 9V power supply. With the screw on the left it is possible to adjust the grip of the wheel to prevent it from slipping.
My first test shows that the motor is too weak and too fast for this application, now I know that the motor is doing around 5 revolutions to open the door completely, as the nominal speed of this affordable motor is dated to 240rpm, and I want to open the door in around 3 seconds, I can try to add some gears with a ratio of around 2.5:1 to increase the torque and reduce the speed to approximately 100rpm, hoping to have enough torque.
The fixing concept on the door works really good because I can adjust the hight of the mover with the included guides and fixed the position with just two screws. This will be helpful to adjust the grip of the wheel, considering the different types of floors including slippery tiles.
That’s all about my first prototype!
SECOND PROTOTYPE:
In order to add a gear reduction some other parts are needed like the bracket for the motor and a new wheel axis. The motor pivots around its fixing point and with one screw it is possible to adjust the grip of the wheel.
Now it's time to print!
I have set the gears to around 1:4 ratio and now the system is working.
I have still some problems because the wheel slips slightly but I’m very confident using the right rubber tyre (the one that I’m using is made of hard plastic) the mover will work properly.
THIRD PROTOTYPE:
Our intention was to use a little 5V motor but this was not powerful enough to move the door on different surfaces and in a reliable way. We have changed the motor to a 12V one with integrated gears. The drive unit has a suspension to adapt to the different conditions of the floor and to keep the grip as constant as possible.
Changes done:
-Integration of Arduino Nano
-New 12V Motor added (can work with 9V)
-3D Printed wheel and tyre in TPU
-New suspension for the drive unit.
Views of the current standFinally using an Arduino MKR instead of Arduino Nano we can introduce smart functions like remote control for hospitals or nursing homes.
Or just to name an example: by using the adequate sensor we can detect body temperatures locking the door for people with fever.
Note:
It was not possible to use the sponsored MKR due to malfunctions with IOS, or maybe the received MKR was damaged.
Things to do...after 3 prototypes of each component we have a functional automatic door handle but there’s still a “few” things to do:
-Integration of an end switch to stop the door in closed position
-Integration of batteries and wireless charger
-new silent Motor
-Code updates
-IoT
-Alternative system to the Carrier
Code explanationTo manage the interactions of the touchless door, we used an state dependant program. That means that the programs acts different depending on the current state of the door. It features five states:
- Closed(default)
- Opening from the pull side
- Opening from the push side
- Fully opened
- Closing
Then, to manage the inputs and outputs of the program we use the following functions:
For the ultrasonic sensors, we read their value at the start of the loop() function
distance1 = getDistance(TRIG1, ECO1);
distance2 = getDistance(TRIG2, ECO2);
The getDistance function is our standard ultrasonic sensor handler.
int getDistance(int TRIG, int ECO){
digitalWrite(TRIG, LOW);
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);
int duration = pulseIn(ECO, HIGH);
return duration / 58.2;
}
Then we handle the DC motor(the one that moves the door) with the doorMotor() function. This is just a method to use the motor in both directions and stop it, using the l298n driver.
#define MOTOR_CLOSING -1
#define MOTOR_STOPPED 0
#define MOTOR_OPENING 1
int IN1 = 8;
int IN2 = 9;
int ENA = 10;
void doorMotor(int rotDirection){
if(rotDirection == MOTOR_CLOSING){
Serial.println ("Door motor closing...");
digitalWrite(ENA, HIGH);
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
}else{
if(rotDirection == MOTOR_STOPPED){
Serial.println ("Door motor stopping...");
digitalWrite(ENA, LOW);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
}else{
if(rotDirection == MOTOR_OPENING){
Serial.println ("Door motor opening...");
digitalWrite(ENA, HIGH);
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
}
}
}
}
And last but not least we move the handle with an stepper motor. We control it with a lookup table. It controls the coils of the stepper which are activated in a certain order.
const int STEP1 = 4;
const int STEP2 = 5;
const int STEP3 = 6;
const int STEP4 = 7;
const int numSteps = 4;
const int stepsLookup[4] = {B1000, B0100, B0010, B0001};
As you can see by default we use the stepper with one-phase behaviour as we got the best results this way but it can be changed.
Then to advance the motor we use the opening() and closing() functions which are very similar.
void opening(){
stepCounter++;
if(stepCounter >= numSteps) stepCounter = 0;
setOutput(stepCounter);
}
void closing(){
stepCounter--;
if(stepCounter < 0) stepCounter = numSteps - 1;
setOutput(stepCounter);
}
We also included a demagnetize() function to turn off all coils.
void demagnetize(){
digitalWrite(STEP1, LOW);
digitalWrite(STEP2, LOW);
digitalWrite(STEP3, LOW);
digitalWrite(STEP4, LOW);
}
And finally the setOutput function translates these look-up table values to the coils that should be activated in the stepper.
void setOutput(int step){
digitalWrite(STEP1, bitRead(stepsLookup[step], 0));
digitalWrite(STEP2, bitRead(stepsLookup[step], 1));
digitalWrite(STEP3, bitRead(stepsLookup[step], 2));
digitalWrite(STEP4, bitRead(stepsLookup[step], 3));
}
Now that we know how to read the sensors and activate the motors we can start explaining the behaviour of the program.
To behave differently in each state, we use a switch clause in out loop() function, governed by an integer that represents the state of the program.
#define DOOR_CLOSED 0
#define DOOR_OPENING 1
#define DOOR_OPENING_OUTSIDE 4
#define DOOR_OPENED 2
#define DOOR_CLOSING 3
int doorState = DOOR_CLOSED;
void loop(){
switch(doorState){
case DOOR_CLOSED:
...
break;
...
case DOOR_OPENED:
...
break;
}
}
Now we will examine the behaviour of the program in each state.
ClosedWhen the door is closed we want the program to wait until the user places his hand on the ultrasonic sensor, so we compare the value read by the sensor with a constant that defines how close should the user place his hand
const int handDistance = 5;
We make sure that the distance is positive as the sensor can sometimes give erratic values. We will expand on this problem later. This is the code for detecting the user's hand in the sensor that is on the pull side.
case DOOR_CLOSED:
if(distance1 <= handDistance && distance1 > 0){
When we detect that the user has placed his hand on the sensor, we start turning the stepper motor, controlling the door's handle.
for(int i = 0; i < steps; i++){
opening();
delayMicroseconds(motorSpeed);
}
We can tweak the angle of rotation by modifying the variable steps.
When the handle is opened, we stop sending current to the stepper coils so the rest of the components can draw more power(this is specially important for the motor that opens the door)
demagnetize();
Then we can start the DC motor that opens the door and change the program's state to opening
doorState = DOOR_OPENING;
doorMotor(MOTOR_OPENING);
handInSensor = true;
We also set the variable handInSensor to True, for the reasons we will see in the next chapter.
OpeningWhen the door is opening, we don't have to do anything until it fully opens, when we should stop the DC motor. So we compare the value of the inside sensor with a constant that determines how close to the wall do we want the door to stop.
const int wallDistance = 20;
case DOOR_OPENING:
if(distance1 <= wallDistance && distance1 > 0 && !handInSensor){
As you can see we use the variable handInSensor in here. The reasoning is simple, if we didn't use it as soon as the door started opening it would detect the hand of the user still on the sensor as if it were the wall, and the motor would stop. So we first have to detect when the user takes away his hand to avoid this problem.
if(distance1 > wallDistance){
handInSensor = false;
}
So when the hand is taken away and the sensor detects proximity to an object, we stop the DC motor and set the state to fully opened.
doorMotor(MOTOR_STOPPED);
doorState = DOOR_OPENED;
Opening from the outsideYou may have noticed that we have only explained how to open the door from the pull side. To open it from the push side we do basically the same, with the exception that we don't have to use that handInSensor variable, as the sensor that detected the hand and the sensor that will detect the wall are different.
case DOOR_CLOSED:
if(distance2 <= handDistance && distance2 > 0){
for(int i = 0; i < steps; i++){
opening();
delayMicroseconds(motorSpeed);
}
demagnetize();
doorState = DOOR_OPENING_OUTSIDE;
doorMotor(MOTOR_OPENING);
}
break;
Then, when it is opening;
case DOOR_OPENING_OUTSIDE:
if(distance1 <= wallDistance && distance1 > 0){
Serial.println("Wall");
doorMotor(MOTOR_STOPPED);
doorState = DOOR_OPENED;
}
break;
OpenedWhen the door has opened we need to wait some time to let the user pass through the door and then start to close it, so we use a delay with another constant, that represents the waiting time in milliseconds.
const int doorWaitTime = 2000;
case DOOR_OPENED:
delay(doorWaitTime);
doorMotor(MOTOR_CLOSING);
doorState = DOOR_CLOSING;
break;
ClosingSo here comes the difficult part. In order to not introduce more components to the project, we decided to detect when the door is closed by reading the push side sensor value and detecting when it stays constant(indicating the door has stopped moving)
To do this, we tried to take samples of the readings of the sensor and calculating its variance. We do this at the start of the loop function, having declared the size of the sample and the variables we will use.
const int sampleSize = 3;
const float varThreshold = 1.5;
int sampleIndex = 0;
int sample[sampleSize];
float sampleVariance = 0;
So in each iteration of the loop() function we save the sensor's detected value in our sample.
sample[sampleIndex] = distance2;
sampleIndex++;
Then, when we have collected a full sample, we detect its variance
if(sampleIndex % sampleSize == 0){
sampleIndex = 0;
int mean = 0;
for(int i = 0; i < sampleSize; i++){
mean += sample[i];
}
mean /= sampleSize;
sampleVariance = 0;
for(int i = 0; i < sampleSize; i++){
sampleVariance += pow(sample[i] - mean, 2);
}
sampleVariance /= sampleSize;
}
So finally we determine the door has stopped if the variance of the last sample is less than the constant varThreshold we defined earlier. In out switch statement,
case DOOR_CLOSING:
if(sampleVariance < varThreshold){
doorState = DOOR_CLOSED;
doorMotor(MOTOR_STOPPED);
for(int i = 0; i < steps; i++){
closing();
delayMicroseconds(motorSpeed);
}
demagnetize();
}
break;
As you can see we first make sure the door is closed and then we return the handle to the original position. This is because our DC motor doesn't have enought torque to close the door with the handle closed. If you are using a more powerful motor, you can close the handle when the door is on the opened position.
If the sensor gave accurate values we would have finished, but this is not the case. When the sensor is a long distance from an object it gives erratic values. We noticed they tend to be the correct value but sometimes drop a large amount. This in turn creates a large variance even when the door is stationary, making this code useless.
To solve this problem, we take a sub-sample of the sensor values and take the biggest value of the sample as the real sensor reading. We do this in a similar way to the previous sampling. First we define our variables:
const int subSampleSize = 5;
int subSampleIndex = 0;
float subSampleMax = 0;
And then at the start of the loop() function we compute this sub-sample maximum,
if(distance2 > subSampleMax){
subSampleMax = distance2;
}
subSampleIndex++;
When the sub-sample is finished, we feed its value to the original sample, as it if were the sensor reading, and reset the variables to take a new sub-sample.
if(subSampleIndex % subSampleSize == 0){
sample[sampleIndex] = subSampleMax;
sampleIndex++;
subSampleMax = 0;
subSampleIndex = 0;
}
Then we change a bit the big sample code so it only computes the variance one time.
if(sampleIndex % sampleSize == 0 && subSampleIndex % subSampleSize == 0){
...
}
Here we can see the effect of the sub-sample, how it filters out these spikes and the general noise. Blue is the raw sensor data, red is the subSampleMax value.
And below the green value is the variance. You can see in the graph that the value is pretty high when the door is closing as the sensor values move a lot but when it is closed it drops to almost zero as the sensor values are mostly constant.
By adjusting the sample and sub-sample sizes we tried to find a compromise between having small samples which means more noise and in turn less precision(stopping before closing completely for example) and big samples which means slower detection, so the DC motor keeps running a bit with the door is already closed.
Comments