/** * @file PlantCtrl.h * @author your name (you@domain.com) * @brief Abstraction to handle the Sensors * @version 0.1 * @date 2020-05-27 * * @copyright Copyright (c) 2020 * */ #ifndef PLANT_CTRL_H #define PLANT_CTRL_H #include "HomieTypes.h" #include #include "ControllerConfiguration.h" #include "RunningMedian.h" #include "MathUtils.h" #include "MQTTUtils.h" #include "LogDefines.h" #define ANALOG_REREADS 5 #define MOISTURE_MEASUREMENT_DURATION 400 /** ms */ #define PWM_FREQ 50000 #define PWM_BITS 8 class Plant { private: HomieNode *mPlant = NULL; HomieInternals::PropertyInterface mPump; RunningMedian mMoisture_raw = RunningMedian(ANALOG_REREADS); RunningMedian mTemperature_degree = RunningMedian(ANALOG_REREADS); int mPinSensor = 0; /**< Pin of the moist sensor */ int mPinPump = 0; /**< Pin of the pump */ bool mConnected = false; int mPlantId = -1; SENSOR_MODE mSensorMode; public: PlantSettings_t *mSetting; /** * @brief Construct a new Plant object * * @param pinSensor Pin of the Sensor to use to measure moist * @param pinPump Pin of the Pump to use */ Plant(int pinSensor, int pinPump, int plantId, HomieNode *plant, PlantSettings_t *setting, SENSOR_MODE mode); void postMQTTconnection(void); void advertise(void); // for sensor that might take any time void blockingMoistureMeasurement(void); // for sensor that need a start and a end in defined timing void startMoistureMeasurement(void); void stopMoistureMeasurement(void); void deactivatePump(void); void activatePump(void); String getSensorModeString() { SENSOR_MODE mode = getSensorMode(); return SENSOR_STRING[mode]; } bool isTimerOnly() { long current = this->mSetting->pSensorDry->get(); return equalish(current, TIMER_ONLY); } bool isHydroponic() { long current = this->mSetting->pSensorDry->get(); return equalish(current, HYDROPONIC_MODE); } SENSOR_MODE getSensorMode() { return mSensorMode; } /** * @brief Check if a plant is too dry and needs some water. * * @return true * @return false */ bool isPumpRequired() { if (isHydroponic() || isTimerOnly()) { // hydroponic only uses timer based controll return true; } bool isDry = getCurrentMoisturePCT() < getTargetMoisturePCT(); bool isActive = isPumpTriggerActive(); return isDry && isActive; } bool isPumpTriggerActive() { long current = this->mSetting->pSensorDry->get(); return !equalish(current, DEACTIVATED_PLANT); } float getTargetMoisturePCT() { if (isPumpTriggerActive()) { return this->mSetting->pSensorDry->get(); } else { return DEACTIVATED_PLANT; } } float getCurrentMoisturePCT() { switch (getSensorMode()) { case NONE: return DEACTIVATED_PLANT; case FREQUENCY_MOD_RESISTANCE_PROBE: return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MIN_FRQ, MOIST_SENSOR_MAX_FRQ, 0, 100); case ANALOG_RESISTANCE_PROBE: return mapf(mMoisture_raw.getMedian(), ANALOG_SENSOR_MAX_MV, ANALOG_SENSOR_MIN_MV, 0, 100); } return MISSING_SENSOR; } float getCurrentMoistureRaw() { if (getSensorMode() == FREQUENCY_MOD_RESISTANCE_PROBE) { if (mMoisture_raw.getMedian() < MOIST_SENSOR_MIN_FRQ) { return MISSING_SENSOR; } else if (mMoisture_raw.getMedian() > MOIST_SENSOR_MAX_FRQ) { return SHORT_CIRCUIT_MODE; } } return mMoisture_raw.getMedian(); } HomieInternals::SendingPromise &setProperty(const String &property) const { return mPlant->setProperty(property); } void init(void); void initSensors(void); long getCooldownInSeconds() { return this->mSetting->pPumpCooldownInSeconds->get(); } /** * @brief Get the Hours when pumping should start * * @return hour */ int getHoursStart() { return this->mSetting->pPumpAllowedHourRangeStart->get(); } /** * @brief Get the Hours when pumping should end * * @return hour */ int getHoursEnd() { return this->mSetting->pPumpAllowedHourRangeEnd->get(); } bool isAllowedOnlyAtLowLight(void) { if (this->isHydroponic()) { return false; } return this->mSetting->pPumpOnlyWhenLowLight->get(); } void publishState(int stateNumber, String stateString); bool switchHandler(const HomieRange &range, const String &value); void setSwitchHandler(HomieInternals::PropertyInputHandler f); long getPumpDuration() { return this->mSetting->pPumpDuration->get(); } long getPumpMl() { return this->mSetting->pPumpMl->get(); } }; #endif