EcoRenovator  

Go Back   EcoRenovator > Improvements > Geothermal & Heat Pumps
Advanced Search
 


Blog 60+ Home Energy Saving Tips Recent Posts Search Today's Posts Mark Forums Read


Reply
 
Thread Tools Display Modes
Old 11-17-15, 07:15 AM   #11
jeff5may
Supreme EcoRenovator
 
Join Date: Jan 2010
Location: elizabethtown, ky, USA
Posts: 2,428
Thanks: 431
Thanked 619 Times in 517 Posts
Send a message via Yahoo to jeff5may
Default

Quote:
Originally Posted by TechShop View Post
Acuario, nice work. I don't think you need to add any frills to your web pages. That touch screen thermostat will put a nice face on the finished product.

Jeff, for the reasons you have cited, I'm an advocate of using an inexpensive PLC relay unit to do the dirty work at the heat pump as commanded by the system controller or thermostat (in this case, Arduino).

The simple ladder logic makes it possible to control a complex heat pump arrangement (compressor, pumps, fans, etc with a basic thermostat and these little PLCs can be found in the same price range as a basic Arduino.
For those with a distaste for ladder logic, the PLC units can be fed a program created elsewhere (on a PC) and translated to ladder. Usually the translated program is optimized during translation, so what you write may not be exactly what makes it into the PLC. Debugging or writing explicit commands into the source code is sometimes necessary.

I used a "freebie" power board out of a donated portable dehumidifier for a relay shield equivalent. The board also has a regulated 5VDC power supply built into it that feeds the Uno board through the USB plug.

The board in its old home


The board connected to the Uno with LCD keypad shield

Please take note that this rig functions as a non-standard heat pump thermostat as wired. The yellow and green wires are compressor and reversing valve call, the red is the 24VAC feed to run contactors and controls inside the unit. Defrost control is handled by an ICM 315 board inside the unit. That's what I had in it before the GP controller was made.

However, these power boards are pretty much the same in every window AC or portable dehumidifier out there that has a digital readout and pushbutton control. These boards have enough relays on them that are matched to the demands of the patient unit, so all that wiring and such is already tested and proven by the OEM. To morph a unit into a heat pump, one of the fan relays could easily be rewired to control a reversing valve solenoid. For a water-source unit, a HX pump or two could run straight off a relay or two.

jeff5may is offline   Reply With Quote
Old 11-17-15, 10:34 AM   #12
TechShop
FNG
 
Join Date: Jul 2015
Location: Washington
Posts: 71
Thanks: 8
Thanked 19 Times in 13 Posts
Default

Quote:
Originally Posted by jeff5may View Post
I used a "freebie" power board out of a donated portable dehumidifier for a relay shield equivalent. The board also has a regulated 5VDC power supply built into it that feeds the Uno board through the USB plug.
Perfect! Any time you can source your parts from the scrap pile, or the donor equipment, you're way ahead. I recall seeing similar boards in the 230v/50Hz split units I dealt with overseas. The window units I've played with here in the US were all running on simple capillary-bulb close-on-rise mechanical switches.

I'm mentally breaking down that board: Transformer to bring down the mains voltage... four discrete diodes are the rectifier... Perhaps the TO-220 package to the upper left is the LM7805 with a few filter caps surrounding it for 5v power. I imagine that little DIP 16 sized IC in the upper right quadrant of the first picture is some kind of gate array or buffer to interface the relays with TTL signals... or perhaps some other low complexity IC? The white header at the top is the control interface. ...and some kind of piezoelectric alarm/horn in there too? I would presume the large black square relay is the compressor. The 3 blue relays are fan motors, etc.

Good stuff.
TechShop is offline   Reply With Quote
The Following User Says Thank You to TechShop For This Useful Post:
jeff5may (11-17-15)
Old 11-17-15, 03:29 PM   #13
jeff5may
Supreme EcoRenovator
 
Join Date: Jan 2010
Location: elizabethtown, ky, USA
Posts: 2,428
Thanks: 431
Thanked 619 Times in 517 Posts
Send a message via Yahoo to jeff5may
Default

Quote:
Originally Posted by TechShop View Post
Perfect! Any time you can source your parts from the scrap pile, or the donor equipment, you're way ahead. I recall seeing similar boards in the 230v/50Hz split units I dealt with overseas. The window units I've played with here in the US were all running on simple capillary-bulb close-on-rise mechanical switches.

I'm mentally breaking down that board: Transformer to bring down the mains voltage... four discrete diodes are the rectifier... Perhaps the TO-220 package to the upper left is the LM7805 with a few filter caps surrounding it for 5v power. I imagine that little DIP 16 sized IC in the upper right quadrant of the first picture is some kind of gate array or buffer to interface the relays with TTL signals... or perhaps some other low complexity IC? The white header at the top is the control interface. ...and some kind of piezoelectric alarm/horn in there too? I would presume the large black square relay is the compressor. The 3 blue relays are fan motors, etc.

Good stuff.
You pretty much nailed the board. The mains transformer spits out 9vac or so which filters to unregulated 12vdc for the relay coils. The chip is typically a ULNxxxx (EDIT: xxxx=2003 or 2803) logic inverter which runs off the 7805 and makes or breaks ground to the relays and buzzer. The chip takes TTL inputs from the control at the other end of the white plug. Power and cold ground feed out the white plug to run the control at the other end. Love and happiness for my demands!

Another power board mentioned is the Amana p/n 30132033. This one has no mains transformer, but has extra high-current relays (original purpose:strip heaters) and thermistors on board that connect to the same type of pin headers. This board originally went in hotel PTAC units and still sells for around $20.

Last edited by jeff5may; 11-20-15 at 06:17 AM.. Reason: part number added
jeff5may is offline   Reply With Quote
Old 01-03-16, 02:17 PM   #14
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default Code for heatpump controller

Ok, here we go.. This is my code for my heat pump controller. First an explanation of the structure.

There are 9 individual modules (.ino files) that make up the controller. Each one attempts to encapsulate functionality of a particular part of the controller.

As the complexity of the controller and the necessity to run various peripheral parts increased it became evident that a real time operating system would be needed, many of the modules run functions specifically related to the peripherals leaving the 'main' program (thread) to control the state of the machine.

Enough waffle..
The code for the RTOS was found here:
https://github.com/greiman/NilRTOS-Arduino

Other code libraries are either part of the Arduino code or are easily found searching on Google. Some code is modified from samples and extended to meet my needs. The code is commented so hopefully makes sense. I'll split it into individual posts as it's a bit much all in one...
Note: Some defines/code may not be used as it was there in development and I haven't removed it yet..


1. Main header file HeatpumpController.h
#pragma once
#ifndef HPCONTROLLER_H
#define HPCONTROLLER_H

int defTemp; //Temperature to run a defrost cycle
int defTime; //Time to run defrost cycle
int defUpperTemp; //If evap reaches this temperature abandon the defrost cycle
int maxWaterTemp; //Maximum circuit water temperature
int desiredRoomTemp; //Desired room temperature
int hysterisis; //Hysterisis setting
int defStep; //Defrost step
int deltaOn; //Temp to turn on pump
int deltaOff; //Temp to turn off pump
bool operatingState; //System operating state - true=on, false=off
bool machineRunningState = FALSE; //State of machine - true=on, false=off
int operatingMode; //System operating mode
char systemState[20]; //System state string
bool readAnalog = FALSE; //Flag to indicate an analogue conversion is taking place. Needed for rtos so 2 analogue reads don't clash
bool rtosStarted = FALSE; //Flag the RTOS has started (needed for change from delay to nilDelay...)

unsigned long lastDefrost = 0;
unsigned long timeBetweenDefrost; //Time between defrost cycles
unsigned long defrostRunTime;
unsigned long defrostStartDelay;
unsigned long defrostStartTime;
bool defrostFlag = FALSE; //Flag to say if we are in a defrost cycle (and to start a defrost cycle)

float lastTempEvaporator;
float lastTempCompressor;
float tempCompressor;
float tempEvaporator;
float humidity;
float dht11Temperature;
float dewPointTemp;

#define ON 0
#define OFF 1
#define COOL 0
#define HEAT 1

//Analogue sensors
int ntcSensor[4];
#define flowTemp ntcSensor[0]
#define returnTemp ntcSensor[1]
#define ambientTemp ntcSensor[2]
#define s4 ntcSensor[3]

//Heat pump control pins (Digital outputs)
#define FANLOW 2
#define FANHIGH 3
#define COMPRESSOR 4
#define DHT11PIN 5
#define DS18B20 7
#define PUMP 8
#define VALVE 9

//Status of individual relays. Set all on so startup sets the pin outputs correctly
bool compStat = ON;
bool fanLow = ON;
bool fanHigh = ON;
bool valve = ON;
bool pump = ON;

//Non volatile memory storage
//#define STARTDELAY 0 //Startup delay time
//#define MAX_TEMP 1 //
//#define MIN_TEMP 2
//#define HEATCOOL 3
#define DEFROSTTIME 4 //Defrost time
#define DEFROSTTEMP 5 //Defrost start temperature
#define DEFROSTMAXTEMP 6 //Temperature to stop defrost (overrides time for defrost cycle)
#define MAXCOMPTEMP 7 //Maximum compressor temperature
#define LASTSYSTEMSTATE 8 //Last state - on or off
#define LASTSYSTEMMODE 9 //Last operating mode (heat/cool)
#define DESIREDROOMTEMP 10 //Temperature for room
#define MAXWATERTEMP 11 //Maximum water temperature
#define HYSTERISIS 12 //Hysterisis setting
#define TIMEBETWEENDEFROST 13 //Time between defrost cycles
#define LASTCOMPRUN 14 //Time last compressor run - note 4 bytes!
#define NEXTFREE 18

/* System constants */
#define DEFROSTTEMP 2
#define DEFROST_OFFSET 10
#define COMPSTARTDELAY 180000 //Minimum delay time between compressor starts (for power fail/reset etc)

/* LCD Pages */
#define LCD_PAGE0 0 //General status
#define LCD_PAGE1 1 //Defrost settings
#define LCD_PAGE2 2
#define LCD_PAGE3 3
#define LCD_PAGE4 4
#define LCD_PAGES 4

int lcd_page = LCD_PAGE0; //Starting LCD page

#include <Time.h>
tmElements_t tm;

void writeTime(tmElements_t tmw);
void readTime();
void saveSystemState(char *state);

//Stuff for debug output via serial port
void SerialPrint2(char *format, ...);
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print (x)
#define DEBUG_PRINTST(x,...) SerialPrint2 (x,__VA_ARGS__)
#define DEBUG_PRINTLN(x) Serial.println (x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTST(x,...)
#define DEBUG_PRINTLN(x)
#endif

//Thread definitions for NilRTOS
NIL_THREAD(threadEthernet, arg);
NIL_THREAD(threadReadNTC, arg);
NIL_THREAD(threadReadDHT11, arg);
NIL_THREAD(threadReadSensors, arg);
NIL_THREAD(threadReadBAC1000, arg);
NIL_THREAD(threadDisplayOLED, arg);
NIL_THREAD(threadSetBACTime, arg);

//System strings
const char sOn[] = "On";
const char sOff[] = "Off";
const char sTrue[] = "True";
const char sFalse[] = "False";
#endif
Acuario is offline   Reply With Quote
Old 01-03-16, 02:19 PM   #15
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default

2. Main code file HeatpumpController.ino
/*
ROCA heat pump pins:
1 - Live
2 - Neutral (Common)
3 - Earth
4 - Valve
5 -
6 - Fan HIGH
7 - Fan LOW
8 -

Sensors:
#1 - pipe sensor (bottom of evaporator)
#5 - Environment

*/
#define DEBUG
#include <DallasTemperature.h>
#include <NilRTOS.h>
#include <Arduino.h>
#include <avr/wdt.h>
#include <NilSerial.h>
#include <NilAnalog.h>

#include <Wire.h>

#include "HeatpumpController.h"
#include "BAC1000.h"

// Macro to redefine Serial as NilSerial to save RAM.
// Remove definition to use standard Arduino Serial.
#define Serial NilSerial
#define analogRead(pin) nilAnalogRead(pin)
//#define delay(ms) nilThdSleep(ms)


void setup(void) {
byte data[1];

//Disable watchdog while setting up everything
wdt_disable();

// Open serial communications
Serial.begin(115200);

setupEthernet(); // start the Ethernet connection and the server:
readTime(); //Get the time from the DS1307
setupRS485(); //Setup RS485 (BAC1000)

EEPROM_read_byte(DEFROSTTEMP, data); //Temperature to run defrost cycle
defTemp = data[0] - DEFROST_OFFSET;
if (defTemp > 10) {
defTemp = 2;
EEPROM_write_byte(DEFROSTTEMP, defTemp + DEFROST_OFFSET);
}

EEPROM_read_byte(DEFROSTTIME, data); //Defrost cycle time (minutes)
defTime = data[0];
if (defTime > 10) {
defTime = 2;
EEPROM_write_byte(DEFROSTTIME, defTime);
}

EEPROM_read_byte(DEFROSTMAXTEMP, data); //Defrost Maximum temperature (temperature to stop defrost, overrides timer)
defUpperTemp = data[0];
if (defUpperTemp > 20) {
defUpperTemp = 10;
EEPROM_write_byte(DEFROSTMAXTEMP, defUpperTemp);
}

EEPROM_read_byte(TIMEBETWEENDEFROST, data); //Minimum time between defrost cycles
timeBetweenDefrost = data[0];
if ((timeBetweenDefrost > 60) || (timeBetweenDefrost < 15)) {
timeBetweenDefrost = 30;
EEPROM_write_byte(TIMEBETWEENDEFROST, timeBetweenDefrost);
}

EEPROM_read_byte(DESIREDROOMTEMP, data); //Desired room temperature
desiredRoomTemp = data[0];
if ((desiredRoomTemp > 35) || (desiredRoomTemp < 10)) {
desiredRoomTemp = 21;
EEPROM_write_byte(DESIREDROOMTEMP, desiredRoomTemp);
}

EEPROM_read_byte(MAXWATERTEMP, data); //Max water temperature
maxWaterTemp = data[0];
if ((maxWaterTemp > 35) || (maxWaterTemp < 10)) {
maxWaterTemp = 21;
EEPROM_write_byte(MAXWATERTEMP, maxWaterTemp);
}

EEPROM_read_byte(HYSTERISIS, data); //Hysterisis
hysterisis = data[0];
if ((hysterisis > 15) || (hysterisis < 2)) {
hysterisis = 5;
EEPROM_write_byte(HYSTERISIS, hysterisis);
}

EEPROM_read_byte(LASTSYSTEMSTATE, data); //Last system state (on/off)
operatingState = data[0];

//If the system was on then start the circulating pump before waiting for everything else
if (operatingState == TRUE) {
setRelay(PUMP, ON);
}
sendBAC1000Command(POWERONOFF, 0, 1, (operatingState << 4), 0, 0, 0);

EEPROM_read_byte(LASTSYSTEMMODE, data); //Last system operating mode (heat/cool)
operatingMode = data[0];
if (operatingMode > 1) {
operatingMode = 1;
EEPROM_write_byte(LASTSYSTEMMODE, operatingMode);
}

DEBUG_PRINTLN(F("STARTUP - STARTUP - STARTUP"));
debugStateOutput();
DEBUG_PRINTLN(F("END STARTUP - END STARTUP"));

//For testing defrost
//defrostUpperTemperature = 40;
//defrostFlag = TRUE;

//Set BAC1000 time and then read its settings so we can synchronise operations
sendBAC1000Command(SETTIME, 0, 1, tm.Second, tm.Minute, tm.Hour, 1);
do {
delay(2000);
sendBAC1000Command(READALL, 0, 1, 0, 0, 0, 0);
} while (BACRead == FALSE);

//Set outputs to off before enabling pinMode so relays don't turn on at startup
setRelay(PUMP, OFF);
setRelay(VALVE, OFF);
setRelay(FANLOW, OFF);
setRelay(FANHIGH, OFF);
setRelay(COMPRESSOR, OFF);
pinMode(PUMP, OUTPUT); // sets the digital pin as output
pinMode(VALVE, OUTPUT); // sets the digital pin as output
pinMode(FANLOW, OUTPUT); // sets the digital pin as output
pinMode(FANHIGH, OUTPUT); // sets the digital pin as output
pinMode(COMPRESSOR, OUTPUT); // sets the digital pin as output

wdt_enable(WDTO_8S); //Start watchdog timer - watchdog reset is in oled code as no delays
nilSysBegin(); // Start Nil RTOS.
rtosStarted = TRUE; //Flag the RTOS has started (needed for change from delay to nilDelay...)
}

void loop(void) {
//int keyValue = 0;

//keyValue = readKeys();
//if (keyValue == 1) {
// lcd_page = lcd_page++ >= LCD_PAGES ? 0 : lcd_page; //Cycle the lcd page count
//}
}

//Main running loop
NIL_THREAD(threadMain, arg) {
unsigned long lastCompressorRun;
char buf[10];

while (TRUE) {
debugStateOutput();

//See if the machine is on or off
if ((operatingMode == HEAT)&&(operatingState == TRUE)) {
//Operational modes for heating
DEBUG_PRINTLN(F("Mode:HEAT, operatingState:ON"));
if ((defrostFlag == FALSE)&&((flowTemp >= maxWaterTemp)||(bacRoomTemp >= desiredRoomTemp))) {
//If we have achieved the target temperature then turn everything but the circulating pump off
saveSystemState("Circulate");
setRelay(PUMP, ON);
setRelay(VALVE, OFF);
setRelay(FANLOW, OFF);
setRelay(FANHIGH, OFF);
setRelay(COMPRESSOR, OFF);
machineRunningState = FALSE;
}

//Machine startup
else if ((flowTemp <= maxWaterTemp - hysterisis)&&(machineRunningState == FALSE)){
//See if the compressor ran recently if so delay startup
lastCompressorRun = EEPROM_readlong(LASTCOMPRUN);
//DEBUG_PRINTST("Last run=%lu\n", lastCompressorRun);
unsigned long currentTimeMs = makeTimeMilli(tm);
//DEBUG_PRINTST("Current=%lu\n", currentTimeMs);
setRelay(PUMP, ON);
if ((lastCompressorRun + COMPSTARTDELAY) < currentTimeMs) {
saveSystemState("Startup");
setRelay(FANLOW, ON);
setRelay(FANHIGH, ON);
setRelay(VALVE, ON);
nilThdSleepSeconds(5);
setRelay(COMPRESSOR, ON);
machineRunningState = TRUE;
}
else
{
saveSystemState("Wait for startup");
}
}

//Check to see if we need to do a defrost.
else if (tempEvaporator == defTemp) {
if(millis() - lastDefrost >= timeBetweenDefrost)
defrostFlag = TRUE;
}
else if (machineRunningState == TRUE)
{
saveSystemState("Running");
}
}

//Else if it is running turn everything off
else if(operatingState == FALSE)
{
saveSystemState("Off");
setRelay(FANLOW, OFF);
setRelay(FANHIGH, OFF);
setRelay(COMPRESSOR, OFF);
setRelay(VALVE, OFF);
setRelay(PUMP, OFF);
defrostFlag = FALSE;
machineRunningState = FALSE;
}

//If the compressor is running save the current time for startup delay
if (compStat == ON) {
EEPROM_writelong(LASTCOMPRUN, makeTimeMilli(tm));
}

// Sleep for 5S.
nilThdSleepSeconds(5);
}
}

//Debug out system status
void debugStateOutput(void) {
DEBUG_PRINTST("Operating mode=%s\n", mode[operatingMode]);
DEBUG_PRINTST("operatingState=%s\n", operatingState == TRUE ? sTrue : sFalse);
DEBUG_PRINTST("machineRunningState=%s\n", machineRunningState == TRUE ? sTrue : sFalse);
DEBUG_PRINTST("flowTemp=%d\n", flowTemp);
DEBUG_PRINTST("maxWaterTemp=%d\n", maxWaterTemp);
DEBUG_PRINTST("bacRoomTemp=%d\n", bacRoomTemp);
DEBUG_PRINTST("desiredRoomTemp=%d\n", desiredRoomTemp);
}

//Save string for system state and output the state on debugger
void saveSystemState(char *state)
{
sprintf(systemState, state);
DEBUG_PRINTLN(systemState);
}

// Turn on/off relays and preserve state flag
void setRelay(int relay, bool state)
{
switch (relay) {
case COMPRESSOR:
if (compStat != state) {
digitalWrite(COMPRESSOR, state);
compStat = state;
}
break;
case FANLOW:
if (fanLow != state) {
digitalWrite(FANLOW, state);
fanLow = state;
}
break;
case FANHIGH:
if (fanHigh != state) {
digitalWrite(FANHIGH, state);
fanHigh = state;
}
break;
case VALVE:
if (valve != state) {
digitalWrite(VALVE, state);
valve = state;
}
break;
case PUMP:
//DEBUG_PRINTST("Pump = %d, set = %d\n", pump, state);
if (pump != state) {
digitalWrite(PUMP, state);
pump = state;
}
break;
}
}

//Thread to control derfost cycle
NIL_THREAD(threadDefrost, arg) {
while (TRUE) {
const char defStepS[] = "Defrost step";

if (defrostFlag == TRUE) {
sprintf(systemState, "%s 1", defStepS);
DEBUG_PRINTLN(systemState);
defStep = 1;
defrostStartTime = millis();
setRelay(FANLOW, OFF);
setRelay(FANHIGH, OFF);
setRelay(COMPRESSOR, OFF);
nilThdSleepSeconds(10);
defStep = 2;
sprintf(systemState, "%s 2", defStepS);
setRelay(VALVE, OFF);
nilThdSleepSeconds(10);
defStep = 3;
sprintf(systemState, "%s 3", defStepS);
setRelay(COMPRESSOR, ON);

while (defrostFlag == TRUE) {
if (tempEvaporator >= defUpperTemp)
defrostFlag = FALSE; //Cancel defrost if we have reached the programmed upper temperature
defrostRunTime = millis() - defrostStartTime;
if(defrostRunTime >= (defTime * 60000))
defrostFlag = FALSE; //Cancel defrost if we have reached the timeout for the defrost cycle
nilThdSleepMilliseconds(500);
}
defStep = 4;
sprintf(systemState, "%s 4", defStepS);
setRelay(COMPRESSOR, OFF);
nilThdSleepSeconds(10);
defStep = 5;
sprintf(systemState, "%s 5", defStepS);
setRelay(FANHIGH, ON);
nilThdSleepSeconds(5);
defStep = 6;
sprintf(systemState, "%s 6", defStepS);
setRelay(VALVE, ON);
nilThdSleepSeconds(10);
setRelay(COMPRESSOR, ON);
defStep = 0;
defrostFlag = FALSE;
lastDefrost = millis(); //Save the last defrost end time
}
// Sleep for 30S.
nilThdSleepSeconds(30);
}
}
Acuario is offline   Reply With Quote
Old 01-03-16, 02:20 PM   #16
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default Last part of HeatpumpController.ino

// Declare stacks for threads
NIL_WORKING_AREA(waThreadMain, 1024);
NIL_WORKING_AREA(waThreadEth, 1024);
NIL_WORKING_AREA(waThreadDS18B20, 128);
NIL_WORKING_AREA(waThreadBAC, 256);
NIL_WORKING_AREA(waThreadDHT11, 128);
NIL_WORKING_AREA(waThreadOLED, 128);
NIL_WORKING_AREA(waThreadDefrost, 32);
NIL_WORKING_AREA(waThreadNTC, 64);//128
NIL_WORKING_AREA(waThreadSetBACTime, 256);

/*
* Threads static table, one entry per thread. A thread's priority is determined by its position in the table with highest priority first.
* These threads start with a null argument. A thread's name may also be null to save RAM since the name is currently not used.
*/
NIL_THREADS_TABLE_BEGIN()
NIL_THREADS_TABLE_ENTRY("", threadEthernet, NULL, waThreadEth, sizeof(waThreadEth))
NIL_THREADS_TABLE_ENTRY("", threadMain, NULL, waThreadMain, sizeof(waThreadMain))
NIL_THREADS_TABLE_ENTRY("", threadDisplayOLED, NULL, waThreadOLED, sizeof(waThreadOLED))
NIL_THREADS_TABLE_ENTRY("", threadReadBAC1000, NULL, waThreadBAC, sizeof(waThreadBAC))
NIL_THREADS_TABLE_ENTRY("", threadReadSensors, NULL, waThreadDS18B20, sizeof(waThreadDS18B20))
NIL_THREADS_TABLE_ENTRY("", threadReadDHT11, NULL, waThreadDHT11, sizeof(waThreadDHT11))
NIL_THREADS_TABLE_ENTRY("", threadDefrost, NULL, waThreadDefrost, sizeof(waThreadDefrost))
NIL_THREADS_TABLE_ENTRY("", threadReadNTC, NULL, waThreadNTC, sizeof(waThreadNTC))
NIL_THREADS_TABLE_ENTRY("", threadSetBACTime, NULL, waThreadSetBACTime, sizeof(waThreadSetBACTime))
NIL_THREADS_TABLE_END()

//Format and output string to serial debug port
void SerialPrint2(char *format, ...)
{
char buff[128];
va_list args;
va_start(args, format);
vsnprintf(buff, sizeof(buff), format, args);
va_end(args);
buff[sizeof(buff) / sizeof(buff[0]) - 1] = '\0';
Serial.print(buff);
}
Acuario is offline   Reply With Quote
Old 01-03-16, 02:21 PM   #17
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default

3. 24C34EEPROM.ino

#define AT24C32_I2C_ADDRESS 0x50 // the I2C address of Tiny RTC AT24C32 EEPROM

void EEPROM_write_byte(unsigned int uiAddress, byte bData)
{
int iCount = 0;
do
{
Wire.beginTransmission(AT24C32_I2C_ADDRESS);
Wire.write((byte)(uiAddress >> 8)); // MSB
Wire.write((byte)uiAddress); // LSB
Wire.write(bData);
} while (Wire.endTransmission() != 0 && ++iCount<10);
}

void EEPROM_read_byte(unsigned int uiAddress, byte *pbData)
{
int iCount = 0;
do
{
Wire.beginTransmission(AT24C32_I2C_ADDRESS);
Wire.write((byte)(uiAddress >> 8)); // MSB
Wire.write((byte)uiAddress); // LSB
} while (Wire.endTransmission() != 0 && ++iCount<10);
Wire.requestFrom(AT24C32_I2C_ADDRESS, 1);
*pbData = Wire.read();
}


//////////////////////////////////////////////////////////////////////////////////////
// read long from EEPROM, give starting address
unsigned long EEPROM_readlong(int address) {
byte data[1];
unsigned long dword = 0;

EEPROM_read_byte(address, data);
dword = data[0];
dword = dword << 8;
EEPROM_read_byte(address+1, data);
dword = dword + data[0];
dword = dword << 8;
EEPROM_read_byte(address+2, data);
dword = dword + data[0];
dword = dword << 8;
EEPROM_read_byte(address+3, data);
dword = dword + data[0];
return dword;
}

void EEPROM_writelong(int address, unsigned long dword) {
byte data;

data = dword & 0xff;
EEPROM_write_byte(address + 3, data);
dword = dword >> 8;
data = dword & 0xff;
delay(100);
EEPROM_write_byte(address + 2, data);
dword = dword >> 8;
data = dword & 0xff;
delay(100);
EEPROM_write_byte(address + 1, data);
dword = dword >> 8;
data = dword & 0xff;
delay(100);
EEPROM_write_byte(address, data);
}
Acuario is offline   Reply With Quote
Old 01-03-16, 02:22 PM   #18
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default

4. BAC1000.h - header file for BAC1000 thermometer

#pragma once
#ifndef _BAC1000_h
#define _BAC1000_h

//RS485 settings
#define RS485TxControl 10 //RS485 Direction control
#define RS485Transmit HIGH
#define RS485Receive LOW
#define RS485Serial Serial1


//Commands
#define READALL 0xa0 //Read all data from thermostat
#define TRANSMITALL 0xa1 //Send all data to thermostat
#define POWERONOFF 0xa2 //Power on/off
#define FANSPEED 0x03 //Control fan speed relays
#define MODESETTING 0xa4 //Mode setting
#define KEYLOCK 0xa5 //Lock/unlock keypad
#define TEMPSETTING 0xa6 //Set desired temperature
#define TEMPCALIB 0xa7 //Temperature calibration
#define SETTIME 0xa8 //Set thermostat clock
#define AUTOMANUAL 0xa9 //Manual/auto programmable
#define DATAACK 0x50 //Command accepted ok

//Masks for status
#define UNITONOFF 0x10 //Unit on - 1, off - 0
#define HEATCOOL 0x60 //Operation mode - 00 - Cooling, 01 - Heating, 10 - Ventilation
#define FANS 0x03 //Fan speed - 00 - Auto, 01 - High, 10 - Medium, 11 - Low
#define POWERON 0x10 //Power unit on, power off with 00

int bacRoomTemp;
int bacSetTemp;
bool powerStateBAC;
int operatingModeBAC;
const char* mode[] = { "Cool", "Heat", "Ventilate" };
bool BACRead = FALSE;
bool waitRead = FALSE;
bool sendBAC1000Command(int bacCmd, int idl, int idh, int data1, int data2, int data3, int data4);

#endif
Acuario is offline   Reply With Quote
Old 01-03-16, 02:23 PM   #19
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default

5. BAC1000.ino
#include <Wire.h>

//For BAC1000 thermometer
#include "BAC1000.h"

NIL_THREAD(threadReadBAC1000, arg) {
while (TRUE) {
{
sendBAC1000Command(READALL, 0, 1, 0, 0, 0, 0);
// Sleep for 30 seconds.
nilThdSleepSeconds(30);
}
}
}

//Reset BAC time every hour
NIL_THREAD(threadSetBACTime, arg) {
while (TRUE) {
{
DEBUG_PRINTLN("Set BAC Time");
sendBAC1000Command(SETTIME, 0, 1, tm.Second, tm.Minute, tm.Hour, 1);
// Sleep for 1 hour
nilThdSleepSeconds(3600);
}
}
}

void setupRS485(void) {

//Setup for RS485
pinMode(RS485TxControl, OUTPUT);
digitalWrite(RS485TxControl, RS485Receive); // Init Transceiver
RS485Serial.begin(2540); // set the data rate
}

//Send command to BAC1000. Retry 5 times
// Command, Device ID low, ID high, Data(4)
bool sendBAC1000Command(int bacCmd, int idl, int idh, int data1, int data2, int data3, int data4) {
int retryCount = 0;
bool result;

//Wait for current read to end before trying to read again
do{
if (rtosStarted == TRUE)
nilThdSleepSeconds(2);
else
delay(2000);
}while (waitRead == TRUE);

waitRead = TRUE;
for (retryCount = 0; retryCount < 2; retryCount++) {
result = sendBAC1000Cmd(bacCmd, idl, idh, data1, data2, data3, data4);
if (result == FALSE)
{
DEBUG_PRINTLN("BAC command fail");
if (rtosStarted == TRUE)
nilThdSleepSeconds(5);
else
delay(5000);
}
else {
DEBUG_PRINTLN("BAC read ok");
break;
}
}
waitRead = FALSE;
return result;
}

bool sendBAC1000Cmd(int bacCmd, int idl, int idh, int data1, int data2, int data3, int data4) {
int byteReceived;
int byteSend;
int i = 0;
char checksum;
char frameRX[8];
char frameTX[7] = { bacCmd, idl, idh, data1, data2, data3, data4 };
//char frame[7] = { 0xA8, 0, 1, 0, 0x0, 9, 1 };

DEBUG_PRINTLN("Send BAC command frame");

digitalWrite(RS485TxControl, RS485Transmit); // Enable RS485 Transmit
for (int i = 0; i < 7; i++) {
RS485Serial.write(frameTX[i]); // Send bytes to thermostat
}

RS485Serial.write(bac1000Checksum(frameTX)); // Send byte to Remote
RS485Serial.flush(); //Wait for end of send before disabling RS485
digitalWrite(RS485TxControl, RS485Receive); // Disable RS485 Transmit

while (RS485Serial.available()) //Look for data
{
byteReceived = RS485Serial.read(); // Read received byte
frameRX[i] = byteReceived;
i++;
//Serial.print(byteReceived); // Show on Serial Monitor
//Serial.print(" ");
}

if (frameRX[0] == DATAACK) { //Data received ok
switch (bacCmd) {
case READALL:
char buf[20];
char checksum1;
checksum = frameRX[7];
frameRX[7] = '\0';
checksum1 = bac1000Checksum(frameRX);
if (checksum1 != checksum) {
//sprintf(buf, " Frame=%x %x %x %x %x %x %x %x", frameRX[0], frameRX[1], frameRX[2], frameRX[3], frameRX[4], frameRX[5], frameRX[6], frameRX[7]);
//Serial.println(buf);
//sprintf(buf, " o=%x n=%x", checksum, checksum1);
DEBUG_PRINTLN("BAC CS Failure");
//Serial.println(buf);
return FALSE;
}
bacRoomTemp = frameRX[6];
bacSetTemp = frameRX[5] / 2;
desiredRoomTemp = bacSetTemp;

//Read and update BAC power state if necessary
powerStateBAC = frameRX[3] & UNITONOFF;
if(operatingState != powerStateBAC){
operatingState = powerStateBAC;
EEPROM_write_byte(LASTSYSTEMSTATE, operatingState);
}

//Read and update operating mode if necessary
operatingModeBAC = (frameRX[3] & HEATCOOL) >> 5;
if (operatingMode != operatingModeBAC) {
operatingMode = operatingModeBAC;
EEPROM_write_byte(LASTSYSTEMMODE, operatingMode);
}

BACRead = TRUE; //Flag the BAC has been successfully read
//char buf[20];
//sprintf(buf, "Room temp: %d", frameRX[6]);
//Serial.println(buf);
//sprintf(buf, "Set temp: %d", frameRX[5] / 2);
//Serial.println(buf);
//sprintf(buf, "Thermometer power: %s", powerStateBAC == 0 ? "Off" : "On");
//Serial.println(buf);
//sprintf(buf, "Mode: %s", mode[operatingModeBAC]);
//Serial.println(buf);
break;
}
return TRUE;
}
else
{
return FALSE;
}
}

//CheckSum = (COMMAND + ID0 + ID1 + Data0 + Data1 + Data2 + Data3) & 0xFF ^ 0xA5;
unsigned char bac1000Checksum(char *ptr) {
unsigned char chk = 0;

for (int i = 0; i < 7; i++) {
chk += ptr[i];
}
chk = chk & 0xff;
chk = chk ^ 0xa5;
return chk;
}
Acuario is offline   Reply With Quote
Old 01-03-16, 02:24 PM   #20
Acuario
Apprentice EcoRenovator
 
Join Date: May 2011
Location: Tortosa, Spain
Posts: 221
Thanks: 2
Thanked 81 Times in 46 Posts
Default

6. DHT11.ino
#include <dht.h>
#include <math.h>

dht DHT11;

NIL_THREAD(threadReadDHT11, arg) {
while (TRUE) {
int chk = DHT11.read11(DHT11PIN);

//Serial.print("Read sensor: ");
switch (chk)
{
case DHTLIB_OK:
humidity = (float)DHT11.humidity;
dht11Temperature = (float)DHT11.temperature;
dewPointTemp = dewPoint(DHT11.temperature, DHT11.humidity);
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.println("DHT11 Checksum error");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.println("DHT11 Time out error");
break;
default:
Serial.println("DHT11 Unknown error");
break;
}

//Serial.print("Humidity (%): ");
//Serial.println(humidity, 2);

//Serial.print("Temperature (°C): ");
//Serial.println(dht11Temperature, 2);

//Serial.print("Dew Point (°C): ");
//Serial.println(dewPointTemp);

// Sleep for 10 seconds.
nilThdSleepSeconds(10);
}
}

// dewPoint function NOAA
// reference (1) : Algorithms - Schlatter and Baker
// reference (2) : About the Weather Station
//
double dewPoint(double celsius, double humidity)
{
// (1) Saturation Vapor Pressure = ESGG(T)
double RATIO = 373.15 / (273.15 + celsius);
double RHS = -7.90298 * (RATIO - 1);
RHS += 5.02808 * log10(RATIO);
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / RATIO))) - 1);
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1);
RHS += log10(1013.246);

// factor -3 is to adjust units - Vapor Pressure SVP * humidity
double VP = pow(10, RHS - 3) * humidity;

// (2) DEWPOINT = F(Vapor Pressure)
double T = log(VP / 0.61078); // temp var
return (241.88 * T) / (17.558 - T);
}

Acuario is offline   Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 04:00 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Ad Management by RedTyger
Inactive Reminders By Icora Web Design