From be40a8c2d340e5d5492076bb9e7a17f4ec68c55c Mon Sep 17 00:00:00 2001
From: jonny_l480 <jonny@wwad.de>
Date: Tue, 15 Aug 2023 17:10:59 +0200
Subject: [PATCH] Rework fan control: single pin, more delay, config

- remove second fan instance since both fans are controlled via one pin
  now
- more config options so fans turn on less at short movements
  => less noise and less relay cycles
---
 main/config.cpp | 13 ++++-----
 main/config.hpp |  7 ++---
 main/fan.cpp    | 76 +++++++++++++++++++++++++++++++++----------------
 main/fan.hpp    | 19 +++++++++----
 main/main.cpp   |  8 ++----
 5 files changed, 76 insertions(+), 47 deletions(-)

diff --git a/main/config.cpp b/main/config.cpp
index f1003f3..f26d5cd 100644
--- a/main/config.cpp
+++ b/main/config.cpp
@@ -97,15 +97,12 @@ joystick_config_t configJoystick = {
 //----------------------------
 //--- configure fan contol ---
 //----------------------------
-fan_config_t configFanLeft = {
-    .gpio_fan = GPIO_NUM_13, //FIXME simplify fan control! now only one pin used - might cause issues
-    .msRun = 5000,
-    .dutyThreshold = 35
-};
-fan_config_t configFanRight = {
+fan_config_t configCooling = {
     .gpio_fan = GPIO_NUM_13,
-    .msRun = 5000,
-    .dutyThreshold = 35
+    .dutyThreshold = 40,
+	.minOnMs = 1500,
+	.minOffMs = 3000,
+	.turnOffDelayMs = 5000,
 };
 
 
diff --git a/main/config.hpp b/main/config.hpp
index 1857310..e81734e 100644
--- a/main/config.hpp
+++ b/main/config.hpp
@@ -14,7 +14,7 @@
 
 //in IDLE mode: set loglevel for evaluatedJoystick to DEBUG 
 //and repeatedly read joystick e.g. for manually calibrating / testing joystick
-#define JOYSTICK_LOG_IN_IDLE
+//#define JOYSTICK_LOG_IN_IDLE
 
 
 //create global controlledMotor instances for both motors
@@ -39,7 +39,6 @@ extern automatedArmchair armchair;
 //create global httpJoystick object
 extern httpJoystick httpJoystickMain;
 
-//configuration for fans
-extern fan_config_t configFanLeft;
-extern fan_config_t configFanRight;
+//configuration for fans / cooling
+extern fan_config_t configCooling;
 
diff --git a/main/fan.cpp b/main/fan.cpp
index d6be041..eeafb99 100644
--- a/main/fan.cpp
+++ b/main/fan.cpp
@@ -15,15 +15,16 @@ static const char * TAG = "fan-control";
 //-----------------------------
 //-------- constructor --------
 //-----------------------------
-controlledFan::controlledFan(fan_config_t config_f, controlledMotor* motor_f ){
-    //copy config
-    config = config_f;
-    //copy pointer to motor object
-    motor = motor_f;
+controlledFan::controlledFan (fan_config_t config_f, controlledMotor* motor1_f, controlledMotor* motor2_f ){
+	//copy config
+	config = config_f;
+	//copy pointer to motor objects
+	motor1 = motor1_f;
+	motor2 = motor2_f;
 
-    //initialize gpio pin
-    gpio_pad_select_gpio(config.gpio_fan);
-    gpio_set_direction(config.gpio_fan, GPIO_MODE_OUTPUT);
+	//initialize gpio pin
+	gpio_pad_select_gpio(config.gpio_fan);
+	gpio_set_direction(config.gpio_fan, GPIO_MODE_OUTPUT);
 }
 
 
@@ -32,25 +33,50 @@ controlledFan::controlledFan(fan_config_t config_f, controlledMotor* motor_f ){
 //--------- handle ---------
 //--------------------------
 void controlledFan::handle(){
-    //get current state of the motor
-    motorStatus = motor->getStatus();
+	//get current state of the motor
+	motor1Status = motor1->getStatus();
+	motor2Status = motor2->getStatus();
 
-    //TODO Add statemachine for more specific control? Exponential average?
-    //update timestamp if threshold exceeded
-    if (motorStatus.duty > config.dutyThreshold){
-        timestamp_lastThreshold = esp_log_timestamp();
-    }
+	//update timestamp if any threshold exceeded
+	if (motor1Status.duty > config.dutyThreshold
+			|| motor2Status.duty > config.dutyThreshold){ //TODO add temperature threshold
+		if (!needsCooling){
+			timestamp_needsCoolingSet = esp_log_timestamp();
+			needsCooling = true;
+		}
+		timestamp_lastThreshold = esp_log_timestamp();
+	} else {
+		needsCooling = false;
+	}
 
-    //turn fan on if time since last exceeded threshold is less than msRun
-    if (esp_log_timestamp() - timestamp_lastThreshold < config.msRun) {
-        gpio_set_level(config.gpio_fan, 1);        
-        ESP_LOGD(TAG, "fan is on (gpio: %d)", (int)config.gpio_fan);
-    }
-    //otherwise turn fan off
-    else {
-        gpio_set_level(config.gpio_fan, 0);        
-        ESP_LOGD(TAG, "fan is off (gpio: %d)", (int)config.gpio_fan);
-    }
+
+	//turn off condition
+	if (fanRunning
+			&& !needsCooling //no more cooling required
+			&& (motor1Status.duty == 0) && (motor2Status.duty == 0) //both motors are off 
+			   //-> keeps fans running even when lower than threshold already, however turnOffDelay already started TODO: start turn off delay after motor stop only?
+			&& (esp_log_timestamp() - timestamp_lastThreshold) > config.turnOffDelayMs){ //turn off delay passed
+		fanRunning = false;
+		gpio_set_level(config.gpio_fan, 0);        
+		timestamp_turnedOff = esp_log_timestamp();
+		ESP_LOGI(TAG, "turned fan OFF gpio=%d, minOnMs=%d, WasOnMs=%d", (int)config.gpio_fan, config.minOnMs, esp_log_timestamp()-timestamp_turnedOn);
+	}
+
+	//turn on condition
+	if (!fanRunning
+			&& needsCooling
+			&& ((esp_log_timestamp() - timestamp_turnedOff) > config.minOffMs) //fans off long enough
+			&& ((esp_log_timestamp() - timestamp_needsCoolingSet) > config.minOnMs)){ //motors on long enough
+		fanRunning = true;
+		gpio_set_level(config.gpio_fan, 1);        
+		timestamp_turnedOn = esp_log_timestamp();
+		ESP_LOGI(TAG, "turned fan ON gpio=%d, minOffMs=%d,  WasOffMs=%d", (int)config.gpio_fan, config.minOffMs, esp_log_timestamp()-timestamp_turnedOff);
+	}
+
+	//TODO Add statemachine for more specific control? Exponential average?
+	//TODO idea: try other aproach? increment a variable with certain weights e.g. integrate over duty, then turn fans on and decrement the variable again
+	
+	ESP_LOGD(TAG, "fanState=%d, duty1=%f, duty2=%f, needsCooling=%d", fanRunning, motor1Status.duty, motor2Status.duty, needsCooling);
 }
 
 
diff --git a/main/fan.hpp b/main/fan.hpp
index 1300fad..a17c9b4 100644
--- a/main/fan.hpp
+++ b/main/fan.hpp
@@ -12,8 +12,10 @@ extern "C"
 //struct with all config parameters for a fan
 typedef struct fan_config_t {
     gpio_num_t gpio_fan;
-    uint32_t msRun;
     float dutyThreshold;
+	uint32_t minOnMs;
+	uint32_t minOffMs;
+	uint32_t turnOffDelayMs;
 } fan_config;
 
 
@@ -24,7 +26,7 @@ typedef struct fan_config_t {
 class controlledFan {
     public:
         //--- constructor ---
-        controlledFan (fan_config_t config_f, controlledMotor* motor_f );
+        controlledFan (fan_config_t config_f, controlledMotor* motor1_f, controlledMotor* motor2_f );
 
         //--- functions ---
         void handle();
@@ -32,9 +34,16 @@ class controlledFan {
 
     private:
         //--- variables ---
-        uint32_t timestamp_lastThreshold;
+		bool fanRunning = false;
+		bool needsCooling = false;
+		uint32_t timestamp_needsCoolingSet;
+        uint32_t timestamp_lastThreshold = 0;
+		uint32_t timestamp_turnedOn = 0;
+		uint32_t timestamp_turnedOff = 0;
         fan_config_t config;
-        controlledMotor * motor;
+        controlledMotor * motor1;
+        controlledMotor * motor2;
 
-        motorCommand_t motorStatus;
+        motorCommand_t motor1Status;
+        motorCommand_t motor2Status;
 };
diff --git a/main/main.cpp b/main/main.cpp
index a9c8e4d..f37675b 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -88,12 +88,10 @@ void task_button( void * pvParameters ){
 void task_fans( void * pvParameters ){
     ESP_LOGI(TAG, "Initializing fans and starting fan handle loop");
     //create fan instances with config defined in config.cpp
-    controlledFan fanLeft(configFanLeft, &motorLeft);
-    controlledFan fanRight(configFanRight, &motorRight);
-    //repeatedly run fan handle functions in a slow loop
+    controlledFan fan(configCooling, &motorLeft, &motorRight);
+    //repeatedly run fan handle function in a slow loop
     while(1){
-        fanLeft.handle();
-        fanRight.handle();
+        fan.handle();
         vTaskDelay(1000 / portTICK_PERIOD_MS);
     }
 }