Design principles: The bare minimum

  • A box is connected to the fan and has one button on the front.
  • Pressing the button causes the fan to start and a counter to start counting down, e. g. 30 minutes.
  • When the countdown reaches 0, the fan stops.
  • Pressing the button while the fan is on, stops the fan.
  • The housing shall be waterproof, due to the humid environment in the bathroom it is necessary to protect the electronics.
  • The controller shall know the state of the fan.
  • The fan can be remotely switched on by the controller. This way, we can set the fan to be on automatically at least once per day, e.g. in the afternoon from 14h onwards.
  • Switching the fan remotely on causes the counter to start as if the "play" button was pressed.
  • If the fan is on, a remote command shall be able to stop the fan immediately.
  • The fan has a europlug.

Optional features that did not make it

  • Powering up shall cause the fan to be off - NO: last state is remembered.
  • The counter start value shall be programmed fixed in the fan module - NO: in the controller.
  • All functions shall work independently of the controller - NO: goal not reached.
  • The counter start value shall be programmed in the fan module. Maybe with a rotary encoder with 10 positions we can set 10 minute intervals; 10, 20, 30, ... 90 minutes. - NO: it is a Domoticz setting.
  • The starting value of the counter can be adjusted in a menu per minute. On the module or on the controller?
  • Optional: Once the fan is on, the user can press the up or down keys to increase/decrease the counter.
  • Optional: The module shall measure the air humidity, and switch on above a certain percentage (and then off below a lower given percentage). This is good in the summer, but not in the winter, because then the heater is probably on too - and you do not want the heater on with the fan running.
  • When started due to humidity rise, there shall be a timeout after which the fan goes off, even if the humidity did not go down.
  • If this module has a display and 6 buttons, then: Potential case at https://www.thingiverse.com/thing:142282
  • The room should have a window open sensor and an automatic control of the heater, which may all work together with rules and maybe scenes on the controller.
  • Optional: The module shall measure the current through the relay, and switch off when excessively low or high.

Behavior

Fan Statechart diagram

Used components

Domoticz

If the window is not open, and the time is right, then the fan goes on for a while:

Blockly for the fan

Blockly for the fan

And there is a dzVents script:

return
{
    on =
    {
        timer = {'every minute',}
    },

    logging = {
        level = domoticz.LOG_INFO,
        marker = 'BathroomFanHumidity',
    },

    data = {
        BFHFanMinutesPerDayCounter = { initial = 0 }, -- the fan should be on for a max number of minutes per day
        BFHBathRoomWasWetLastMinute = { initial = false }, -- previous time, the bathroom was wet
        BFHWetPeriodsPerDayCounter = { initial = 0 }, -- how many times the bathroom was wet today (wet periods)
        BFHMinutesPerPeriodCounter = { initial = 0 }, -- the fan should be on for a max number of minutes per wet period
    },

    execute = function(domoticz)
        local Time = require('Time')
        local currentTime = Time()

        if (domoticz.devices('Badkamer - FAN').state == 'On')
        then
            domoticz.data.BFHFanMinutesPerDayCounter = domoticz.data.BFHFanMinutesPerDayCounter + 1
        end

        if  domoticz.devices('B - Badkamer - WOS-TH').humidity > 97   -- this is the main trigger:
                                                                      -- the bathroom is wet
        and domoticz.devices('F - Slaapkamer - WOS-TH').humidity < 90 -- outside humidity is
                                                                      -- lower than inside
        and domoticz.devices('TempHumBaro').humidity < 85 -- the OpenWeather current humidity
                                                          -- in my home town
        and ( domoticz.devices('B - Badkamer - WOS-RC').state ~= 'Open' -- the window is closed
            or currentTime.hour >= 7 -- from 00:00 to 07:00 we only switch the fan on if the window
                                     -- is closed to prevent noise while we sleep
        )
        and domoticz.data.BFHFanMinutesPerDayCounter < 100 -- the FAN is ON maximum 100 minutes per day
        and domoticz.data.BFHMinutesPerPeriodCounter < 45  -- the FAN is ON maximum 45 minutes
                                                           -- per wet period
        and domoticz.devices('Badkamer - FAN').state == 'Off'
        then
            domoticz.devices('Badkamer - FAN').switchOn().forMin(8)
        end

        if  domoticz.devices('B - Badkamer - WOS-TH').humidity > 97 -- the bathroom is wet
        then
            if (domoticz.data.BFHBathRoomWasWetLastMinute)
            then -- from wet to wet
                domoticz.data.BFHMinutesPerPeriodCounter = domoticz.data.BFHMinutesPerPeriodCounter + 1
            else -- from dry to wet
                domoticz.data.BFHBathRoomWasWetLastMinute = true
                domoticz.data.BFHWetPeriodsPerDayCounter = domoticz.data.BFHWetPeriodsPerDayCounter + 1
                domoticz.data.initialize('BFHMinutesPerPeriodCounter')
            end
        else
            if (domoticz.data.BFHBathRoomWasWetLastMinute)
            then -- from wet to dry
                domoticz.log('The bathroom was wet during '
                             .. domoticz.data.BFHMinutesPerPeriodCounter
                             .. ' minutes, and is dry again now.', domoticz.LOG_INFO)
                domoticz.data.BFHBathRoomWasWetLastMinute = false
            else -- from dry to dry

            end
        end

        if (currentTime.hour <= 1
        and currentTime.min > 50    ) -- between 00:51 and 01:00 we keep resetting the counters
        then
            domoticz.data.initialize('BFHFanMinutesPerDayCounter')
            domoticz.data.initialize('BFHBathRoomWasWetLastMinute')
            domoticz.data.initialize('BFHMinutesPerPeriodCounter')
            domoticz.data.initialize('BFHWetPeriodsPerDayCounter')
        end

        if (currentTime.min == 0) -- at every whole hour
        then
            domoticz.log('Today, the bathroom fan already has run '
                         .. domoticz.data.BFHFanMinutesPerDayCounter
                         .. ' minutes.', domoticz.LOG_INFO)
            domoticz.log('Today, the batroom was already wet '
                         .. domoticz.data.BFHWetPeriodsPerDayCounter
                         .. ' times.', domoticz.LOG_INFO)
            domoticz.log('The last wet period in the bathroom lasted '
                         .. domoticz.data.BFHMinutesPerPeriodCounter
                         .. ' minutes.', domoticz.LOG_INFO)
        end
    end
}

Code

Based on https://www.mysensors.org/build/relay

Code at: fanmodule

This node is NOT a MySensors repeater, see https://www.mysensors.org/about/network.

Start without gateway

This chapter explains what will happen when the gateway can not be found.

https://forum.mysensors.org/topic/4562/upgrading-to-2-0-sketch-dificulty/10

version 2.1 nodes while booting will not proceed to the loop part of the sketch if the gateway is not found. You can however force them to move on. The line of code you need is...

#define MY_TRANSPORT_WAIT_READY_MS 3000

This code tells the node how long to wait for the uplink to be established before moving on to the rest of the sketch. The number at the end is how long to wait in milliseconds, in this case 3000ms (3 seconds). You can set this to a time of your choosing.

if the node establishes the uplink it will move on without any delay.

The above line is placed near the top of your sketch somewhere before

#include <MySensors.h>

have a look at these for a bit more detail....

MY_TRANSPORT_DONT_CARE_MODE: https://forum.mysensors.org/topic/5745/my_transport_dont_care_mode

Synchronising Light switch: https://forum.mysensors.org/topic/6948/synchronising-light-switch

Debug info

First run:

 __  __       ____
|  \/  |_   _/ ___|  ___ _ __  ___  ___  _ __ ___
| |\/| | | | \___ \ / _ \ `_ \/ __|/ _ \| `__/ __|
| |  | | |_| |___| |  __/ | | \__ \  _  | |  \__ \
|_|  |_|\__, |____/ \___|_| |_|___/\___/|_|  |___/
        |___/                      2.3.2

16 MCO:BGN:INIT NODE,CP=RNNNA---,FQ=16,REL=255,VER=2.3.2
26 MCO:BGN:BFR
28 TSM:INIT
29 TSF:WUR:MS=8000
35 !TSM:INIT:TSP FAIL
37 TSM:FAIL:CNT=1
39 TSM:FAIL:DIS
40 TSF:TDI:TSL
8031 MCO:BGN:STP
FAN 2 controller 5.0 - Online!
Initial state is: off (0).
8034 MCO:BGN:INIT OK,TSP=0
10044 TSM:FAIL:RE-INIT
10046 TSM:INIT
10052 !TSM:INIT:TSP FAIL
10054 TSM:FAIL:CNT=2
10056 TSM:FAIL:DIS
10058 TSF:TDI:TSL
20061 TSM:FAIL:RE-INIT
20063 TSM:INIT
20069 !TSM:INIT:TSP FAIL
20071 TSM:FAIL:CNT=3
20073 TSM:FAIL:DIS
20075 TSF:TDI:TSL
30078 TSM:FAIL:RE-INIT
30081 TSM:INIT
30087 !TSM:INIT:TSP FAIL
30089 TSM:FAIL:CNT=4
30091 TSM:FAIL:DIS
30093 TSF:TDI:TSL
40096 TSM:FAIL:RE-INIT
40098 TSM:INIT
40104 !TSM:INIT:TSP FAIL
40107 TSM:FAIL:CNT=5
40109 TSM:FAIL:DIS
40111 TSF:TDI:TSL

Second try:

 __  __       ____
|  \/  |_   _/ ___|  ___ _ __  ___  ___  _ __ ___
| |\/| | | | \___ \ / _ \ `_ \/ __|/ _ \| `__/ __|
| |  | | |_| |___| |  __/ | | \__ \  _  | |  \__ \
|_|  |_|\__, |____/ \___|_| |_|___/\___/|_|  |___/
        |___/                      2.3.2

16 MCO:BGN:INIT NODE,CP=RNNNA---,FQ=16,REL=255,VER=2.3.2
26 MCO:BGN:BFR
28 TSM:INIT
29 TSF:WUR:MS=8000
36 TSM:INIT:TSP OK
37 TSF:SID:OK,ID=1
39 TSM:FPAR
44 ?TSF:MSG:SEND,1-1-255-255,s=255,c=3,t=7,pt=0,l=0,sg=0,ft=0,st=OK:
2051 !TSM:FPAR:NO REPLY
2053 TSM:FPAR
2057 ?TSF:MSG:SEND,1-1-255-255,s=255,c=3,t=7,pt=0,l=0,sg=0,ft=0,st=OK:
2362 TSF:MSG:READ,14-14-1,s=255,c=3,t=8,pt=1,l=1,sg=0:1
2367 TSF:MSG:FPAR OK,ID=14,D=2
2557 TSF:MSG:READ,17-17-1,s=255,c=3,t=8,pt=1,l=1,sg=0:2
2782 TSF:MSG:READ,20-20-1,s=255,c=3,t=8,pt=1,l=1,sg=0:1
3088 TSF:MSG:READ,0-0-1,s=255,c=3,t=8,pt=1,l=1,sg=0:0
3092 TSF:MSG:FPAR OK,ID=0,D=1
4064 TSM:FPAR:OK
4065 TSM:ID
4066 TSM:ID:OK
4068 TSM:UPL
4071 TSF:MSG:SEND,1-1-0-0,s=255,c=3,t=24,pt=1,l=1,sg=0,ft=0,st=OK:1
4080 TSF:MSG:READ,0-0-1,s=255,c=3,t=25,pt=1,l=1,sg=0:1
4085 TSF:MSG:PONG RECV,HP=1
4088 TSM:UPL:OK
4089 TSM:READY:ID=1,PAR=0,DIS=1
4094 TSF:MSG:SEND,1-1-0-0,s=255,c=3,t=15,pt=6,l=2,sg=0,ft=0,st=OK:0100
4102 TSF:MSG:READ,0-0-1,s=255,c=3,t=15,pt=6,l=2,sg=0:0100
4109 TSF:MSG:SEND,1-1-0-0,s=255,c=0,t=17,pt=0,l=5,sg=0,ft=0,st=OK:2.3.2
4117 TSF:MSG:SEND,1-1-0-0,s=255,c=3,t=6,pt=1,l=1,sg=0,ft=0,st=OK:0
4133 TSF:MSG:READ,0-0-1,s=255,c=3,t=6,pt=0,l=1,sg=0:M
4141 TSF:MSG:SEND,1-1-0-0,s=255,c=3,t=11,pt=0,l=16,sg=0,ft=0,st=OK:FAN 2 controller
4150 TSF:MSG:SEND,1-1-0-0,s=255,c=3,t=12,pt=0,l=3,sg=0,ft=0,st=OK:5.0
4159 TSF:MSG:SEND,1-1-0-0,s=1,c=0,t=3,pt=0,l=12,sg=0,ft=0,st=OK:Fan module 2
4166 MCO:REG:REQ
4169 TSF:MSG:SEND,1-1-0-0,s=255,c=3,t=26,pt=1,l=1,sg=0,ft=0,st=OK:2
4175 TSF:MSG:READ,0-0-1,s=255,c=3,t=27,pt=1,l=1,sg=0:1
4180 MCO:PIM:NODE REG=1
4183 MCO:BGN:STP
FAN 2 controller 5.0 - Online!
Initial state is: off (0).
4187 MCO:BGN:INIT OK,TSP=1

Tip: try pasting the above in https://www.mysensors.org/build/parser

Addition of a temperature & humidity sensor

Since the fan should run when the bathroom is wet, it would be best to have a humidity sensor in the same node. So, I added a SHT30 sensor. This node is housed in a waterproof case, suitable for a bathroom, so the sensor needs to be properly encapsulated.

Wrap around of the Arduino millis() function

The function "milis()" returns the number of milliseconds passed since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 50 days. See https://www.arduino.cc/reference/en/language/functions/time/millis/.

The wrap around to zero caused the sending of temperature and humidity values to stop. So, in version 7.0 of the sketch, I added:

void loop()
{
    while (millis() < 100UL) {
        // Wrap-around of millis() happens every 50 days.
        // Just start all over with all timers:
        state_entered = 0;
        last_heartbeat_time = 0;
        last_th_sample_time = 0;
    }
    ...

Let's hope this will fix the problem - we need to wait 50 days now to see if it did ...

Tags: domotica nodes
Comments: