I made a quick update to the code last night to implement better rules for the lights and heaters. Basically I added a range, similar to how a thermostat works. So now instead of turning the heater on when the temp drops below 55 degrees, and back off again when the temp goes above 55, I added a 2-degree buffer range. Now when the temp goes below 54 the heater comes on, and when the temp goes back above 56 the heater is shut off. This way the heater will actually stay on for a few minutes at a time rather than performing a rapid on/off cycle right around the target temperatures. I think I mentioned a similar problem with the light controls, where the lights would be turned on, then the light sensor detected it was bright inside the greenhouse and turned the lights back off again, resulting in a 10-second cycle period.
The next step is implementing an actual rule system that can be changed on the fly. I built some code for this but unfortunately it makes heavy usage of strings, which of course are very badly handled in the arduino environment. I don't really know much about working with chars in C though, and the rule interpreter makes heavy use of substring to break apart the rules and handle parenthesis within the structure. I'm going to try and finalize my version of the routine and get it working with live data, but hopefully someone else will come along that can rewrite the routines in a more standardized (and memory-friendly) way.
Had my first lockup since putting the ESP32 in the greenhouse. I knew it would happen eventually, so I started looking at how to implement a watchdog today. It seems that most methods require compiling under IDF rather than the arduino IDE, which I have no experience with. I did find a bug report and some sample methods that should work in the arduino IDE however, so I'll try adding in one of those tonight.