main/task_driver.c

Sat, 20 Oct 2018 13:23:15 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 20 Oct 2018 13:23:15 +0200
changeset 0
b74b0e4902c3
child 1
ad2c8b13eb88
permissions
-rw-r--r--

Initial checkin brewboard

/**
 * @file task_driver.c
 * @brief BrewBoard relays driver. Control the hardware outputs with the
 *        Solid State relays for the Mash/Boil kettle (MLT, the Hot
 *        Liquer Tank (HLT) and the pump. The MLT has a PID controller
 *        during mashing, and a simple bang on/off control during the
 *        boil.
 *        The HLT output can be off, bang on/off, or just on if the MLT
 *        is off, depending on the configuration.
 *        Use SSR modules that switch during zero crossing of the mains
 *        power, so that when one is turned on and on the same time the
 *        other is turned off, they won't be active at the same time.
 */

#include "config.h"


#define	SSR_MLT		CONFIG_SSR_MLT_GPIO
#define	SSR_HLT		CONFIG_SSR_HLT_GPIO
#define SSR_PUMP	CONFIG_SSR_PUMP_GPIO


bool			outEnable = false;
DRIVER_State		* driver_state;
SemaphoreHandle_t	xSemaphoreDriver  = NULL;
int			MLT_pin = 0;
int			HLT_pin = 0;
int			Pump_pin = 0;
double			Input = 0, Output = 0, Setpoint = 0;
int			MLT_Mode = MLT_MODE_NONE;
double			HLT_Input = 0, HLT_Setpoint = 0;
int			HLT_Output = 0;
int			HLT_Mode = HLT_MODE_NONE;

static const char       *TAG = "task_driver";

extern SemaphoreHandle_t xSemaphoreDS18B20;
extern DS18B20_State * ds18b20_state;
extern unsigned long lastTime;


/**
 * @brief Turn the MLT SSR on or off.
 */
void MLT(int onoff);

/**
 * @brief Turn the HLT SSR on or off.
 */
void HLT(int onoff);

/**
 * @brief Turn the Pump on or off.
 */
void Pump(int onoff);



void MLT(int onoff) {

    if (onoff && outEnable) {
	gpio_set_level(SSR_MLT, 1);
	MLT_pin = 1;
    } else {
	gpio_set_level(SSR_MLT, 0);
	MLT_pin = 0;
    }
}



void HLT(int onoff) {

    if (onoff && outEnable) {
	gpio_set_level(SSR_HLT, 1);
	HLT_pin = 1;
    } else {
	gpio_set_level(SSR_HLT, 0);
	HLT_pin = 0;
    }
}



void Pump(int onoff) {

    if (onoff && outEnable) {
	gpio_set_level(SSR_PUMP, 1);
	Pump_pin = 1;
    } else {
	gpio_set_level(SSR_PUMP, 0);
	Pump_pin = 0;
    }
}



void LoadPIDsettings() {
    PID_SetTunings(equipment.PID_kP, equipment.PID_kI, equipment.PID_kD, equipment.PID_POn);
    PID_SetSampleTime(equipment.SampleTime);

    /*
     * Initialize the PID
     */
    Output = 0.0;   // Reset internal Iterm.
    PID_SetMode(PID_MANUAL);
    PID_SetMode(PID_AUTOMATIC);
}



void task_driver(void *pvParameter)
{
    TickType_t		wait_ticks, last_tick, now_tick;
    bool		rc;
    unsigned long	now, RealTime, w_StartTime = 0;

    ESP_LOGI(TAG, "Starting output drivers");

    /*
     * Configure IOMUX register.
     */
    gpio_pad_select_gpio(SSR_MLT);
    gpio_set_direction(SSR_MLT, GPIO_MODE_OUTPUT);
    gpio_pad_select_gpio(SSR_HLT);
    gpio_set_direction(SSR_HLT, GPIO_MODE_OUTPUT);
    gpio_pad_select_gpio(SSR_PUMP);
    gpio_set_direction(SSR_PUMP, GPIO_MODE_OUTPUT);

    /*
     * Initialize state
     */
    driver_state = malloc(sizeof(DRIVER_State));
    driver_state->enable = outEnable = false;
    driver_state->mlt_gpio = SSR_MLT;
    driver_state->mlt_mode = MLT_MODE_NONE;
    driver_state->mlt_sp = driver_state->mlt_pv = 0.0;
    driver_state->mlt_power = 0;
    driver_state->hlt_gpio = SSR_HLT;
    driver_state->hlt_mode = HLT_MODE_NONE;
    driver_state->hlt_sp = driver_state->hlt_pv = 0.0;
    driver_state->hlt_power = 0;
    driver_state->hlt_and_mlt = false;
    driver_state->pump_gpio = SSR_PUMP;
    driver_state->pump_run = 0;

    PID(&Input, &Output, &Setpoint, 150, 1.5, 15000, PID_P_ON_E, PID_DIRECT);
    
    /*
     * One loop must complete in 20 mSecs, that is one mains
     * frequency period cycle in 50 Hz countries.
     */
    while (1) {

	last_tick = xTaskGetTickCount();

	if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
	    /*
	     * Get the current temperature readings
	     */
	    if (xSemaphoreTake(xSemaphoreDS18B20, 10) == pdTRUE) {
		if (ds18b20_state->mlt_valid)
		    driver_state->mlt_pv = ds18b20_state->mlt_temperature;
		if (ds18b20_state->hlt_valid)
		    driver_state->hlt_pv = ds18b20_state->hlt_temperature;
		xSemaphoreGive(xSemaphoreDS18B20);
	    }

	    /*
	     * Other values that we need
	     */
	    Input = driver_state->mlt_pv;
	    Setpoint = driver_state->mlt_sp;
	    if (driver_state->mlt_mode != MLT_Mode) {
		if (driver_state->mlt_mode == MLT_MODE_BANG) {
		    PID_SetMode(PID_MANUAL);
		} else if (driver_state->mlt_mode == MLT_MODE_PID) {
		    LoadPIDsettings();
		}
		MLT_Mode = driver_state->mlt_mode;
		ESP_LOGI(TAG, "MLT mode set to %d", MLT_Mode);
	    }
	    if (driver_state->hlt_mode != HLT_Mode) {
		HLT_Mode = driver_state->hlt_mode;
		ESP_LOGI(TAG, "HLT mode set to %d", HLT_Mode);
	    }
	    outEnable = driver_state->enable;
	    HLT_Input = driver_state->hlt_pv;
	    HLT_Setpoint = driver_state->hlt_sp;
	    xSemaphoreGive(xSemaphoreDriver);
	}

	rc = false;
	now = xTaskGetTickCount() * portTICK_PERIOD_MS;

	if ((PID_GetMode() == PID_AUTOMATIC) && (MLT_Mode == MLT_MODE_PID)) {
	    rc = PID_Compute();
	    RealTime = (equipment.SampleTime * equipment.MashPower) / 100;
	} else {
	    /*
	     * Schedule the loop ourself.
	     */
	    unsigned long timeChange = (now - lastTime);
	    if (timeChange >= equipment.SampleTime) {
		lastTime = now;
		rc = true;
	    }
	    RealTime = equipment.SampleTime;
	    if (driver_state->mlt_mode == MLT_MODE_BANG) {
	        Output = (Input < Setpoint) ? 255:0;
	    }
	    if (driver_state->mlt_mode == MLT_MODE_NONE || driver_state->mlt_mode == MLT_MODE_OFF) {
		Output = 0;
	    }
	}

	if (rc) {
	    w_StartTime = now;
	}

	if ((int)((Output / 255.0) * RealTime) > (now - w_StartTime)) {
	    MLT(1);
	    if ((equipment.SSR2 == SSR2_HLT_SHARE) || (equipment.SSR2 == SSR2_ON_IDLE)) {
	    	HLT(0);
		HLT_Output = 0;
	    }
	} else {
	    MLT(0);
	    if (equipment.SSR2 == SSR2_HLT_SHARE) {
	    	if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
		    HLT_Output = 0;
	    	    if (driver_state->hlt_mode == HLT_MODE_BANG) {
		    	HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0;
	    	    } else if (driver_state->hlt_mode == HLT_MODE_ON) {
		    	HLT_Output = 1;
	    	    }
		    xSemaphoreGive(xSemaphoreDriver);
	    	}
	    	HLT(HLT_Output);
	    } else if (equipment.SSR2 == SSR2_ON_IDLE) {
		HLT_Output = 1;
		HLT(1);
	    }
	}

	/*
	 * Independant HLT temperature control
	 */
	if  (equipment.SSR2 == SSR2_HLT_IND) {
	    if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
		HLT_Output = 0;
		if (driver_state->hlt_mode == HLT_MODE_BANG) {
		    HLT_Output = (HLT_Input < HLT_Setpoint) ? 1:0;
		} else if (driver_state->hlt_mode == HLT_MODE_ON) {
		    HLT_Output = 1;
		}
		xSemaphoreGive(xSemaphoreDriver);
	    }
	    HLT(HLT_Output);
	}

	/*
	 * Update the driver results.
	 */
	if (xSemaphoreTake(xSemaphoreDriver, 10) == pdTRUE) {
	    driver_state->mlt_power = (int)((Output * 100) / 255.0);
	    if (HLT_Output) {
		if (equipment.SSR2 == SSR2_HLT_SHARE) {
	    	    driver_state->hlt_power = 100 - driver_state->mlt_power;
		} else if  (equipment.SSR2 == SSR2_HLT_IND) {
		    driver_state->hlt_power = 100;
		}
	    } else {
	    	driver_state->hlt_power = 0;
	    }
	    if (driver_state->pump_run != Pump_pin)
		Pump(driver_state->pump_run);
	    xSemaphoreGive(xSemaphoreDriver);
	}

#if 0
	if (rc) {
	    printf("ST: %s MLT[In: %7.3f Out: %3.0f Sp: %6.2f %s RT: %lu]  HLT[In: %7.3f Out: %d Sp: %5.1f]\n", outEnable ? "E":"D",
		    Input, Output, Setpoint, PID_GetMode() ? "AUTOMATIC" : "MANUAL   ", RealTime,
		    HLT_Input, HLT_Output, HLT_Setpoint);
	}
#endif

	// Not reliable, so do it manually.
	//vTaskDelayUntil(&last_wake_time, (1000 / 50) / portTICK_PERIOD_MS);
	now_tick = xTaskGetTickCount();
	if ((now_tick - last_tick) > (1000 / 50)) {
	    // This happens one or two times during a brew.
	    wait_ticks = (1000 / 50);
	} else {
	    wait_ticks = (1000 / 50) - (now_tick - last_tick);
	}
	if (wait_ticks == 0) {
	    // This is rare, but it happens.
	    wait_ticks = 1;
	}

	vTaskDelay(wait_ticks);
    }
}

mercurial