# Humidity control for the bathroom

The situation: there are two bathrooms, which have fans controlled manually.

The goal:

• To switch on a bathroom fan when the certain humidity level is reached;
• To switch on both fans is somebody is using the toilet;

Let’s go.

Part #1: The preparations.

Our goals are not that hard, and all should be solvable step by step. So how our device should look like:

• The display for monitoring the humidity and the temperature;
• Control elements to enter threshold values for the humidity;
• The humidity sensor;
• The ultrasonic distance sensor;
• Two relays for fan control;
• The light sensor;
• The voltage regulator for the device supply.

Perhaps it is all clear except the one thing – the ultrasonic distance sensor. I was thinking about different approaches how to detect a human in the bathroom:

1. A microbutton on the door – the straightforward solution, not reliable at all – the doors opened and closed, sometimes not closed at all – we have a cat going in and out 🙂
2. The photobased sensor, a receiver and a transmitter on opposite sides. Already something, but requires a wiring from both door sides
3. The motion detector – well, there aren’t a lot of movements in the toilet
4. The ultrasonic sensor – just a single wire, reliable triggering, good accuracy – exactly what I need.

I like to debug my projects right on the pcb, this time I made a pdf file for it.

Everything is quite usual for me already, maybe except the brightness control, which is pure analog realization, as I described earlier. Just in case, reminding one more time – this was a really bad idea to put the photosensor on the right side of a such display – it has highlight LED right there, you will struggle a lot like me with tuning and hiding this sensor to get appropriate results.

The pcb with a humidity sensor and the relay for controlling the fan:

This pcb is hidden inside of the small box:

The pcb with relays for a second bathroom:

I did not put the ultrasonic sensor to a separate enclosure, left it as is.

Now we have all pcbs prepared, time to move to the code. We might create a breakout the required components of the code:

• The LCD display functions – already explored;
• Receiving the temperature and humidity values with checking of the control sum;
• Receiving the distance to the closest object;
• Refreshing the display with current statuses;
• Form to enter the threshold value for the humidity;
• Saving the data to EEPROM;

Receiving the temperature and humidity values with checking of the control sum

The common functions for the humidity sensor DHT11 already have been created. But the control sum check is not realized yet, the algorithm is dead simple:

1. Asking sensor to provide the humidity and the temperature values;
2. If the sum of 1st and third bytes are equal to the value in 5th byte then OK, if not wait 1s;
3. Repeating #2 n times if needed.

In the end I have got something like this:

void GetRHandTempRoutine()
{

RHStatus = 0;                          //Статусная переменная, устанавливается в 1 при успешной отработке функции GetRHandTemp()
lcd_clear();
lcd_goto(0x00);
lcd_putch('*');
GetRHandTemp();                        //Попытка 1

if (!RHStatus)                         //Попытка 2
{
lcd_clear();
lcd_goto(0x01);
lcd_putch('*');
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);

GetRHandTemp();

if (!RHStatus)                       //Попытка 3
{
lcd_clear();
lcd_goto(0x00);
lcd_putch('*');
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);

GetRHandTemp();
if (!RHStatus)                    //Попытка 4
{
lcd_clear();
lcd_goto(0x01);
lcd_putch('*');
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);

GetRHandTemp();

if (!RHStatus)                  //Попытка 5
{
lcd_clear();
lcd_goto(0x00);
lcd_putch('*');
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
__delay_ms(200);
GetRHandTemp();
if (!RHStatus)               //Если ничего не получилось
{
DHTbyte[0] = 0;
lcd_clear();
lcd_goto(0x00);
lcd_puts("Error, call");   //опускаем руки
lcd_goto(0x40);
lcd_puts("+375297251214"); //и звоним мне
}
}
}
}
}
}

The star is to animate actions, to have some idea if the device is really working or just frozen. The testdrive:

Receiving the distance to the closest object

Firstly, refreshing the memory, thinking how to embed everything to our PCB. Well, I technically thought about it already before, and put two pins for this sensor. All I needed to do is to define the button for testing of the range, now to the code:

if (!RA3)
{
__delay_ms(100);
if (!RA3)
{
GetRange();
}
}

It went really smooth, I actually was surprised that nothing happened.

The refreshing of the information

So, what the display should show to us?

1. A humidity;
2. A temperature;
3. The fan status;
4. The human presence status.

Writing the function:

void DisplayAll()
{
lcd_clear();
lcd_goto(0x00);
lcd_puts("RH");
lcd_goto(0x06);
lcd_puts("Temp");
display_digit(DHTbyte[0],0x40);              //Отображаем влажность
display_digit(DHTbyte[1],0x46);              //Отображаем температуру
lcd_goto(0x48);
lcd_putch(0b11011111); //градус
lcd_putch(0b01000011); //С
lcd_goto(0x0D);
lcd_putch('V');
lcd_goto(0x0F);
lcd_putch('T');
lcd_goto(0x4D);
if (!BathCulStatus)
lcd_putch('_');                              //Статус вентилятора в ванной
else
lcd_putch('*');

if (Range<=RangeTrig)
lcd_putch('|');

lcd_goto(0x4F);
if (!ToiCulStatus)
lcd_putch('_');
else
lcd_putch('*');

}

A simple test right away:

Saving the data to EEPROM

Nothing special with this function, first of all I created two variables status of which I’m going to check by each power up:

RHTrig = eeprom_read(0x04);
RangeTrig = eeprom_read(0x05);

void SaveState()
{
eeprom_write(0x04, RHTrig);
eeprom_write(0x05, RangeTrig);
}

Menu for entering the threshold values

Now we need to allow the user to enter the threshold values of the humidity and the range, right away to the code:

void Menu()
{
static bit flag = 0;                    //статусная переменная для выхода из меню
unsigned char RHntStatus = 0;           //флаг текущего пункта меню
RHntStatus = 1;
flag = 0;
lcd_clear();
lcd_goto(0x00);
lcd_putch('*');                         //по умолчанию первый пункт
lcd_puts("RHTrig = ");
display_digit(RHTrig,0x0A);             //Тригерное значение влажности
lcd_goto(0x41);
lcd_puts("Range Trig = ");
display_digit(RangeTrig,0x4E);          //Тригерное значение расстояния
while(!flag)
{
if (!RA0) //Выбор пункта меню
{
__delay_ms(200);
if (!RA0)
{
switch (RHntStatus)
{
case 0 : lcd_goto(0x40);          //Влажность
lcd_putch(' ');
lcd_goto(0x00);
lcd_putch('*');
RHntStatus = 1;
break;
case 1 : lcd_goto(0x00);          //Расстояние
lcd_putch(' ');
lcd_goto(0x40);
lcd_putch('*');
RHntStatus = 2;
break;
case 2 : lcd_goto(0x00); //Выход
lcd_putch('X');
lcd_goto(0x40);
lcd_putch('X');
RHntStatus = 0;
break;
}

}
}//конец выбора пункта меню

if (!RA3) //Выход из меню
{
__delay_ms(200);
if (!RA3)
{
switch (RHntStatus)
{
case 0 : lcd_clear();
lcd_puts("EXIT");
flag = 1;
break;
case 1 : RHTrig++;
if (RHTrig>95)
RHTrig = 35;
display_digit(RHTrig, 0x0A);
break;
case 2 : RangeTrig++;
if (RangeTrig > 90)
RangeTrig = 5;
display_digit(RangeTrig,0x4E);
break;
}

}
}

}//while(!flag)
SaveState();             //На выходе из меню сохраняемя

GetRange();              //Делаем запрос на расстояние

GetRHandTempRoutine();   //Проверяем влажность и температуру

if (DHTbyte[0])          //И отображаем дежурную информацию
DisplayAll();
}

That what we got.

OK, the foundation is created, now time to move to the next part.

Part #2. Combined all together

I tried to put all possible situations to the next table:

– sign in the table means don’t do anything, X – switch the fan off, * – switch the fan ON.

Thus, we need to introduce a refreshing rate, and when time has came, check all the conditions and based on statuses take one or another decision.

We have only TMR2 free from any duties, with an oscillation frequency equal to 12 MHz and maximum divider ratios we can get 21.71 ms max, it is too frequent for me, so we need to to add also a service variable for counting and increasing the refresh interval.

Here we have our event handler:

if (TickCount > 300)              //Для тестов, 300 тиков ~ 6.5 c
{
TickCount = 0;
TMR2ON = 0;
TMR2IE = 0;
GetRange();                      //Запрос расстояния
GetRHandTempRoutine();           //Влажность и температура

if ((Range <= RangeTrig)&&(!ManInToi)) //Есть кто в туалете, начало
{
ManInToi = 1;
ManInToiCount = 1;                //Запускаем счетчик времени присутствия человека
}
else
{
if ((Range <= RangeTrig)&&(ManInToi))  //В туалете кто-то уже давно
{
ManInToiCount++;
if (ManInToiCount>=ManInToiCountTrig) //Человек находится больше заданного времени
{
ToiCulStatus = 1;                   //Включаем вентилятор в туалете
ManInToiCount = ManInToiCountTrig;  //И не даем счетчику превысить тригер, для обратного счета
}
}
}//else

if ((Range > RangeTrig)&&(ManInToiCount >= 1)) //Человек только что ушел
{
ManInToiCount-- ;                          //Обратный отсчет
if (ManInToiCount <= 1)                    //Не даем зайти ниже 0
ManInToiCount = 0;
ToiCulStatus = 1;                          //И вентилятор пока работает
}

if ((Range > RangeTrig)&&(DHTbyte[0]<RHTrig)&&(!ManInToiCount))   //если никого нет давно и влажность ниже заданной вырубаем в туалете
ToiCulStatus = 0;

if (DHTbyte[0]>=RHTrig)                        //Если влажность больше заданной
{
BathCulStatus = 1;
ToiCulStatus = 1;
}
else
BathCulStatus = 0;

BathSw = BathCulStatus;                         //Непосредственное управление вентиляторами
ToiSw = ToiCulStatus;
if (DHTbyte[0])                                 //Обновляем инфу
DisplayAll();

InitTimer2();                                   //Запускаем таймер заново

}

Now we can adjust the refreshing time and threshold time of precence value by the variable ManInToiCountTrig.

For my tests I settled this value equal to 4, i.e. 26 seconds. Time to compose a test setup!

During my tests I found a quite unfortunate thing – the program was freezing quite often, especially when ultrasonic sensor was used and the obstacle was far from it. I was already ready to add the external watchdog, but then added a capacitor on the RH pin, it helped mostly, but there were still accidental freezes so I decided to add the watchdog. The question is how to determine that device is working and not frozen. In our case we have on a constant basis the refreshing of the data from sensor and display info.

Thus, the one of possibilities is to monitor the Trig wire – if for a certain time the line has not changed its state then we need to restart the uC.

We have this ugliness in a result:

Part #3. The battlefield trials

The video:

The beta version has been tested and working normally in principle, now things which we need to modify:

• The dht11 sensor is not reliable at all, better to replace it on dht22
• Need to add a possibility of adjusting of the time from appearing the human in the bathroom to the switching on the fan. Also would be good to adjust how long the fan should work after the person has left the bathroom
• The aesthetic view not in the best, but the device user is in renovation stage still and will polish it by himself

The sources