Heater Remote On/Off

We like to use a Dish Heater from time to time to help keep the temp a nice comfortable level. The problem is, Dish Heaters don't have anything for a thermostat other than a timed on and off which inevitably will either be too warm or too cold during the cold winter nights.

I thought about doing a remote on/off like a WeMo from Belkin but then I would still have the same problem, but I could turn it on and off from bed. A improvement but not the answer I was looking for.

So, I decided to come up with a temperature controller to determine when to turn on and off the Dish Heater. One of the fundamental problems with sampling the air temp from near the Dish Heater is that its either way too cold there on the floor or its way too hot being too close to the element. So I decided to come up with a remote unit to sample the temperature and a host or server module with a relay to control the heater on/off.

This is what I came up with...

Server Module

Here you see all of the component of the server module. I took apart a 6 volt DC transformer and got it down to its circuit board to power the ATMega328P. I used a LM7805 5v voltage regulator to make sure the ATMega is protected and another voltage regulator LM7805 and adjusted it down to 3.3 volts to power the LCD display and the nRFL01 wireless module. I have four buttons. One for the ATMega reset, one for a change select and two for the change up or down. When you click the change select, the on temp will blink for roughly 3 seconds. During that time, you can change the temperature that the ATMega will turn on the relay. Click the chage select again and you can change the Off temperature. It gets the temp from the client module.

Client Module

Here the client has a similar set to the server but instead of the LCD and buttons, you have a temp probe coming off of the top left of the board and it gets its power from the 6v lead acid battery instead of the AC/DC transformer. I made the ATMega go to sleep for a max of 8 seconds at a time using the onboard watch dog timer in order to conserve battery levels. Once it wakes up, it reads the temp probe and sends the info over to the server then shuts back down. Easy enough.

6v Battery Checker

I decided and I wanted to keep an eye on the battery levels of the client module so I made a quickie battery checker. This simple design uses the recharge probes sticking out of the front of the enclosure to power the battery checker and to do its check when you press the button on the front. Total time to read the levels takes about 5 seconds. Once done, it will display on the RGB LED either Green for above 80% charge to Red/Green above 70% and Red below 70%. Then I just have to attach the charge to the probes and wait for it to complete its charging. Easy stuff. I'm hoping to get about a month of service out of the battery between charges but time will tell.

Front of Client Enclosure

Server Code

#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//LCD Variables
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

int OnTemp = 68;
int OffTemp = 72;
bool currentlysettingtemp = false;
int settingtimer = 0;
int settingwhat = -1;
int blinkint = 0;

bool startprinting = false;
String temp = "";
String lasttemp = "";
bool heateron = false;
int pin9 = 9; //Relay Pin

int displaytimer = 0;

void setup(){
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, 0x3c);
display.display();
display.clearDisplay();

Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);

pinMode(pin9, OUTPUT);
digitalWrite(pin9, HIGH);

Mirf.spi = &MirfHardwareSpi;
Mirf.init();
Mirf.setRADDR((byte *)"TempServer1");
Mirf.payload = sizeof(unsigned long);
Mirf.config();

Serial.println("Server Started");
}

void loop(){
displaytimer++;

if (!currentlysettingtemp)
{
if(Mirf.dataReady())
{
getData();
}

if (displaytimer > 1500)
{
displaytimer = 0;
DisplayFun();
}
}
else
{
DisplayFun();
}

ButtonFun();
}

void getData()
{
byte data[Mirf.payload];
Mirf.getData(data);
char c = data[0];

if (c == '~')
{
if (startprinting)
{
Serial.print("Current Temp: ");
lasttemp = temp;
Serial.println(temp);

char buf[temp.length()];
temp.toCharArray(buf, temp.length()+1);
double t = atof(buf);
if (t >= OffTemp)
{
if (heateron)
{
//Turn off Heater
digitalWrite(pin9, HIGH);

heateron = false;
Serial.print("Heater Off: ");
Serial.println(t);
}
}
else if (t <= OnTemp && t > 20)
{
if (!heateron)
{
//Turn on Heater
digitalWrite(pin9, LOW);

heateron = true;
Serial.print("Heater On: ");
Serial.println(t);
}
}

temp = "";
}
startprinting = true;
}
else
{
if (startprinting)
{
temp += c;
}
}
}

void ButtonFun()
{
int setting = digitalRead(2);
int tempdown = digitalRead(4);
int tempup = digitalRead(3);

//Timeout settings
if (currentlysettingtemp)
{
settingtimer++;
if (settingtimer > 50)
{
currentlysettingtemp = false;
settingwhat = -1;
}
}

//Button 1 pressed - Settings
if (setting == 0)
{
currentlysettingtemp = true;
settingtimer = 0;
if (settingwhat == -1)
{
settingwhat = 0; //Setting TempDown
}
else if (settingwhat == 0)
{
settingwhat = 1; //Setting TempUp
}
else if (settingwhat == 1)
{
settingwhat = 0; //Setting TempDown
}
delay(30);
}

//Button 2 pressed - TempDown
if (tempdown == 0)
{
settingtimer = 0;
if (settingwhat == 0)
{
OnTemp--;
delay(30);
}
else if (settingwhat == 1)
{
OffTemp--;
delay(30);
}
}

//Button 3 pressed - TempUp
if (tempup == 0)
{
settingtimer = 0;
if (settingwhat == 0)
{
OnTemp++;
delay(30);
}
else if (settingwhat == 1)
{
OffTemp++;
delay(30);
}
}
}

void DisplayFun()
{
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);

if (lasttemp != "")
{
display.print(lasttemp);
display.println("F");
}
else
{
display.println("Waiting..");
}

if (currentlysettingtemp && settingwhat == 0)
{
if (blinkint > 5)
{
blinkint = 0;
display.print(" ");
}
else
{
blinkint++;
display.print(OnTemp);
}
}
else
{
display.print(OnTemp);
}
display.println("F On");

if (currentlysettingtemp && settingwhat == 1)
{
if (blinkint > 5)
{
blinkint = 0;
display.print(" ");
}
else
{
blinkint++;
display.print(OffTemp);
}
}
else
{
display.print(OffTemp);
}
display.println("F Off");

if (heateron)
{
display.println("Heat On");
}
else
{
display.println("Heat Off");
}

display.display();
}

 

Client Code

//DS18B20
#include <OneWire.h>
#include <DallasTemperature.h>

//nRF24L01
#include <SPI.h>
#include <Mirf.h>
#include <nRF24L01.h>
#include <MirfHardwareSpiDriver.h>

//Sleep Power Saving
#include <avr/sleep.h>
#include <avr/wdt.h>

//DS18B20
#define ONE_WIRE_BUS 3
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void setup()
{
Serial.begin(9600);

//DS18B20
sensors.begin();

//nRF24L01
Mirf.spi = &MirfHardwareSpi;
Mirf.init();
Mirf.setRADDR((byte *)"TempClient1");
Mirf.payload = sizeof(unsigned long);
Mirf.config();

Serial.println("Client Started");
}

void loop()
{
//DS18B20
sensors.requestTemperatures();
double temp = sensors.getTempCByIndex(0) * 1.8 + 32;
Serial.println(temp);

char tempString[sizeof(temp)];
dtostrf(temp,2,2,tempString);

//nRF24L01
Mirf.setTADDR((byte *)"TempServer1");
transmit(tempString);

PutToBed();
}

void PutToBed()
{
// disable ADC
ADCSRA = 0;

// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0); // set WDIE, and 8 seconds delay
wdt_reset(); // pat the dog

set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();

// turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
sleep_cpu ();

// cancel sleep as a precaution
sleep_disable();
}

void transmit(const char *string)
{
byte c;

for( int i=0 ; string[i]!=0x00 ; i++ )
{
c = string[i];
Mirf.send(&c);
while( Mirf.isSending() ) ;
}

c = '~';
Mirf.send(&c);
while( Mirf.isSending() ) ;
}

Battery Level Code
#define NUM_SAMPLES 10

int sum = 0;
unsigned char sample_count = 0;
float voltage = 0.0;
float R_LOAD = 5.015;
float numofchecks = 0;
float totalvoltage = 0;
float ledvalue = 0;

int ledRed = 6;
int ledGreen = 5;
int ledBlue = 3;

int green = 0;
int yellow = 0;
int red = 0;

int ledtimer = 0;

void setup()
{
Serial.begin(9600);
}

void loop()
{
ledtimer++;
numofchecks++;
// take a number of analog samples and add them up
while (sample_count < NUM_SAMPLES) {
sum += analogRead(A2);
sample_count++;
delay(10);
}

voltage = ((float)sum / (float)NUM_SAMPLES * R_LOAD) / 1024.0;
voltage = voltage * 11.132;
totalvoltage += voltage;

if(numofchecks > 10)
{
float tv = totalvoltage / numofchecks;
ledvalue = tv;
Serial.print("Avg V: ");
Serial.println(tv);

numofchecks = 0;
totalvoltage = 0;
}

sample_count = 0;
sum = 0;

ShowLED();
}

void ShowLED()
{
float batterycap = 6;
float batteryper = ledvalue / batterycap;

float greenval = 50 * batteryper;
float redval = 0;
if (batteryper >= 0.80 && batteryper < 0.90)
{
redval = 25;
}
else if (batteryper >= 0.70 && batteryper < 0.80)
{
redval = 50;
}
else if (batteryper >= 0.50 && batteryper < 0.70)
{
redval = 100;
}
else if (batteryper < 0.50)
{
redval = 255;
}

analogWrite(ledGreen, greenval);
analogWrite(ledRed, redval);
}