Introduction: Dehumidifer - Smart(er)
Background
This project came to life after I got a Dehumidifier. Despite being the quietest one I could find on Amazon, it was still too loud, especially during the night. I needed a solution that would turn it off at night and turn it back on in the morning without me having to manually do so. Using a smart plug wasn't an option because the Dehumidifier's default power state is off, so it wouldn't turn on with the plug. So, what I needed was to simulate key presses to turn it on and off. Naturally for me, the solution I started to explore involved a D1 Mini.
How this Dehumidifer Operates
The noisy part is the fan of course. The device also has an LED, and a single button that rotate between the following states:
- Fan and LED are off
- Fan is on and the LED changes color periodically
- Fan is on and the LED color stays constant
- Fan is on with the LED turned off
In addition, when the water tank is full, the device goes into an idle state and the LED flashes red.
The Spec
What I needed to do was to simulate a press of the button to turn the device off at night, and then on again in the morning. As explained above, turning it on / off will require more than one button press. In addition, I will need to be able to detect when the water tank is full. Finally, I wanted to also find a source for 5v so that the D1 Mini will not not a separate power source.
The Plan
My plan was to use a INA219 sensor to measure the current running the entire device to figure out if the fan is on or off. The reason I wanted to do that was because I didn't want to hard code the number of keypresses based on the state - this would have been error prone. Measuring the current would give me an accurate understanding of whether the device is on or off.
I was also concerned about the Tank Full state. I figured that I should be able to detect that actual signal the indicates that, but I assumed that if not, then I can probably just infer that if I key press enough times and the device doesn't turn on.
Finally, I needed to find a way to simulate a key press. Initially I assumed I will do this with a relay, but I was concerned about space inside the device.
Supplies
- Dehumidifer
- D1 Mini
- INA219
- BC327 Amplifier Transistors (PNP)
Step 1: Explore
The first phase was to explore. I needed to find a source for 5V, figure out where to measure the current, and locate the switch circuit. Additionally, I wanted to detect the signal for when the water tank is full.
Even though the device uses a 12V power supply, I was fortunate to quickly locate a 5V contact, making this part of the project simple.
Finding the switch was also easy, and I ended up soldering two onto those contacts and running them down. The brown wires run from the switch contacts.
Initially, I thought about measuring the current drawn from the fan, but then I decided it would be more interesting and easier for me to hook it up to the main power supply. So, I split the + cable and hooked it up to the INA219.
Lastly, I was able to locate a contact on the main board that reads 5V when the water tank is full and LOW otherwise. I soldered this contact using the blue wire.
Step 2: Packaging
As I began to solder and connect all of the components, I quickly realized there wasn't enough space for a relay module. As a result, I opted for a simple transistor.
To attach the INA219 to the prototype board, I used an angled connector and bent it slightly to fit the overall setup's curved form factor (refer to the photos).
To make it easy to flash the software, I soldered the D1 Mini onto headers. The remaining connections are on the inner side of the prototype board.
The last photo shows that there is just enough space for this setup when the main unit of the fan is reassembled.
Step 3: Soldering
The INA219 is connected to the D1 Mini using the following:
SDL -> D1
SDA -> D2
GND -> G
VCC -> 5V
The Vin- and Vin+ on the INA219 were connected to the main + power of the device, which I had to split, so that it will run through the INA219 so that it will be able to measure the current.
The BC327 Transistor connects the Base to D3 in the D1 Mini. The Collector and the Emitter connect to the two opposing sides of the button on the device. This enables simulating a button press.
Finally, D5 is connected to the full tank indicator I found. As a reminder, this goes HIGH when the water tank is full, and LOW otherwise.
Step 4: Software
Let's explore how the program works, from a very high-level, this is the basic idea:
float current_mA = ina219.getCurrent_mA();
int magnet_state = digitalRead(D5);
bool fan_on = (current_mA > 1000);
bool water_full = (magnet_state == 1);
int start_time = schedule[today].start_hour * 60 + schedule[today].start_minute;
int end_time = schedule[today].end_hour * 60 + schedule[today].end_minute;
int now_time = hours * 60 + minutes;
bool should_on = (now_time >= start_time && now_time < end_time);
if (!water_full) {
if (should_on) {
if (!fan_on)
turnOn();
} else {
if (fan_on)
turnOff();
}
}
This is running the program's loop() every minute on the minute:
- Measuring the current and inferring the fan_on state - Measuring the current using a simple multimeter, I determined that when the device is idle it draws about 200mA and when the fan is on it gets to 2000mA. So seeing it above 1000mA means the fan is on, otherwise off.
- D5 is where I connected the wire that I was able to see goes HIGH when the water tank is full. I called it Magnet because it is using a magnet. Reading the digital signal will update the water_full boolean.
- The program maintains an array of struct schedule with a start_time and end_time members. I use this in combination with the current day, hour and minute to determine if the fan should be on or off, and update the should_on boolean.
- Finally, the if statement is pretty self-explanatory: Unless the water tank is full, turn on the fan if it should be on and if it's currently off, otherwise, turn it off if it's on.
Next, let's go over how the turnOn() function works:
void turnOn() {
int i = 0;
float current_mA = ina219.getCurrent_mA();
while ((current_mA < 500) && (i < 5)) {
click();
current_mA = ina219.getCurrent_mA();
i++;
}
if (current_mA > 1000) { // To turn off the light
click();
click();
}
}
I wanted to avoid the assumption that I know what the actual state of the device is. This could be for multiple reasons, but in general assuming I know the state is very error prone. So the idea is to click the button until the current raises above 500mA. To avoid going into an infinite loop, I limited this to 5 attempts.
If you recall, the first On state also includes the LED. For me, I prefer to turn the LED off, so I needed additional to clicks.
Off is pretty much the opposite, while here I do not need the extra clicks. I just click until the power goes down. Again, limiting this to 5 tries to avoid an infinite loop.
void turnOff() {
int i = 0;
float current_mA = ina219.getCurrent_mA();
while ((current_mA > 1000) && (i < 5)) {
click();
current_mA = ina219.getCurrent_mA();
i++;
}
}
Now let's explain what a click() looks like:
void click() {
digitalWrite(D3, HIGH);
delay(500);
digitalWrite(D3, LOW);
delay(500);
}
Since I'm using a transistor, a click is basically turning the collector HIGH for the duration of the click, and then setting it LOW again.