Cheese cave control

Greetings friends, I have been goofing around with arduino now for a few years, temp monitors, blinking lights and a few other minor projects. I have a new project that exceeds my capability thus far. I want to control the temp and humidity in a used refrigerator. I have put together a few things:

1- Boarduino 1- DHT 11 temp/humidity sensor 1- 16x2 LCD display 2- 25A Solid State Relays 2- Radio Shack 271-1716 50k potentiometers 1- ultrasonic humidifier.

The idea is to use the potentiometers to set the desired temp/humidity, and the relays will cycle the fridge and humidifier on/off. pretty simple but I am a little lost. I can start with reading the temp/humidity from the dht 11 script, so far so good, then i get lost as to what next to do.

Thanks,

Matt

Step by step approach :) 1. try to print hum/temp and pot. value on the LCD.

Right, I am using the sketch by jherland to print to the lcd. It works well. Two seconds may be to frequent as I don’t want to kill the compressor on the fridge.

/*
 * Measure humidity and temperature with a DHT11 every 2 seconds,
 * and output the readings on the serial port, and a 16x2 LCD panel.
 * Also store per-minute averages to EEPROM and dump them on restart.
 *
 * Originally based on the DHT11 example code from http://www.dfrobot.com/wiki/index.php?title=DHT11_Temperature_and_Humidity_Sensor_(SKU:_DFR0067) (unknown license)
 * and the LCD 16x2 tutorial/example code from http://www.arduino.cc/en/Tutorial/LiquidCrystal (Public Domain)
 *
 * Johan Herland <johan@herland.net>
 */

#include <LiquidCrystal.h>
#include <EEPROM.h>

// Initialize LCD library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// DHT11 signal pin is connected to this analog port
const unsigned int DHT11_PIN = 0;

// EEPROM size (1024 bytes on ATmega328)
const unsigned int EEPROM_SIZE = 1024;

// Current datagram from DHT11 (2B humidity, 2B temperature, 1B checksum)
byte dht11_dat[5];

int cur_minute;
int humd_sum;
int temp_sum;
int nsamples;
float humd_avg;
float temp_avg;
unsigned int eeprom_addr;


void setup()
{
	// Set DHT11 port as output port with initial value '1'
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);

	// Initialize serial port
	Serial.begin(9600);
	Serial.println("Ready");

	// Set up 16x2 LCD panel
	lcd.begin(16, 2);

	// Dump EEPROM to serial port
	lcd.setCursor(0, 0);
	lcd.print("Dumping EEPROM  ");
	lcd.setCursor(0, 1);
	lcd.print("to serial port..");
	Serial.println("EEPROM dump start");
	int i, j;
	byte b = 0;
	for (i = 0; i < EEPROM_SIZE; i += 0x10) {
		Serial.print(i, HEX);
		Serial.print(":");
		for (j = 0; j < 0x10; j++) {
			if (j % 2 == 0)
				Serial.print(" ");
			else
				Serial.print("/");
			b = EEPROM.read(i + j);
			if (b == 0xff)
				break;
			Serial.print((float)b / 4.0);
		}
		Serial.println();
		lcd.setCursor(15, 1);
		if (i % 0x80 >= 0x40)
			lcd.print(".");
		else
			lcd.print(" ");
		if (b == 0xff)
			break;
	}
	Serial.println("EEPROM dump end");

	cur_minute = millis() / 60000;
	humd_sum = 0;
	temp_sum = 0;
	nsamples = 0;
	humd_avg = 0;
	temp_avg = 0;
	eeprom_addr = 0;
}


byte read_dht11_dat()
{
	byte i = 0;
	byte result = 0;
	for (i = 0; i < 8; i++) {
		// wait forever until analog input port 0 is '1'
		// (NOTICE: PINC reads all the analog input ports
		// and _BV(X) is the macro operation which pull up
		// positon 'X' to '1' and the rest positions to '0'.
		// It is equivalent to 1 << X.)
		while(!(PINC & _BV(DHT11_PIN)));
		// if analog input port 0 is still '1' after 30 us
		// this position is 1.
		delayMicroseconds(30);
		if(PINC & _BV(DHT11_PIN))
			result |= (1 << (7 - i));
		// wait '1' finish
		while((PINC & _BV(DHT11_PIN)));
	}
	return result;
}


boolean acquire_dht11_sample()
{
	byte dht11_in;
	byte i; // start condition

	PORTC &= ~_BV(DHT11_PIN); // 1. pull-down i/o pin for 18ms
	delay(18);
	PORTC |= _BV(DHT11_PIN);  // 2. pull-up i/o pin for 40ms
	delayMicroseconds(1);
	DDRC &= ~_BV(DHT11_PIN);  // let analog port 0 be input port
	delayMicroseconds(40);

	dht11_in = PINC & _BV(DHT11_PIN); // read only the input port 0
	if (dht11_in) {
		// wait for DHT response signal: LOW
		Serial.println("dht11 start condition 1 not met");
		delay(1000);
		return false;
	}
	delayMicroseconds(80);
	dht11_in = PINC & _BV(DHT11_PIN);
	if (!dht11_in) {
		// wait for second response signal: HIGH
		Serial.println("dht11 start condition 2 not met");
		return false;
	}

	delayMicroseconds(80); // now ready for data reception
	// receive 40 bits data. Details are described in datasheet
	for (i = 0; i < 5; i++)
		dht11_dat[i] = read_dht11_dat();

	// set DHT11 port to output value '1', after data is received
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);

	// verify checksum
	byte dht11_check_sum =
		dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
	if (dht11_dat[4] != dht11_check_sum)
		Serial.println("DHT11 checksum error");

	return true;
}


boolean update_avg(int minutes)
{
	nsamples += 1;
	humd_sum += dht11_dat[0];
	temp_sum += dht11_dat[2];

	if (minutes == cur_minute)
		return false;

	humd_avg = (float) humd_sum / (float) nsamples;
	temp_avg = (float) temp_sum / (float) nsamples;
	cur_minute = minutes;
	nsamples = 0;
	humd_sum = 0;
	temp_sum = 0;
	return true;
}


void serial_output(unsigned long runtime, boolean updated)
{
	// report humidity and temperature on serial port
	Serial.print("Current runtime/humidity/temperature: ");
	Serial.print(runtime);
	Serial.print(" ms, ");
	Serial.print(dht11_dat[0], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[1], DEC);
	Serial.print(" %, ");
	Serial.print(dht11_dat[2], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[3], DEC);
	Serial.println(" C");
	if (updated) {
		Serial.print("Average humidity/temperature last minute: ");
		Serial.print(humd_avg);
		Serial.print("%, ");
		Serial.print(temp_avg);
		Serial.println("C");
	}
}


void lcd_output(int minutes, int seconds)
{
	byte humidity = dht11_dat[0];
	byte temperature = dht11_dat[2];

	// print humidity on lcd
	lcd.setCursor(0, 0);
	lcd.print("Humd.: ");
	if (humidity < 10)
		lcd.print(" ");
	lcd.print(humidity);
	lcd.print("%");

	// print average humidity last minute
	lcd.print(" ");
	lcd.print(humd_avg);
	lcd.print("   ");
/*
	// print runtime on lcd
	if (minutes < 100) {
		lcd.print(" ");
		if (minutes < 10)
			lcd.print(0);
	}
	lcd.print(minutes);
	lcd.print(":");
	if (seconds < 10)
		lcd.print(0);
	lcd.print(seconds);
*/

	// print temperature on lcd
	lcd.setCursor(0, 1);
	lcd.print("Temp.: ");
	if (temperature < 10)
		lcd.print(" ");
	lcd.print(temperature);
	lcd.print("C");

	// print average temperature last minute
	lcd.print(" ");
	lcd.print(temp_avg);
	lcd.print("   ");
}


void log_to_eeprom()
{
	byte humd = humd_avg * 4;
	byte temp = temp_avg * 4;
	EEPROM.write(eeprom_addr++, humd);
	EEPROM.write(eeprom_addr++, temp);
	eeprom_addr %= EEPROM_SIZE;
}


void loop()
{
	if (!acquire_dht11_sample())
		return;

	unsigned long runtime = millis();
	unsigned long minutes = runtime / 60000;
	unsigned long seconds = (runtime / 1000) % 60;

	boolean updated = update_avg(minutes);

	serial_output(runtime, updated);

	lcd_output(minutes, seconds);

	if (updated)
		log_to_eeprom();

	// wait 2 seconds until next reading
	delay(2000);
}

Step2: you have to calibrate the humidity and temperature sensors so you know what is going on in the cave Step3: you have to find out the regions of H/T you want to operate in Step4: you have to find out how your cave reacts to switching on/off (how fast the temp/hum. changes) Step5: you have to decide on the control type - simple on/off, PID, fuzzy, etc. Step6. you have to implement the control algorithm.. Step7. you have to test and fine tune the control algorithm untill your cheese will be happy :)

Step2: you have to calibrate the humidity and temperature sensors so you know what is going on in the cave

By calibrate do you mean just comparing the DHT 11 to a separate sensor?

Step3: you have to find out the regions of H/T you want to operate in

Temp range: 40deg F to 80deg F Humidity: 50% to 100%

Step4: you have to find out how your cave reacts to switching on/off (how fast the temp/hum. changes)

Going to need some experimentation! Fun

Step5: you have to decide on the control type - simple on/off, PID, fuzzy, etc.

Simple would probably suffice, I have PID on the vat and it works like a charm.

Step6. you have to implement the control algorithm.. Step7. you have to test and fine tune the control algorithm untill your cheese will be happy smiley

Like cheese only time will tell.

so to add to the sketch I have working;

int tempPot = analogRead(A1); int humidPot = analogRead(A2);

This will read the values from 0- 1023. I then need to interpolate that value (0-1023) to the temp(40-80)/humidity(50-1000 range?

Step by Step

By calibrate do you mean just comparing the DHT 11 to a separate sensor?

By calibrating I mean the temperature and humidity you display on the LCD is plus/minus accurate (so you get real degF and % values on the LCD). I do not know the sensor - maybe it is accurate enough for your app within the range of interest. When not, you have to provide some kind of calibration.. Btw, fuzzy logic is quite popular with fridges :)

This will read the values from 0- 1023. I then need to interpolate that value (0-1023) to the temp(40-80)/humidity(50-1000 range?

You may use the two potentiometers for entering the H and T into the system in that way (to-be values). So I would show as-is values for T/H (from the sensor, calibrated) and to-be values (from the pots) for T/H on the LCD display and that would be a good starting point (Step2a) :).

PS: I see in the datasheet the DTH11 has few% accuracy in 0-50degC - probably no calibration required in that temp range :)

A common issue in simple control systems of this type is that when the reading is around the set point, the system will be turning on and off a lot, which is bad for your mechanical components. It's usually solved by having some tolerance so if you're looking for 60% humidity you keep the humidifier on on until you get to 62%. As humidity falls, don't turn it back on until it gets to 58%. q.v. Hysteresis.

The hysteresis principle could be used for the setting of the to-be input values from the potentiometers (as the ADC values will probably jump plus/minus 1-3 digits). Also you may consider to connect 100nF..1uF capacitors from analog inputs to ground - that will help with pots readings as well.

mdmoore00:

Step2: you have to calibrate the humidity and temperature sensors so you know what is going on in the cave

By calibrate do you mean just comparing the DHT 11 to a separate sensor?

Where the separate sensors are an accurate thermometer and an accurate humidity meter (hydrometer?), yes.

Well I have to say I’m completely surprised, but I modified the sketch to display the pot values on the LCD. I did some editing of the sketch that is probably not the correct way but it worked. I think removed the part that stores to the EEPROM. I now need to read the pots and convert that value to the temperature/humidity range I want then display that value on the lcd. here is the complete sketch:

/*

 * Measure humidity and temperature with a DHT11 every 2 seconds,
 * references two potentiometers for set-point values to control 
 * two SSR that will drive a refrigerator and a humidifier, 
 * and output the readings on the serial port, and a 16x2 LCD panel.
 * 
 *
 * Originally based on the DHT11 example code from http://www.dfrobot.com/wiki/index.php?title=DHT11_Temperature_and_Humidity_Sensor_(SKU:_DFR0067) (unknown license)
 * and the LCD 16x2 tutorial/example code from http://www.arduino.cc/en/Tutorial/LiquidCrystal (Public Domain)
 * Furthere based on Johan Herland <johan@herland.net> DHT 11, LCD code
 */

#include <LiquidCrystal.h>


// Initialize LCD library with the numbers of the interface pins
LiquidCrystal lcd(11, NULL, 12, 7, 8, 9, 10);

// DHT11 signal pin is connected to this analog port
const unsigned int DHT11_PIN = 0;

// EEPROM size (1024 bytes on ATmega328)
const unsigned int EEPROM_SIZE = 1024;

// Current datagram from DHT11 (2B humidity, 2B temperature, 1B checksum)
byte dht11_dat[5];

int cur_minute;
int humd_sum;
int temp_sum;
int nsamples;
int humd_set = A1;
int temp_set;
unsigned int eeprom_addr;


void setup()
{
	// Set DHT11 port as output port with initial value '1'
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);

	// Initialize serial port
	Serial.begin(9600);
	Serial.println("Ready");

	// Set up 16x2 LCD panel
	lcd.begin(16, 2);

	
	cur_minute = millis() / 60000;
	humd_sum = 0;
	temp_sum = 0;
	nsamples = 0;
	humd_set = 0;
	temp_set = 0;

}


byte read_dht11_dat()
{
	byte i = 0;
	byte result = 0;
	for (i = 0; i < 8; i++) {
		// wait forever until analog input port 0 is '1'
		// (NOTICE: PINC reads all the analog input ports
		// and _BV(X) is the macro operation which pull up
		// positon 'X' to '1' and the rest positions to '0'.
		// It is equivalent to 1 << X.)
		while(!(PINC & _BV(DHT11_PIN)));
		// if analog input port 0 is still '1' after 30 us
		// this position is 1.
		delayMicroseconds(30);
		if(PINC & _BV(DHT11_PIN))
			result |= (1 << (7 - i));
		// wait '1' finish
		while((PINC & _BV(DHT11_PIN)));
	}
	return result;
}


boolean acquire_dht11_sample()
{
	byte dht11_in;
	byte i; // start condition

	PORTC &= ~_BV(DHT11_PIN); // 1. pull-down i/o pin for 18ms
	delay(18);
	PORTC |= _BV(DHT11_PIN);  // 2. pull-up i/o pin for 40ms
	delayMicroseconds(1);
	DDRC &= ~_BV(DHT11_PIN);  // let analog port 0 be input port
	delayMicroseconds(40);

	dht11_in = PINC & _BV(DHT11_PIN); // read only the input port 0
	if (dht11_in) {
		// wait for DHT response signal: LOW
		Serial.println("dht11 start condition 1 not met");
		delay(1000);
		return false;
	}
	delayMicroseconds(80);
	dht11_in = PINC & _BV(DHT11_PIN);
	if (!dht11_in) {
		// wait for second response signal: HIGH
		Serial.println("dht11 start condition 2 not met");
		return false;
	}

	delayMicroseconds(80); // now ready for data reception
	// receive 40 bits data. Details are described in datasheet
	for (i = 0; i < 5; i++)
		dht11_dat[i] = read_dht11_dat();

	// set DHT11 port to output value '1', after data is received
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);

	// verify checksum
	byte dht11_check_sum =
		dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
	if (dht11_dat[4] != dht11_check_sum)
		Serial.println("DHT11 checksum error");

	return true;
}


boolean update_avg(int minutes)
{
	nsamples += 1;
	humd_sum += dht11_dat[0];
	temp_sum += dht11_dat[2];

	if (minutes == cur_minute)
		return false;
	
	cur_minute = minutes;
	nsamples = 0;
	humd_sum = 0;
	temp_sum = 0;
	return true;
}


void serial_output(unsigned long runtime, boolean updated)
{
	// report humidity and temperature on serial port
	Serial.print("Current runtime/humidity/temperature: ");
	Serial.print(runtime);
	Serial.print(" ms, ");
	Serial.print(dht11_dat[0], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[1], DEC);
	Serial.print(" %, ");
	Serial.print(dht11_dat[2], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[3], DEC);
	Serial.println(" C");
	if (updated) {
		Serial.print("Average humidity/temperature last minute: ");

		Serial.print("%, ");

		Serial.println("C");
	}
}


void lcd_output(int minutes, int seconds)
{
	byte humidity = dht11_dat[0];
	byte temperature = dht11_dat[2];

	// print humidity on lcd
	lcd.setCursor(0, 0);
	lcd.print("Humd.: ");
	if (humidity < 10)
		lcd.print(" ");
	lcd.print(humidity);
	lcd.print("%");

	// print Set Point humidity 
	lcd.print(" ");
	lcd.print(humd_set);
	lcd.print("   ");
/*
	// print runtime on lcd
	if (minutes < 100) {
		lcd.print(" ");
		if (minutes < 10)
			lcd.print(0);
	}
	lcd.print(minutes);
	lcd.print(":");
	if (seconds < 10)
		lcd.print(0);
	lcd.print(seconds);
*/

	// print temperature on lcd
	lcd.setCursor(0, 1);
	lcd.print("Temp.: ");
	if (temperature < 10)
		lcd.print(" ");
	lcd.print(temperature);
	lcd.print("C");

	// print Set Point temperature
	lcd.print(" ");
	lcd.print(temp_set);
	lcd.print("   ");
}

void loop()
{
  
  humd_set = analogRead(A1);
  temp_set = analogRead(A2);
  
	if (!acquire_dht11_sample())
		return;

	unsigned long runtime = millis();
	unsigned long minutes = runtime / 60000;
	unsigned long seconds = (runtime / 1000) % 60;

	boolean updated = update_avg(minutes);

	serial_output(runtime, updated);

	lcd_output(minutes, seconds);

	if (updated)
		

	// wait 2 seconds until next reading
	delay(2000);
}

I now need to read the pots and convert that value to the temperature/humidity range I want then display that value on the lcd. here is the complete sketch:

There is the "map" function (see http://arduino.cc/en/Reference/Map ) :

  humd_set = analogRead(A1);
  temp_set = analogRead(A2);

  h_tobe = map(humd_set, 0, 1023, 50, 100);  //  convert to 50-100% humidity
  t_tobe = map(temp_set, 0, 1023, 40, 80);   //  convert to 40-80degF

Oh my George Takei, i got it to print the set point to the LCD! I couldn’t have imagined i would be able to get this far… Thanks so much so far. Now i have an issue. The sketch is so jumbled with extraneous stuff it wont refresh. it only takes the first reading and prints that. I should have started this from a more basic sketch, what can I cut out and maintain my desired functionality?

/*
 * Measure humidity and temperature with a DHT11 every 2 seconds,
 * references two potentiometers for set-point values to control 
 * two SSR that will drive a refrigerator and a humidifier, 
 * and output the readings on the serial port, and a 16x2 LCD panel.
 * 
 *
 * Originally based on the DHT11 example code from http://www.dfrobot.com/wiki/index.php?title=DHT11_Temperature_and_Humidity_Sensor_(SKU:_DFR0067) (unknown license)
 * and the LCD 16x2 tutorial/example code from http://www.arduino.cc/en/Tutorial/LiquidCrystal (Public Domain)
 * Furthere based on Johan Herland <johan@herland.net> DHT 11, LCD code
 */

#include <LiquidCrystal.h>


// Initialize LCD library with the numbers of the interface pins
LiquidCrystal lcd(11, NULL, 12, 7, 8, 9, 10);

// DHT11 signal pin is connected to this analog port
const unsigned int DHT11_PIN = 0;

// Current datagram from DHT11 (2B humidity, 2B temperature, 1B checksum)
byte dht11_dat[5];

int cur_minute;
int humd_sum;
int temp_sum;
int nsamples;
int humd_set = A1;
int temp_set = A2;
int h_tobe = map(humd_set, 0, 1023, 50, 100);  //  convert to 50-100% humidity
int t_tobe = map(temp_set, 0, 1023, 40, 80);   //  convert to 40-80degF
  
void setup()
{
	// Set DHT11 port as output port with initial value '1'
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);

	// Initialize serial port
	Serial.begin(9600);
	Serial.println("Ready");

	// Set up 16x2 LCD panel
	lcd.begin(16, 2);

	
	cur_minute = millis() / 60000;
	humd_sum = 0;
	temp_sum = 0;
	nsamples = 0;
	humd_set = 0;
	temp_set = 0;
        h_tobe   = 0;
        t_tobe   = 0;
}


byte read_dht11_dat()
{
	byte i = 0;
	byte result = 0;
	for (i = 0; i < 8; i++) {
		// wait forever until analog input port 0 is '1'
		// (NOTICE: PINC reads all the analog input ports
		// and _BV(X) is the macro operation which pull up
		// positon 'X' to '1' and the rest positions to '0'.
		// It is equivalent to 1 << X.)
		while(!(PINC & _BV(DHT11_PIN)));
		// if analog input port 0 is still '1' after 30 us
		// this position is 1.
		delayMicroseconds(30);
		if(PINC & _BV(DHT11_PIN))
			result |= (1 << (7 - i));
		// wait '1' finish
		while((PINC & _BV(DHT11_PIN)));
	}
	return result;
}


boolean acquire_dht11_sample()
{
	byte dht11_in;
	byte i; // start condition

	PORTC &= ~_BV(DHT11_PIN); // 1. pull-down i/o pin for 18ms
	delay(18);
	PORTC |= _BV(DHT11_PIN);  // 2. pull-up i/o pin for 40ms
	delayMicroseconds(1);
	DDRC &= ~_BV(DHT11_PIN);  // let analog port 0 be input port
	delayMicroseconds(40);

	dht11_in = PINC & _BV(DHT11_PIN); // read only the input port 0
	if (dht11_in) {
		// wait for DHT response signal: LOW
		Serial.println("dht11 start condition 1 not met");
		delay(1000);
		return false;
	}
	delayMicroseconds(80);
	dht11_in = PINC & _BV(DHT11_PIN);
	if (!dht11_in) {
		// wait for second response signal: HIGH
		Serial.println("dht11 start condition 2 not met");
		return false;
	}

	delayMicroseconds(80); // now ready for data reception
	// receive 40 bits data. Details are described in datasheet
	for (i = 0; i < 5; i++)
		dht11_dat[i] = read_dht11_dat();

	// set DHT11 port to output value '1', after data is received
	DDRC |= _BV(DHT11_PIN);
	PORTC |= _BV(DHT11_PIN);

	// verify checksum
	byte dht11_check_sum =
		dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
	if (dht11_dat[4] != dht11_check_sum)
		Serial.println("DHT11 checksum error");

	return true;
}


boolean update_avg(int minutes)
{
	nsamples += 1;
	humd_sum += dht11_dat[0];
	temp_sum += dht11_dat[2];

	if (minutes == cur_minute)
		return false;
	
	cur_minute = minutes;
	nsamples = 0;
	humd_sum = 0;
	temp_sum = 0;
	return true;
}


void serial_output(unsigned long runtime, boolean updated)
{
	// report humidity and temperature on serial port
	Serial.print("Current runtime/humidity/temperature: ");
	Serial.print(runtime);
	Serial.print(" ms, ");
	Serial.print(dht11_dat[0], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[1], DEC);
	Serial.print(" %, ");
	Serial.print(dht11_dat[2], DEC);
	Serial.print(".");
	Serial.print(dht11_dat[3], DEC);
	Serial.println(" C");
        Serial.print(analogRead (A1));
        Serial.print(" ");
         Serial.print(analogRead (A2));
        Serial.print(" ");
	if (updated) {
		Serial.print("Average humidity/temperature last minute: ");

		Serial.print("%, ");

		Serial.println("C");
	}
}


void lcd_output(int minutes, int seconds)
{
	byte humidity = dht11_dat[0];
	byte temperature = dht11_dat[2];

	// print humidity on lcd
	lcd.setCursor(0, 0);
	lcd.print("Humd.: ");
	if (humidity < 10)
		lcd.print(" ");
	lcd.print(humidity);
	lcd.print("%");

	// print Set Point humidity 
	lcd.print(" ");
	lcd.print(h_tobe);
	lcd.print(" % ");
/*
	// print runtime on lcd
	if (minutes < 100) {
		lcd.print(" ");
		if (minutes < 10)
			lcd.print(0);
	}
	lcd.print(minutes);
	lcd.print(":");
	if (seconds < 10)
		lcd.print(0);
	lcd.print(seconds);
*/

	// print temperature on lcd
	lcd.setCursor(0, 1);
	lcd.print("Temp.: ");
	if (temperature < 10)
		lcd.print(" ");
	lcd.print(temperature);
	lcd.print("C");

	// print Set Point temperature
	lcd.print(" ");
	lcd.print(t_tobe);
	lcd.print(" C ");
}

void loop()
{
  
  humd_set = analogRead(A1);
  temp_set = analogRead(A2);
  
  h_tobe = map(humd_set, 0, 1023, 50, 100);  //  convert to 50-100% humidity
  t_tobe = map(temp_set, 0, 1023, 40, 80);   //  convert to 40-80degF
  
  	if (!acquire_dht11_sample())
		return;

	unsigned long runtime = millis();
	unsigned long minutes = runtime / 60000;
	unsigned long seconds = (runtime / 1000) % 60;

	boolean updated = update_avg(minutes);

	serial_output(runtime, updated);

	lcd_output(minutes, seconds);

	if (updated)
		

	// wait 2 seconds until next reading
	delay(2000);
}

Step by step…
I would fine tune a simple loop, which prints as-is and to-be temp and humidity on the LCD in a simple loop. I would not mess with the time stuff at this stage…
Something like this:

void loop()
{
  
  humd_set = analogRead(A1);
  temp_set = analogRead(A2);
  
  h_tobe = map(humd_set, 0, 1023, 50, 100);  //  convert to 50-100% humidity
  t_tobe = map(temp_set, 0, 1023, 40, 80);   //  convert to 40-80degF
  
  acquire_dht11_sample();   // gives asis values of temp and humd

 // serial_output(runtime, updated);

  lcd_output();   
  //   line1:    ACT T=48F H=82%  
  //   line2:    SET T=55F H=75%

  // here are the functions for actual control of the cave (simple on/off with hysteresis)
  // temp_control(asis, tobe);   // if asis > tobe then switch the fridge on else off
  // humd_control(asis, tobe);   // if asis < tobe then switch the humidifier on else off

  // wait 2 seconds until next reading
  delay(2000);
}

I'd assume that this is your immediate problem:

   if (!acquire_dht11_sample())
        return;

Presumably that's stopping your LCD from updating. I'd prove it with some serial.Prints and then likely some more in acquire_dht11_sample to see why.

That seems to be the prob… when I serial print i get “dht11 start condition 1 not met”, and that means there is something wrong here???

boolean acquire_dht11_sample()
{
  byte dht11_in;
  byte i; // start condition

  PORTC &= ~_BV(DHT11_PIN); // 1. pull-down i/o pin for 18ms
  delay(18);
  PORTC |= _BV(DHT11_PIN);  // 2. pull-up i/o pin for 40ms
  delayMicroseconds(1);
  DDRC &= ~_BV(DHT11_PIN);  // let analog port 0 be input port
  delayMicroseconds(40);

  dht11_in = PINC & _BV(DHT11_PIN); // read only the input port 0
  if (dht11_in) {
    // wait for DHT response signal: LOW
    Serial.println("dht11 start condition 1 not met");
    delay(1000);
    return false;
  }
  delayMicroseconds(80);
  dht11_in = PINC & _BV(DHT11_PIN);
  if (!dht11_in) {
    // wait for second response signal: HIGH
    Serial.println("dht11 start condition 2 not met");
    return false;
  }

  delayMicroseconds(80); // now ready for data reception
  // receive 40 bits data. Details are described in datasheet
  for (i = 0; i < 5; i++)
    dht11_dat[i] = read_dht11_dat();

  // set DHT11 port to output value '1', after data is received
  DDRC |= _BV(DHT11_PIN);
  PORTC |= _BV(DHT11_PIN);

  // verify checksum
  byte dht11_check_sum =
    dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
  if (dht11_dat[4] != dht11_check_sum)
    Serial.println("DHT11 checksum error");

  return true;
}

WOW! Everything is working now. I even have a if else statement comparing the actual t/h to the set points and depending on the result it sends a digital high/low to the SSR (for the time being a LED). The only last thing is to work on the high/low for the fridge. Pito, you said fuzzy logic is commonly used in this case. I’m not very familiar with this application, do you have an example?

/*
 * Measure humidity and temperature with a DHT11 every 2 seconds,
 * references two potentiometers for set-point values to control 
 * two SSR that will drive a refrigerator and a humidifier, 
 * and output the readings on the serial port, and a 16x2 LCD panel.
 * 
 *
 * Originally based on the DHT11 example code from http://www.dfrobot.com/wiki/index.php?title=DHT11_Temperature_and_Humidity_Sensor_(SKU:_DFR0067) (unknown license)
 * and the LCD 16x2 tutorial/example code from http://www.arduino.cc/en/Tutorial/LiquidCrystal (Public Domain)
 * Furthere based on Johan Herland <johan@herland.net> DHT 11, LCD code
 */

#include <LiquidCrystal.h>


// Initialize LCD library with the numbers of the interface pins
LiquidCrystal lcd(11, NULL, 12, 7, 8, 9, 10);

// DHT11 signal pin is connected to this analog port
const unsigned int DHT11_PIN = 0;

// Current datagram from DHT11 (2B humidity, 2B temperature, 1B checksum)
byte dht11_dat[5];

int cur_minute;
int humd_sum;
int temp_sum;
int nsamples;
int humd_set = A1;
int temp_set = A2;
int h_tobe = map(humd_set, 0, 1023, 50, 100);  //  convert to 50-100% humidity
int t_tobe = map(temp_set, 0, 1023, 40, 80);   //  convert to 40-80degF
int HumSSR = 2;
int TemSSR = 3;
int humidity = dht11_dat[0];
int temperature = dht11_dat[2];


void setup()
{
  // Set DHT11 port as output port with initial value '1'
  DDRC |= _BV(DHT11_PIN);
  PORTC |= _BV(DHT11_PIN);

  // Initialize serial port
  Serial.begin(9600);
  Serial.println("Ready");

  // Set up 16x2 LCD panel
  lcd.begin(16, 2);


  cur_minute = millis() / 60000;
  humd_sum = 0;
  temp_sum = 0;
  nsamples = 0;
  humd_set = 0;
  temp_set = 0;
  h_tobe   = 0;
  t_tobe   = 0;
}


byte read_dht11_dat()
{
  byte i = 0;
  byte result = 0;
  for (i = 0; i < 8; i++) {
    // wait forever until analog input port 0 is '1'
    // (NOTICE: PINC reads all the analog input ports
    // and _BV(X) is the macro operation which pull up
    // positon 'X' to '1' and the rest positions to '0'.
    // It is equivalent to 1 << X.)
    while(!(PINC & _BV(DHT11_PIN)));
    // if analog input port 0 is still '1' after 30 us
    // this position is 1.
    delayMicroseconds(30);
    if(PINC & _BV(DHT11_PIN))
      result |= (1 << (7 - i));
    // wait '1' finish
    while((PINC & _BV(DHT11_PIN)));
  }
  return result;
}


boolean acquire_dht11_sample()
{
  byte dht11_in;
  byte i; // start condition

  PORTC &= ~_BV(DHT11_PIN); // 1. pull-down i/o pin for 18ms
  delay(18);
  PORTC |= _BV(DHT11_PIN);  // 2. pull-up i/o pin for 40ms
  delayMicroseconds(1);
  DDRC &= ~_BV(DHT11_PIN);  // let analog port 0 be input port
  delayMicroseconds(40);

  dht11_in = PINC & _BV(DHT11_PIN); // read only the input port 0
  if (dht11_in) {
    // wait for DHT response signal: LOW
    Serial.println("dht11 start condition 1 not met");
    delay(1000);
    return false;
  }
  delayMicroseconds(80);
  dht11_in = PINC & _BV(DHT11_PIN);
  if (!dht11_in) {
    // wait for second response signal: HIGH
    Serial.println("dht11 start condition 2 not met");
    return false;
  }

  delayMicroseconds(80); // now ready for data reception
  // receive 40 bits data. Details are described in datasheet
  for (i = 0; i < 5; i++)
    dht11_dat[i] = read_dht11_dat();

  // set DHT11 port to output value '1', after data is received
  DDRC |= _BV(DHT11_PIN);
  PORTC |= _BV(DHT11_PIN);

  // verify checksum
  byte dht11_check_sum =
    dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
  if (dht11_dat[4] != dht11_check_sum)
    Serial.println("DHT11 checksum error");

  return true;
}


boolean update_avg(int minutes)
{
  nsamples += 1;
  humd_sum += dht11_dat[0];
  temp_sum += dht11_dat[2];

  if (minutes == cur_minute)
    return false;

  cur_minute = minutes;
  nsamples = 0;
  humd_sum = 0;
  temp_sum = 0;
  return true;
}


void serial_output(unsigned long runtime, boolean updated)
{
  // report humidity and temperature on serial port
  Serial.print("Current runtime/humidity/temperature: ");
  Serial.print(runtime);
  Serial.print(" ms, ");
  Serial.print(dht11_dat[0], DEC);
  Serial.print(".");
  Serial.print(dht11_dat[1], DEC);
  Serial.print(" %, ");
  Serial.print(dht11_dat[2], DEC);
  Serial.print(".");
  Serial.print(dht11_dat[3], DEC);
  Serial.println(" C");
  Serial.print(analogRead (A1));
  Serial.print(" ");
  Serial.print(analogRead (A2));
  Serial.print(" ");
  if (updated) {
    Serial.print("Average humidity/temperature last minute: ");

    Serial.print("%, ");

    Serial.println("C");
  }
}


void lcd_output(int minutes, int seconds)
{
  byte humidity = dht11_dat[0];
  byte temperature = dht11_dat[2];

  // print humidity on lcd
  lcd.setCursor(0, 0);
  lcd.print("Humd:");
  if (humidity < 10)
    lcd.print(" ");
  lcd.print(humidity);
  lcd.print("% SET:");

  // print Set Point humidity 
  lcd.print(h_tobe);
  lcd.print("%");
  /*
	// print runtime on lcd
   	if (minutes < 100) {
   		lcd.print(" ");
   		if (minutes < 10)
   			lcd.print(0);
   	}
   	lcd.print(minutes);
   	lcd.print(":");
   	if (seconds < 10)
   		lcd.print(0);
   	lcd.print(seconds);
   */

  // print temperature on lcd
  lcd.setCursor(0, 1);
  lcd.print("Temp:");
  if (temperature < 10)
    lcd.print(" ");
  lcd.print(temperature);
  lcd.print("C SET:");

  // print Set Point temperature
  lcd.print(t_tobe);
  lcd.print("C");
}

void loop()
{

  humd_set = analogRead(A1);
  temp_set = analogRead(A2);

  h_tobe = map(humd_set, 0, 1023, 0, 100);  //  convert to 0-100% humidity
  t_tobe = map(temp_set, 0, 1023, 0, 80);   //  convert to 0-80degF

  acquire_dht11_sample();
    

  unsigned long runtime = millis();
  unsigned long minutes = runtime / 60000;
  unsigned long seconds = (runtime / 1000) % 60;

  boolean updated = update_avg(minutes);

  serial_output(runtime, updated);

  lcd_output(minutes, seconds);

  if (dht11_dat[0] < h_tobe)
  {
      digitalWrite(HumSSR, HIGH); 
  }
  else
  {
      digitalWrite(HumSSR, LOW); 
  }

  if (dht11_dat[2] > t_tobe) 
  {
      digitalWrite(TemSSR, HIGH); 
  }
  else
  {
      digitalWrite(TemSSR, LOW); 
  }
    if (updated)


      // wait 2 seconds until next reading
      delay(2000);

}

The only last thing is to work on the high/low for the fridge.

You can use exactly the same method as you did for the humidity, at least to start with.

.[/quote]

You can use exactly the same method as you did for the humidity, at least to start with. [/quote]

I have the fridge on a simple on/off scheme. I need to ensure that the switching doesn't harm the fridge. Can I just have the fridge over cool the set point by a few degrees and turn on a few degrees above the set point?

Pito, you said fuzzy logic is commonly used in this case. I’m not very familiar with this application, do you have an example?

I am afraid I do not. There are papers on the web available…

You do not have the hysteresis built in. So near a setpoint it could switch on/off erratically.

Let say your hysteresis at temperature will be 4 degF.

So you want to:

  1. switch the fridge on when t_asis is 2degF higher than the t_tobe, and
  2. you switch the fridge off when t_asis is 2 deg lower that t_tobe… Or something like that :slight_smile:

I would strongly recommend you to use variables with meaningfull names, ie. instead of “dht11_dat…” do use “t_asis”…

...
  int t_hhyst = 2;    // hyst = 4degF - you need to experiment with proper value
  int h_hhyst = 2;    // hyst = 4% - you need to experiment with proper value

 // *****   humidifier on/off with hysteresis:
 if (h_asis < (h_tobe - h_hhyst)) {
	digitalWrite(HumSSR, HIGH);    // humidifier ON
	//humid_led = ON;
	}
 if (h_asis > (h_tobe + h_hhyst)) {
	digitalWrite(HumSSR, LOW);     // humidifier OFF
	//humid_led = OFF;
	}
 // *****

 // *****   fridge on/off with hysteresis:
 if (t_asis > (t_tobe + t_hhyst)) {
	digitalWrite(TemSSR, HIGH);    // fridge ON
	//fridge_led = ON;
	}
 if (t_asis < (t_tobe - t_hhyst)) {
	digitalWrite(TemSSR, LOW);     // fridge OFF
	//fridge_led = OFF;
	}
 // *****
...

That will switch on/off around the tobe setpoints in a “controlled manner” - a kind of oscillations between the hysteresis margins. The average value will be the tobe.
No warranties of any kind :slight_smile:

hysttemp.jpg

I need to ensure that the switching doesn't harm the fridge.

That is not an easy job. The issues you might to face (that is what we like) :) :

  1. the t_tobe and h_tobe values may jump maybe +/-3 when the adc conversion is not fixed properly (hw or sw-wise). The same with t-asis and h_asis.

  2. when the arduino crashes/hangs or you interrupt/stop the control loop somehow, the fridge and/or the humidifier may stay on or off permanently

  3. the current delay (2000) may switch on/off the fridge and humidifier each 2 seconds worst case (when the fridge and humidifier have enough power to change the physical conditions in the cave fast). Therefore I suggested you on certain Step to investigate how fast those systems react on a change. Based on that you may set the delay accordingly (so to minimize the switching on/off).

  4. the fridge and humidifier may not switch off worst case (when the fridge and humidifier do not have enough power to change the physical conditions in the cave). So you have to check whether the fridge and humidifier have enough power to meet your preset tobe values (within your ranges of interest), with some safety margin..

Good luck..