The Auto-Empty-Tank

In some areas of this world it is necessary to use more or less constantly dehumidifiers to get rid of humidity at home. Otherwise one might have to deal with mold sooner or later. But what to do during holidays or periods when nobody is there to take care of emptying the watertank? Then the next project is born: the auto empty tank. :-)

12V pump
I started with a small 12V pump and some level-sensors. The pump I had already, level-sensors I've got cheap for $6 per piece. The plan was to have the two sensors in the water tank of the dehumidifier to tell the pump when to switch on and when to switch off again. Probably something which could be done with one relay and one of the level sensors built-in upside down.

cheap level sensor

Considering not being home for a week or two... what would happen if one of the level-sensors got stuck? Or the pump would get faulty not pumping the water at all? Of course the dehumidifier itself will switch off if the tank is too full. But if e.g. lower level sensor is faulty and the pump doesn't switch off anymore? Not so good...

So controlling is better done by a microcontroller. I still have a few ATTINY4313, probably almost a bit oversized for this task, anyway.

I've built a plastic frame for the level sensors out of old lego and mounted it with double sided tape into the tank. Luckily, there was a corner in the tank where the pump fitted into.

level sensors and pump inside tank
The circuit is super simple. Just the two level sensors, a relay for the pump and a status led needed to be connected with the microcontroller, as I'm using the internal oscillator. So not even a quartz crystal is necessary (which would come along with the need of two more capacitors).


I'm driving this with a combined power supply delivering 5V for the microcontroller and 12V for the pump. It turned out that it is also necessary for the pump to connect a flyback diode. Otherwise the microcontroller might get restartet the moment when the pump is switched off. (which would kill all security timer effects I've build into the software)
The whole thing I've soldered on a small, actually too small board.

From cabling perspective I need to make sure for more space next time... :-)

During normal operations the LED toggles on/off every second. If there is a technical problem it instead flashes 1-3 times every second:
  • flashing once: Pump started, but the upper level sensor didn't switch off again in time (15secs)
  • flashing twice: Pump runs longer than max timeout (180 sec), so either pump problem or lower level sensor error
  • flashing three times: lower level sensor is off when upper level sensor is on. (one of the switches stuck or shortcut or defective contact
If the device is in error status, only a reset (power on /off, no button yet) will allow for normal operation again.

The source code in c:


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

volatile unsigned int ms; // milliseconds to seconds counter
volatile unsigned int sectimer; //sectimer
volatile uint8_t status; //status
uint8_t loop;
const uint8_t s_ok = 0;
const uint8_t s_pump_level = 1;
const uint8_t s_pump_too_long = 2;
const uint8_t s_level_switch = 3;
const unsigned int max_pump_time = 180; //max. runtime (secs) of the pump before failure status
const unsigned int max_pump_time_level_full = 15; //max. runtime (secs)of the pump without switch off of level_full sensor

#define level1_on (PIND & (1<<PIND2)) //input pin on for level1 switch
#define level2_on (PIND & (1<<PIND3)) //input pin on for level2 switch
#define level1_off !(PIND & (1<<PIND2)) //input pin off for level1 switch
#define level2_off !(PIND & (1<<PIND3)) //input pin off for level2 switch
#define status_ok (status == s_ok) //error status
#define set_pump_on  PORTD |= (1<<PIND5) //set pin for pump on
#define set_pump_off PORTD &=~(1<<PIND5) //clear pin for pump off
#define statusled_on  PORTD |= (1<<PIND6) //set pin for status led
#define statusled_off PORTD &=~(1<<PIND6) //clear pin for status led

int main(void)
{
 DDRD = 0b01100000; // pin directions pind5 and pind6 as output

  // Timer 0 in CTC MODE with 125 steps based on prescaling 8
  TCCR0A = (1<<WGM01);
  OCR0A = 125-1; //considering 1MHz divided by 125 and considering zero when counting
  TCCR0B |= (1<<CS01);
  // enable interrupt
  TIMSK |= (1<<OCIE0A);
  sei();

  PORTD = 0b00000000;
  sectimer = 0;
  status = s_ok;

 while(1) {
            if (level1_on && level2_on && (sectimer == 0) && status_ok)
            {
                set_pump_on;
                sectimer = 1;
            }
            if (level1_on && level2_on && (sectimer > max_pump_time_level_full) && status_ok)
            {
                set_pump_off;
                status=s_pump_level;
            }
            if (level1_on && level2_off && (sectimer > max_pump_time) && status_ok)
            {
                set_pump_off;
                status=s_pump_too_long;
            }
            if (level1_off && level2_on && status_ok)
            {
                set_pump_off;
                status=s_level_switch;
            }
            if (level1_off)
            {
                set_pump_off;
                sectimer = 0;
            }
          }
 return 0;
}
ISR (TIMER0_COMPA_vect)
{
  ms++;
  if(ms == 1000)
  {
    if status_ok PORTD = PORTD ^ 0b01000000;
    else
    {
        for(loop = 0; loop < status; loop++)
        {
            statusled_on;
            _delay_ms(100);
            statusled_off;
            _delay_ms(100);
        }
    }
    ms = 0;
    if (sectimer !=0) sectimer++;
  }
}

It's a bit longer as I've used an interrupt for the one second blinking status LED. Somehow all delay functions didn't really work out properly for a second. I've used some defines to make the logic part of the source code easier readable. sectimer is incremented every second for the timeouts and only starts to count if set to something else than zero. 

I've set the fuses for the 4313 like this using avrdude with USB programmer:

avrdude -v -p attiny4313 -c avrispmkII -P usb -Ulfuse:w:0x64:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m

Here you will see a first test run in the sink:

Any comments or questions? Feel free to comment below. :-)


Comments