This project drips the faucet on cold nights to prevent my pipes from freezing!

The “de-icer” senses temperature and either sets my faucet to a drip or stops the drip.

The project in action (manually triggered the servo using a switch for demo).

A continuous rotation servo drives the wheel. LED lights indicate sub-zero/above-zero temperatures.

In real-life use, the temperature sensor would be placed outside.

Process

One major decision point was deciding the mechanism to lift and lower the faucet. I went through many mechanism ideas during the iteration process. Some mechanisms that I considered were a rack and pinion mechanism (shown below) and a gear assembly. The rack and pinion was difficult because it is difficult to constrain the motion in a reliable way, especially given the limited space I had to work with. Similarly, it was difficult to implement gears because I would need at least 4 gears to get a meaningful mechanical advantage. Also, for both of these ideas, meshing was a problem, because besides the inaccuracy in the laser cutting, I had a difficult time installing the laser cut pieces to the motor attachment because there are no features that make it easily mountable.

Another major decision point was to use the continuous servo motor. Other ideas that I toyed with were stepper motors and a DC motor paired with an encoder (testing process can be seen below). There was no simple way to attach the encoder to the DC motor while also turning the faucet, as I had trouble performing even simple tests. The reason I chose the continuous servo motor in the end was because it had a high torque output while being relatively easy to control. However, this did come with the caveat that the motor was not meant to stop at a particular angle. I was able to compensate for this by starting and stopping the motor at the appropriate time (after much trial and error).

Trying to test (unsuccessfully) the DC motor, paired to a rotary encoder.

Calibrating the continuous rotation servo motor with a potentiometer.

Testing the TMP36 sensor and reading the ambient temperature.

Putting together the laser cut pieces.

Discussion

I am relatively satisfied with my project. For this project, I wanted to focus on making something that was actually practical and something that I could actually use. For this reason, I decided to go with a simple design with few bells and whistles. During the ideation process, I thought of different features I could add to this project, such as an LCD display showing temperature readings. However, I thought this would not be very practical, as I do not really care about the exact temperature – I just don’t want my pipes to freeze during the night.

Once I started making this project, I quickly realized that it would be very difficult to make a device that can work on any faucet. Originally, I had an idea of clamping the device to the faucet so that it would be a more generalizable solution, but I realized that this would not be ideal for my faucet, because there is no good place to clamp the device. To make this device more useful for me, I decided to make it something that would work for my faucet, even if it isn’t something that would necessarily work with other faucets.

I learned that laser cutting is a useful tool, and that I can make clean looking enclosures. For this project, I chose 3mm acrylic to make the enclosure and wheel. Another thing I learned was how difficult it is to make a robust mechanism. Although I went through a number of design iterations, I ended on a simple solution, which turned out to be an effective solution to my problem. If I were to give advice to my past self, it would be to start the design iteration process as soon as possible. By doing this, I would be able to find the most effective mechanism for this project early on and focus on coming up with a device that works on more than just my toilet faucet.

I do not expect to build another iteration of this project. Although I am pretty satisfied with the end result, if I were to build another iteration, I would make a device that is more compact/subtle. This could probably be achieved using a smaller motor and appropriate gear reductions. Another issue that I would like to address (that was also brought up during the design critique) was to make this project a more general solution to this problem. At the moment, my device only works at the faucet in my bathroom sink, so it would be great if I could think of a solution to this problem that is more generalizable.

Schematic

Code


#include <Servo.h>

Servo my_motor;
const int TEMP_PIN = A0; // read temperature from this pin
const int SERVO_PIN = 3; // actuate servo from this pin
const int ON_PIN = 8; // led indicates whether activated or not
const int OFF_PIN = 9;
const int BUTTON_PIN = 5; // manually activate

float threshold_temp = 0.0; // will activate when temperature reading falls below this
int sample_time = 100; // how often to measure temperature
bool on_state = 0; // whether activated or not
bool manual_mode = 0;
int servo_speed = 28; // how fast servo rotates
int servo_delay = 200; // how long servo rotates
int off_speed = 28; //turns servo off
int prev_button_read = 0;

void setup() {

    pinMode(TEMP_PIN, INPUT);
    pinMode(ON_PIN, OUTPUT);
    pinMode(OFF_PIN, OUTPUT);
    pinMode(BUTTON_PIN, INPUT);
    my_motor.attach(SERVO_PIN);
    my_motor.write(off_speed);

    Serial.begin(9600);
}

void turn_motor(){
    my_motor.write(0);
    delay(servo_delay);
    my_motor.write(off_speed);
    on_state = 1;
}

void reverse_turn_motor(){
    my_motor.write(255); // reverse rotation
    delay(servo_delay);
    my_motor.write(off_speed);
    on_state = 0;
}

void loop() {
    int reading = analogRead(TEMP_PIN);
    int button_read = digitalRead(BUTTON_PIN);

    delay(sample_time); //debounce and regulate number of temperature measurements
    float voltage = reading * 5.0;
    voltage /= 1024.0;

    float current_temp = (voltage - 0.5) * 100;

    Serial.println(current_temp);

    if ((current_temp < threshold_temp) and (! on_state)){
        turn_motor();
    }
    else if ((button_read) and !(on_state) and (prev_button_read != button_read)){
        turn_motor();
        manual_mode = true;
    }
    else if ((current_temp > threshold_temp) and (on_state) and (! manual_mode)){
        reverse_turn_motor();
    }
    else if ((button_read) and (on_state) and (prev_button_read != button_read)){
        reverse_turn_motor();
        manual_mode = false;
    }

    if (on_state){
        digitalWrite(ON_PIN, HIGH);
        digitalWrite(OFF_PIN, LOW);
    }
    else{
        digitalWrite(ON_PIN, LOW);
        digitalWrite(OFF_PIN, HIGH);
    }

    prev_button_read = button_read;
}