Rules optimisation & error handling

Moderators: grovkillen, Stuntteam, TD-er

Post Reply
Message
Author
izeesp
Normal user
Posts: 6
Joined: 26 Sep 2019, 08:40

Rules optimisation & error handling

#1 Post by izeesp » 02 Oct 2019, 12:07

Hello Guys,

I'm currently trying to automate my indoor grow tent.
It is composed of
  • 200 lm301b leds associated to an AC-DC meanwell driver
  • a 220v (14W) heating mat
  • a 5v (2.5W) mist masker
  • a 12v pc fan
  • a htu21d sensor
  • a bh1750 lux sensor
  • 2 DC-DC SSR
  • 2 DC-AC mechanical relays
  • a nodemcu
I want it to be automated this way and would like it to be totally autonomous (no inputs from tiers):
  • Light on between lightBegin and lightEnd
  • Fan on for w minutes then fan off for x minutes
  • Mist maker for on for y minutes every z minutes (if htu21d humidity below a threshold)
  • Heat on when temperature below lowerThreshold and heat off when temperature below upperThreshold

So I have implemented those rules (all values/thresholds are hardcoded for now)

Code: Select all

On System#Boot do
  gpio,12,0       //Light pin
  gpio,13,0       //Vent pin
  gpio,14,0       //Mist pin
  gpio,15,0       //Heat pin
  timerSet,1,10   //Light timer
  timerSet,2,11   //Vent on timer
  timerSet,3,0    //Vent off timer
  timerSet,4,12   //Mist on timer
  timerSet,5,0    //Mist off timer
  timerSet,6,60
endon

//Light timer
On Rules#Timer=1 do
 if %syshour%>6 and %syshour%<22
   gpio,12,1
 else
   gpio,12,0
 endif
 timerSet,1,60
endon


//Vent on timer
On Rules#Timer=2 do
  gpio,14,1
  timerSet,3,120 
endon

//Vent off timer
On Rules#Timer=3 do
  gpio,14,0
  timerSet,2,180 
endon


//Mist on timer
On Rules#Timer=4 do
  if [htu21d#humidity]>0.0 and [htu21d#humidity]<25.0
    Publish %sysname%/humidity,[htu21d#humidity]
    gpio,13,1
    timerSet,5,60
  endif
  timerSet,4,3600
endon

//Mist off timer
On Rules#Timer=5 do
  gpio,13,0
endon


//Heat on
On htu21d#temperature<20 do
  gpio,15,1
endon
//Heat off
on htu21d#temperature>22 do
  gpio,15,0
endon

//Emergency stop
On Rules#Timer=6 do
  if [htu21d#temperature]>40 or [htu21d#humidity]>85
   gpio,12,0
   gpio,13,0
   gpio,14,0
   gpio,15,0
   timerSet,1,0
   timerSet,2,0
   timerSet,3,0
   timerSet,4,0
   timerSet,5,0
   timerSet,6,0
  else
     timerSet,6,60
  endif
endon


I have 2 goals now:
  1. The main goal is to have a totally autonomous esp that does not rely on an external device to work (it's already done) BUT I'd like if needed to be also able to set the values/threshold via events. I know those values could survive a reboot and not a restart but that's not the point here.
    I think I have to setup dummy devices and variables but don't know the most efficient way of doing it.
    If we take only the light example, I was thinking about someting like this (not tested yet)

    Code: Select all

    On System#Boot do
      gpio,12,0       //Light pin
      TaskValueSet 10,1,7   //[dummy#lightBegin]
      TaskValueSet 10,1,23 //[dummy#lightEnd]
      timerSet,1,10   //Light timer
    endon
    
    On Rules#Timer=1 do
     if %syshour%>[dummy#lightBegin] and %syshour%<[dummy#lightEnd]
       gpio,12,1
     else
       gpio,12,0
     endif
     timerSet,1,60
    endon
    
    On lightOn do
      TaskValueSet 10,1,%eventvalue% //[dummy#lightBegin]
    	timerSet,1,1                         // Force the light loop to take the new lightBegin parameter
    endon
    On lightOff do
      TaskValueSet 10,2,%eventvalue% //[dummy#lightEnd]
    	timerSet,1,1                         // Force the light loop to take the new lightEnd parameter
    endon
    
    Would it work? How could it be optimized?
  2. I'd also like to be able to handle sensor erros: How do espeasy handle i2c or sensor errors? I'd like to be able to shutdown everything if temperature > 40 or humidity > 80 or htu21d sensor not responding
    Does the plugin send a NaN, a 0 or another value if a I2C pin is disconnected or the sensor broken?
    When I disconnect a pin and reboot, the last valid value is still pushed to MQTT -> How to hande this case? I was thinking of storing the last value in a Dummy var and compare it with the current. If it's exactly the same (float with 2 digits), sensor might not be working and all devices should be shutdown.
    When I disconnect a pin and restart, value seems to be 0 (didn't have time to test it) -> How to hande this case? I can add conditions for temperature >0 and humidity> which seems relevant because in this context, neither temperature nor humidity should reach this value
Regards and thanks again for your hardwork,
Ize

User avatar
ThomasB
Normal user
Posts: 406
Joined: 17 Jun 2018, 20:41
Location: USA

Re: Rules optimisation & error handling

#2 Post by ThomasB » 04 Oct 2019, 04:46

1.
I was thinking about someting like this (not tested yet)
Your proposed rules look reasonable.

2.
How do espeasy handle i2c or sensor errors?
The sensor will report the i2c read error in the logs. But it doesn't appear that a rule can detect a read error.
How to hande this case? I was thinking of storing the last value in a Dummy var and compare it with the current. If it's exactly the same (float with 2 digits), sensor might not be working and all devices should be shutdown.
I agree that is a possible solution. See explanation in next comment.
When I disconnect a pin and restart, value seems to be 0 (didn't have time to test it) -> How to hande this case?
If the sensor's i2c is disconnected then the plugin's data reads will not be updated. So the last stored value is what you will see. Therefore, on cold boot the data is zero'd and so it will remain zero until the i2c is working again. That is to say, if the temperature and humidity remain at zero for any length of time then the sensor is in trouble.

- Thomas

TD-er
Core team member
Posts: 1656
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: Rules optimisation & error handling

#3 Post by TD-er » 04 Oct 2019, 11:41

ThomasB wrote:
04 Oct 2019, 04:46
[...]
How do espeasy handle i2c or sensor errors?
The sensor will report the i2c read error in the logs. But it doesn't appear that a rule can detect a read error.
[...]
That's an interesting remark you just made.
I guess it would make sense for a plugin to send out an event like [<taskname>#error] or something like that.
Then you can use that as a trigger in rules.

Maybe the opposite notation would make even more sense, like [taskReadError#<taskname>] to allow matching any error or trigger on specific errors.

User avatar
ThomasB
Normal user
Posts: 406
Joined: 17 Jun 2018, 20:41
Location: USA

Re: Rules optimisation & error handling

#4 Post by ThomasB » 04 Oct 2019, 18:05

TD-er wrote:
04 Oct 2019, 11:41
I guess it would make sense for a plugin to send out an event like [<taskname>#error] or something like that.
Good idea. But I'd prefer to have the existing float values return a unique number such as -999.99 to indicate read failure. This would allow for simpler rule structure when checking for valid data. Along with that, you could include your idea for those that want a global trigger event for data read errors.

- Thomas

TD-er
Core team member
Posts: 1656
Joined: 01 Sep 2017, 22:13
Location: the Netherlands
Contact:

Re: Rules optimisation & error handling

#5 Post by TD-er » 05 Oct 2019, 00:23

Well then matching "NaN" could be a valid error state?
That's not possible right now (matching a nan is also tricky in programming languages, since you can only check for it when "a != a")

User avatar
ThomasB
Normal user
Posts: 406
Joined: 17 Jun 2018, 20:41
Location: USA

Re: Rules optimisation & error handling

#6 Post by ThomasB » 05 Oct 2019, 20:25

Well then matching "NaN" could be a valid error state?
Sounds good. NaN or unique float values are both candidates for indicating a bad read.

Unfortunately this doesn't immediately help the OP. I think the discussed workaround ideas are the best choice at this point.

- Thomas

izeesp
Normal user
Posts: 6
Joined: 26 Sep 2019, 08:40

Re: Rules optimisation & error handling

#7 Post by izeesp » 06 Oct 2019, 15:40

ThomasB wrote:
04 Oct 2019, 04:46
1.
I was thinking about someting like this (not tested yet)
Your proposed rules look reasonable.

2.
How do espeasy handle i2c or sensor errors?
The sensor will report the i2c read error in the logs. But it doesn't appear that a rule can detect a read error.
How to hande this case? I was thinking of storing the last value in a Dummy var and compare it with the current. If it's exactly the same (float with 2 digits), sensor might not be working and all devices should be shutdown.
I agree that is a possible solution. See explanation in next comment.
When I disconnect a pin and restart, value seems to be 0 (didn't have time to test it) -> How to hande this case?
If the sensor's i2c is disconnected then the plugin's data reads will not be updated. So the last stored value is what you will see. Therefore, on cold boot the data is zero'd and so it will remain zero until the i2c is working again. That is to say, if the temperature and humidity remain at zero for any length of time then the sensor is in trouble.

- Thomas
Thanks for the feedback.
Implementing a basic error handling could be a great feature

izeesp
Normal user
Posts: 6
Joined: 26 Sep 2019, 08:40

Re: Rules optimisation & error handling

#8 Post by izeesp » 06 Oct 2019, 16:14

Si I rewrote everything to include basic input validation.
The code size almost quadrupled compared to the hardcoded version.
Rules Set 1

Code: Select all

On System#Boot do
  gpio,12,0            //light
  timerSet,1,10
  timerSet,2,0
  TaskValueSet 8,1,7
  TaskValueSet 8,2,23
  gpio,13,0            //vent
  timerSet,3,12
  timerSet,4,0
  TaskValueSet 8,3,120
  TaskValueSet 8,4,180
  gpio,14,0            //mist
  timerSet,5,14
  timerSet,6,0
  TaskValueSet 9,1,120
  TaskValueSet 9,2,3600
  TaskValueSet 9,3,80
  gpio,15,0            //heat
  TaskValueSet 10,1,22
  TaskValueSet 10,2,25
  timerSet,7,60
endon

//Light
On Rules#Timer=1 do
 if %syshour%>[config1#lightOnHour] and %syshour%<[config1#lightOffHour]
   gpio,12,1
 else
   gpio,12,0
 endif
 timerSet,1,60
endon
On Rules#Timer=2 do
 if %syshour%>[config1#lightOnHour] and %syshour%<[config1#lightOffHour]
   gpio,12,0
 else
   gpio,12,1
 endif
 timerSet,2,60
endon

//Vent
On Rules#Timer=3 do
  gpio,14,1
  timerSet,4,[config1#ventOnSec] //always on if 0
endon
On Rules#Timer=4 do
  gpio,14,0
  timerSet,3,[config1#ventOffSec]
endon

//Mist
On Rules#Timer=5 do
  if [htu21d#humidity]>0.0 and [htu21d#humidity]<[config2#mistH]
    gpio,13,1
    timerSet,6,[config2#mistOnSec]
  endif
  timerSet,5,[config2#mistOffSec]
endon
On Rules#Timer=6 do
  gpio,13,0
endon

//Emergency stop
On Rules#Timer=7 do
  if [htu21d#temperature]>40 or [htu21d#humidity]>85
    event stopAll
  else
     timerSet,7,60
  endif
endon
Rules Set 2

Code: Select all

On lightOn do
  if %eventvalue%>=0 and %eventvalue%<24 and %eventvalue%!=[config1#lightOffHour]
    TaskValueSet 8,1,%eventvalue%
    event lightConditions
  else
    Publish %sysname%/errors/light,lightOn: %eventvalue%
  endif
endon
On lightOff do
  if %eventvalue%>=0 and %eventvalue%<24 and %eventvalue%!=[config1#lightOnHour]
    TaskValueSet 8,2,%eventvalue%
    event lightConditions
  else
    Publish %sysname%/errors/light,lightOff: %eventvalue%
 endif
endon

on lightConditions do
  if [config1#lightOffHour]>[config1#lightOnHour]
    timerSet,1,1
    timerSet,2,0
  else
    timerSet,1,0
    timerSet,2,1
  endif
endon

On ventOn do
  if %eventvalue%>=0 and %eventvalue%<600
    TaskValueSet 8,3,%eventvalue%
    timerSet,3,1
    timerSet,4,0
  else
    Publish %sysname%/errors/vent,ventOn: %eventvalue%
  endif
endon

On ventOff do
  if %eventvalue%>=0 and %eventvalue%<3600
    TaskValueSet 8,4,%eventvalue%
    timerSet,3,0
    timerSet,4,1
  else
    Publish %sysname%/errors/vent,ventOff: %eventvalue%
 endif
endon
Rules Set 3

Code: Select all

On mistOn do
  if %eventvalue%>0 and %eventvalue%<120
    TaskValueSet 9,1,%eventvalue%
    timerSet,5,1
    timerSet,6,0
  else
    Publish %sysname%/errors/mist,mistOn: %eventvalue%
  endif
endon

On mistOff do
  if %eventvalue%>0 and %eventvalue%<3600
    TaskValueSet 9,2,%eventvalue%
    timerSet,5,0
    timerSet,6,1
  else
    Publish %sysname%/errors/mist,mistOff: %eventvalue%
  endif
endon

On mistH do
  if %eventvalue%>0 and %eventvalue%<60
    TaskValueSet 9,3,%eventvalue%
    timerSet,5,1
    timerSet,6,0
  else
    Publish %sysname%/errors/mist,mistH: %eventvalue%
  endif
endon

On heatOn do
  if %eventvalue%>0 and %eventvalue%<99 and %eventvalue%<[config3#heatL]
    TaskValueSet 9,1,%eventvalue%
  else
    Publish %sysname%/errors/heat,heatOn: %eventvalue%
  endif
endon
On heatOff do
  if %eventvalue%>0 and %eventvalue%<99 and %eventvalue%>[config3#heatH]
    TaskValueSet 9,2,%eventvalue%
  else
    Publish %sysname%/errors/heat,heatOff: %eventvalue%
  endif
endon

on stopAll do
  gpio,12,0
  gpio,13,0
  gpio,14,0
  gpio,15,0
  timerSet,1,0
  timerSet,2,0
  timerSet,3,0
  timerSet,4,0
  timerSet,5,0
  timerSet,6,0
  timerSet,7,0
endon
Rules Set 4

Code: Select all

On htu21d#temperature do
  if [htu21d#temperature]<[config3#heatL]
    gpio,15,1
  endif
  if [htu21d#temperature]>[config3#heatH]
    gpio,15,0
  endif
  if [htu21d#temperature]=[config4#temp1] and [htu21d#temperature]=[config4#temp2]
    event stopAll
  endif
  if [htu21d#humidity]=[config4#humi1] and [htu21d#humidity]=[config4#humi2]
    event stopAll
  endif
endon

As expected, it complexifies a lot the rules and their readability :(
I don't know if I can do much about it, can I?
For a reason I ignore, I couldn't post a ruleset with more than 1300 characters. Maybe a lack of RAM?
So I had to split the rules in 4 rules set (instead of theorically 2)

Next step would be compile espeasy from scratch with only the needed devices/sensors t(o have the more free ram posssible since it seems that very few ram is available) and latest arduino 2.6.0 core!
It seems much more hard than creating the rules sets.
What is the most up to date wiki/guide to do so?

Regards,
Iz

Post Reply

Who is online

Users browsing this forum: No registered users and 16 guests