EcoRenovator

EcoRenovator (https://ecorenovator.org/forum/index.php)
-   Geothermal & Heat Pumps (https://ecorenovator.org/forum/forumdisplay.php?f=10)
-   -   Home made heat pump controller (https://ecorenovator.org/forum/showthread.php?t=4629)

Acuario 11-16-15 01:20 AM

Home made heat pump controller
 
I need a heat pump controller for a flat that I have in the nearby city. I have installed under floor heating as it is probably the most comfortable and efficient heating there is (and it can also work for cooling).

There are commercial heat pumps around but I have enough experience with my own home setup to be able to build my own unit.

One of the 'problems' with building your own whatever is the case and display, especially if it is visible to everyone. I did think about a touch screen LCD display or an OLED display and some buttons but anything I was going to make would look home-made and I wanted something a bit better.

Looking on ebay I found lots of fancy looking thermostats with nice displays and buttons but nothing that could run both heating and cooling, also there was no way o communicate with these units as they were self contained.

After some hunting I finally came across a thermostat, a BAC-1000, on AliBaba that has a RS485 interface so I can communicate with it, works for heating and cooling, looks good and is touch control. It also fits into a standard electrical outlet box - perfect!

http://becaenergy.en.ecplaza.net/6.jpg

One down, several to go.

My initial list of hardware requirements for the controller were:
  • Wall mounted thermostat for user control
  • Sensors for compressor and evaporator
  • Sensors for water flow/return
  • Humidity sensor to avoid dew forming in summer when in cooling
  • Real time clock
  • Relays for compressor (solid state), fan (2 speeds), reversing valve, circulating pump
  • Ethernet interface for status
  • RS485 interface for thermostat
  • Small display for local status information
  • Keyboard for programming

And obviously a processor board to run it all :-)

For many years I have been using pic processors to control my projects and have avoided the Arduinos. My reason has mainly been the lack of range of Arduino boards and the poor support for debugging, there's nothing like single stepping through code to see where you messed up.

I decided to take the plunge though and bought myself a UNO, loaded up the Arduino IDE and wrote a few lines of code. All worked well however the IDE is very basic so I went looking for alternatives. I use Visual Studio extensively so I was really happy to find someone had developed an plugin for VS for programming Arduinos - Visual Micro Arduino Plugin (new version, simpler & more powerful). The plugin works well and is being actively supported, it also includes a debugger that can be activated and although it's not single step it can help - I've not yet tried out much of it's functionality.

The great thing about the Arduino is the number of libraries there are available for almost any peripheral you can think of. Most work first time however some need a bit of coaxing into life. Compared to the pic it's luxury.

I started putting together some modules for the controller using the various libraries I found and then ran out of memory space on the UNO, it really is very limited. So I moved up to a Mega 2560. The great thing is all the code works just the same.

Anyone who programs these small boards will know the typical program structure is a single loop that contains all the logic and calls to read the various sensors, write to displays, set relays etc. All good if you have a fairly simple program with few peripherals but this project quickly exceeded this, especially with the Ethernet interface which runs a web server for (initially) displaying status information.

The solution to this problem is a 'Real time operating system' (RTOS). This allows you to have what seems like separate programs all running at the same time on the same processor, just like your pc or mac. Each 'program' runs in its own thread and has its own memory allocated to it. The individual threads can interact with each other or just run autonomously but they can share the same program variables. The RTOS I found and use is called NilRTOS, it is very small and has been ported to the Arduino.

So, for example, a thread running to read temperature sensors can set values for variables 'sensor1, sensor2, sensor3' etc. and a thread that is running completely separately that displays stuff on an lcd can display these values.

Using this approach allows the ever more complex single thread program to be split up into individual mini 'programs' or threads, each one independently running a peripheral and with the main program loop now free to handle the control of the heat pump without worrying about what is going on around it with the lcd, the Ethernet, reading sensors etc. Hopefully this is clear!

The threads I have in my controller are:
  • Temperature sensors (digital ones - ds18b20)
  • Temperature sensors (analogue 10K NTC thermistors)
  • Display (rather than LCD I use a small OLED display)
  • RS485 to communicate with the BAC thermostat
  • Ethernet for the web server
  • Humidity sensor - I use a DHT11 temperature/humidity sensor
  • Defrost algorythm

... to be continued

Acuario

TechShop 11-16-15 02:29 AM

Cool to see other guys thinking about tackling this item too. I'm interested to see what you come up with.

I'm working on a similar project to control the GSHP I assembled this summer. I just posted some (admittedly sloppy) Arduino code that I'm using for datalogging the DS18B20's. It's a little bloated for the 328P chip, but runs fine on chips with more memory.

The direct control of the GSHP is working now using two OMRON ZEN PLC units with some thermister / voltage divider circuits. I plan to indirectly control everything with a Raspberry Pi / Raspian / Apache web server.

Have you considered using a tablet or cheap smart phone with Android or Linux and a custom UI as a thermostat interface? I am considering something in that direction.

Acuario 11-16-15 12:18 PM

I did consider various options for the thermostat but at 36.90 euros including shipping on AliExpress it would have been difficult to find or build something comparable.

Acuario 11-16-15 01:04 PM

2 Attachment(s)
The benefit of having each part of the program running in its own thread is that it becomes somewhat easier to debug the program as you can disable individual threads and concentrate on the bit you are writing.

A couple of the oddities I found that didn't occur with a single threaded program is that many of the Arduino string class functions failed to work.
Sorry if this gets technical.. the reason appears to be that calls to malloc do not allocate memory as they should do, the return is a null pointer and so any function that relies on the dynamically allocated memory fails. Sadly this means allocating fixed length strings to hold, for example, http requests.

The other oddity was that I started getting odd results when reading analogue peripherals. Initially I was using a 4 button keypad using a single analogue input and a resistor divider to give 4 distinct values (a neat way to use a single pin for a simple keyboard). I was also reading NTC thermistors in a separate thread. Individually the peripherals worked fine but when running in threads they failed. Finally I realised that it was because the threads would clash and both try to read analogue voltages at the same time. It turns out that although the Arduino has multiple analogue pins it only has a single adc (analogue to digital converter) so can only do a single conversion at a time. A flag to indicate an analogue read is in progress that stops any other analogue read taking place sorted that problem out.

Initially I was going to use a keyboard to program the various parameters for such things as defrost temperature (start/stop), defrost cycle time, maximum temperature etc. but it started to become very complex and with 4 buttons and a small display it was getting over complex.

As the controller has an inbuilt web server for displaying status information it seemed the easiest option to use the same server to set up all the settings. A 'few' lines of code, a bit of html and response processing and now programming the controller is dead easy.
http://ecorenovator.org/forum/attach...1&d=1447700433

The status page looks like this:
http://ecorenovator.org/forum/attach...1&d=1447700433

The web pages aren't pretty but they are functional. You could 'beautify' them with some css but the Arduino itself isn't really up to serving up complex web pages, css files etc.

The status page auto updates every 15 seconds using a technique called ajax whereby it only receives the values and then fills in all the spaces on the page with the current values - this saves sending the whole web page each time a refresh is requested.

jeff5may 11-16-15 03:52 PM

Cheater, cheater, pumpkin eater! You've offloaded the work onto other devices! You're supposed to strain the dev board! Just kidding.

In the general purpose controller discussion, many of the contributors could not grasp the idea that the dev board (either uno or mega) should not need to do everything itself. The wish list of functions became so enormous that both ac_hacker and I could not reel in the diversity of devices that were built by many for their own specific sets of needs. My iteration is very basic: it reads a single onewire thermometer and functions as a thermostat with auto changeover. It would be AWESOME if it could speak WiFi, Ethernet or USB with a more intelligent sand-based lifeform. Having a defrost control built into the uno is a primary goal for a unit I am pondering right now.

Ok, so your arduino is acting as an Ethernet to rs485 bridge and a humidity sensor? Are you still using an rtos or did you switch back to wiring/python/c? What else does it do? I'm very interested.

TechShop 11-16-15 07:13 PM

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.

SDMCF 11-16-15 11:56 PM

Quote:

Originally Posted by TechShop (Post 47996)
these little PLCs can be found in the same price range as a basic Arduino.

Could you link to an example please?

Acuario 11-17-15 12:00 AM

2 Attachment(s)
Quote:

Originally Posted by jeff5may (Post 47994)
The wish list of functions became so enormous that both ac_hacker and I could not reel in the diversity of devices that were built by many for their own specific sets of needs.

When I was searching for something already written I came across a couple of examples of this problem. Some had incredible lists of proposed functions only to then die quietly when the task of writing the code began. Breaking the code into distinct modules that encapsulate specific functionality is a great way to solve the problem - just include the bits you need and ignore the rest. Using this approach you could design/build a general purpose controller.

Quote:

Originally Posted by jeff5may (Post 47994)
Ok, so your arduino is acting as an Ethernet to rs485 bridge and a humidity sensor? Are you still using an rtos or did you switch back to wiring/python/c? .

Absolutely still using the rtos as it makes the whole thing fit together as nice easy building blocks. Everything is written in c and using various Arduino libraries I found on the internet to run the core peripherals.

Quote:

Originally Posted by jeff5may (Post 47994)
What else does it do? I'm very interested.

I have included a start up delay and power fail restart procedure. Basically it regularly saves the current time (as it has a real time clock) and in event of a power off/on (or power fail) delays the compressor start-up if necessary. It also restores the system to its last known operating state.

Using the web setup it allows the system to be remotely controlled, overriding the digital thermostat and setting the thermostats settings. Operating mode (heat/cool), on/off, desired temperature can all be set via the web page. With the correct settings in my router I can access this from anywhere making it an iot (internet of things) device.

Here are a couple of (not very pretty!) pictures, one of the whole controller mounted on a board and one of the oled status display. The only bit I needed to actually build was a small board (just visible at the bottom left) that acts as a break-out board for various connections. It has the balance resistors for the NTC thermistors, screw connectors for the ds18b20 temperature sensors, the pins for the dht22 humidity sensor and several rows of common pins for vcc, ground, I2C. The small red board with green terminals is a RS485 to serial converter, the rest of the bits should be easily identified.

http://ecorenovator.org/forum/attach...1&d=1447739914

http://ecorenovator.org/forum/attach...1&d=1447739914

Acuario

Acuario 11-17-15 12:19 AM

1 Attachment(s)
Quote:

Originally Posted by jeff5may (Post 47994)
It would be AWESOME if it could speak WiFi, Ethernet or USB with a more intelligent sand-based lifeform.

Adding Ethernet is really easy. Either use an Ethernet shield with a Wiznet W5100 or an ENC28J60 board. There is also a mini Ethernet board with a W5100 on it. I have another mini-project running that has 4 ntc thermistors on it using a nano. Originally I used the ENC board but I changed to a mini W5100 board as the ENC used quite a bit of program memory and (as usual!) I was running out of memory. This project also serves up a very basic web page with the temperatures of the 4 sensors.

You could add WiFi quite easily too. Check out the ESP8266 module which has built in WiFi plus lots more.

http://ecorenovator.org/forum/attach...1&d=1447740861

Acuario

TechShop 11-17-15 01:04 AM

1 Attachment(s)
Quote:

Originally Posted by Acuario (Post 47984)
Here are a couple of (not very pretty!) pictures, one of the whole controller mounted on a board and one of the oled status display.

Keep in mind your audience in a place like this. There's a special beauty in a pile of hardware assembled and functioning as it's creator intended. Either way, your bundle of jumpers and components approach seems familiar to me and similar to my prototype. :o

I presume the big 40 Amp SSR is meant for your compressor?

RJ-11 (telephone) jacks and plugs seem to be the ticket for the sensors. You can use inexpensive telephone wire for long sensor runs too.


Quote:

Originally Posted by SDMCF (Post 47999)
Could you link to an example please?

12v - 24v (low voltage input and power version) Good for HVAC
Omron CPU Unit Zen 10C1DR D V2 | eBay

100v - 240v (line level input and power version)
Omron Zen 10C3AR A V2 Smart Relay AC100 240V | eBay

I have attached the manual for the ZEN here as well.

jeff5may 11-17-15 07:15 AM

Quote:

Originally Posted by TechShop (Post 47996)
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.
http://i1326.photobucket.com/albums/...psrjfw3oeh.jpg
The board in its old home

http://i1326.photobucket.com/albums/...psugxbkcww.jpg
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.

TechShop 11-17-15 10:34 AM

Quote:

Originally Posted by jeff5may (Post 48004)
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.

jeff5may 11-17-15 03:29 PM

Quote:

Originally Posted by TechShop (Post 48005)
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.
https://www.ptacunits.com/wp-content...5252268816.jpg

Acuario 01-03-16 02:17 PM

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 01-03-16 02:19 PM

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 01-03-16 02:20 PM

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 01-03-16 02:21 PM

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 01-03-16 02:22 PM

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 01-03-16 02:23 PM

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 01-03-16 02:24 PM

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 01-03-16 02:25 PM

7. DS18B20.ino - you will need to put in your own addresses for your sensors
#include <OneWire.h>
#include <DallasTemperature.h>

// Initialize OneWire
OneWire one_wire(DS18B20);
DallasTemperature sensors(&one_wire);

// OneWire Sensor addresses
//28 71 94 2B 06 00 00 2C
//28 81 15 31 04 00 00 34
DeviceAddress compressor = { 0x28, 0x71, 0x94, 0x2B, 0x6, 0x0, 0x0, 0x2C };
DeviceAddress evaporator = { 0x28, 0x81, 0x15, 0x31, 0x4, 0x0, 0x0, 0x34 };

NIL_THREAD(threadReadSensors, arg) {
while (TRUE) {
//Serial.println("Read DS18B20 sensors");
sensors.requestTemperatures();
tempCompressor = sensors.getTempC(compressor);
tempEvaporator = sensors.getTempC(evaporator);
//Serial.println(tempCompressor);
//Serial.println(tempEvaporator);
if (tempCompressor != lastTempCompressor) {
lastTempCompressor = tempCompressor;
}
if (tempEvaporator != lastTempEvaporator) {
lastTempEvaporator = tempEvaporator;
}
// Sleep for 10 seconds.
nilThdSleepSeconds(10);
}
}

Acuario 01-03-16 02:25 PM

8. DS1307.ino
#include <Time.h>
#include <DS1307RTC.h>

const char *monthName[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

uint64_t makeTimeMilli(tmElements_t tm) {
return ((uint64_t)makeTime(tm) * 1000);
}

void writeTime(tmElements_t tmw) {

// Configure the RTC
RTC.write(tmw);
}

void readTime() {

if (RTC.read(tm)) {
//Serial.print("Ok, Time = ");
//print2digits(tm.Hour);
//Serial.write(':');
//print2digits(tm.Minute);
//Serial.write(':');
//print2digits(tm.Second);
//Serial.print(", Date (D/M/Y) = ");
//Serial.print(tm.Day);
//Serial.write('/');
//Serial.print(tm.Month);
//Serial.write('/');
//Serial.print(tmYearToCalendar(tm.Year));
//Serial.println();
}
else {
if (RTC.chipPresent()) {
Serial.println("The DS1307 is stopped. Please run the SetTime");
Serial.println("example to initialize the time and begin running.");
Serial.println();
// get the date and time the compiler was run
if (getDate(__DATE__) && getTime(__TIME__)) {
// and configure the RTC with this info
RTC.write(tm);
}
}
else {
Serial.println("DS1307 read error! Please check the circuitry.");
Serial.println();
}
}
}


void print2digits(int number) {
if (number >= 0 && number < 10) {
Serial.write('0');
}
Serial.print(number);
}

bool getTime(const char *str)
{
int Hour, Min, Sec;

if (sscanf(str, "%d:%d:%d", &Hour, &Min, &Sec) != 3) return false;
tm.Hour = Hour;
tm.Minute = Min;
tm.Second = Sec;
return true;
}


bool getDate(const char *str)
{
char Month[12];
int Day, Year;
uint8_t monthIndex;

if (sscanf(str, "%s %d %d", Month, &Day, &Year) != 3) return false;
for (monthIndex = 0; monthIndex < 12; monthIndex++) {
if (strcmp(Month, monthName[monthIndex]) == 0) break;
}
if (monthIndex >= 12) return false;
tm.Day = Day;
tm.Month = monthIndex + 1;
tm.Year = CalendarYrToTm(Year);
return true;
}

Acuario 01-03-16 02:27 PM

9. Ethernet.ino (part 1)
//For ethernet
#include <SPI.h>
#include <Ethernet.h>
#include "HeatpumpController.h"

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0x00, 0xAD, 0xBE, 0xEF, 0xFD, 0x13 };
byte ip[] = { 192, 168, 0, 190 }; // ip in lan (that's what you need to use in your browser.
byte gateway[] = { 192, 168, 0, 1 }; // internet access via router
byte subnet[] = { 255, 255, 255, 0 }; //subnet mask
#define ETHPORT 8081
EthernetServer server(ETHPORT); // Initialize the Ethernet server library

void returnSetupPage(EthernetClient cl, bool save);
void saveSettings(char * buf);
void returnWebPage(EthernetClient cl);
void XML_response(EthernetClient cl);
void StrClear(char *str, char length);
char StrContains(char *str, char *sfind);

void setupEthernet(void) {
Ethernet.begin(mac, ip);
server.begin();

DEBUG_PRINT("server is at ");
DEBUG_PRINTLN(Ethernet.localIP());
}

NIL_THREAD(threadEthernet, arg) { // listen for incoming clients
#define REQ_BUF_SZ 120 // size of buffer used to capture HTTP requests
char HTTP_req[REQ_BUF_SZ] = { 0 }; // buffered HTTP request stored as null terminated string
char req_index = 0; // index into HTTP_req buffer

while (TRUE) {
EthernetClient client = server.available(); // try to get client

if (client) { // got client?
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) { // client data available to read
char c = client.read(); // read 1 byte (character) from client buffer first part of HTTP request in HTTP_req array (string)
// leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
if (req_index < (REQ_BUF_SZ - 1)) {
HTTP_req[req_index] = c; // save HTTP request character
req_index++;
}
// last line of client request is blank and ends with \n
// respond to client only after last line received
if (c == '\n' && currentLineIsBlank) {
// display received HTTP request on serial port
//DEBUG_PRINTST("Received HTTP:-%s", HTTP_req);

// send a standard http response header
client.println("HTTP/1.1 200 OK");
// remainder of header follows below, depending on if web page or XML page is requested
// Ajax request - send XML file
if (StrContains(HTTP_req, "ajax_inputs")) {
// send rest of HTTP header
client.println("Content-Type: text/xml");
client.println("Connection: keep-alive");
client.println();
// send XML file containing input states
XML_response(client);
}
else if (StrContains(HTTP_req, "setup")) {
returnSetupPage(client, FALSE);
}
else if (StrContains(HTTP_req, "savesettings")) {
saveSettings(HTTP_req);
returnSetupPage(client, TRUE);
}
else if (StrContains(HTTP_req, "settime")) {
setTime(HTTP_req);
returnSetupPage(client, TRUE);
}
else if (StrContains(HTTP_req, "defrostOn")) {
Serial.println("START defrost forced");
defrostFlag = TRUE;
returnSetupPage(client, FALSE);
}
else if (StrContains(HTTP_req, "defrostOff")) {
Serial.println("STOP defrost forced");
defrostFlag = FALSE;
returnSetupPage(client, FALSE);
}
else { // web page request send rest of HTTP header
returnWebPage(client);
}
// reset buffer index and all buffer elements to 0
req_index = 0;
StrClear(HTTP_req, REQ_BUF_SZ);
break;
}
// every line of text received from the client ends with \r\n
if (c == '\n') {
// last character on line of received text
// starting new line with next character read
currentLineIsBlank = true;
}
else if (c != '\r') {
// a text character was received from client
currentLineIsBlank = false;
}
} // end if (client.available())
} // end while (client.connected())
nilThdSleepSeconds(1); // give the web browser time to receive the data
client.stop(); // close the connection
} // end if (client)

// Sleep
nilThdSleepSeconds(10);
}
}

Acuario 01-03-16 02:29 PM

10. Ethernet.ino (part 2)
//Save settings received from web page
//GET /savesettings?defTime=5&defTemp=-2 HTTP/1.1
void saveSettings(char * buf) {
char str1[150];
char *s = str1, *t = NULL;
char sbuf[50];
char keyname[10];
int val;
bool res;

str1[0] = '\0';
const char *start = strchr(buf, '?') + 1;
strncat(str1, start, strcspn(start, " "));
DEBUG_PRINTST("Extracted %s\n", str1);

while ((t = strtok(s, "&")) != NULL) {
s = NULL;
sprintf(sbuf, "%s", t);

sscanf(sbuf, "%32[^=]=%d", keyname, &val);
SerialPrint2("Name=%s, value=%d\n", keyname, val);

//Running time of defrost cycle
if (StrContains(keyname, "defTime")) {
if (defTime != val)
EEPROM_write_byte(DEFROSTTIME, val);
defTime = val;
}

//Temperature of condensor to start defrost cycle
else if (StrContains(keyname, "defStartT")) {
if (defTemp != val)
EEPROM_write_byte(DEFROSTTEMP, val + DEFROST_OFFSET);
defTemp = val;
}
else if (StrContains(keyname, "hys")) {
if (hysterisis != val)
EEPROM_write_byte(HYSTERISIS, val);
hysterisis = val;
}

//Temperature to stop defrost. Overrides timer
else if (StrContains(keyname, "defStopT")) {
if (defUpperTemp != val)
EEPROM_write_byte(DEFROSTMAXTEMP, val);
defUpperTemp = val;
}

//Desired room temperature
else if (StrContains(keyname, "roomTemp")) {
if (desiredRoomTemp != val)
{
res = sendBAC1000Command(TEMPSETTING, 0, 1, 0, 0, val * 2, 0);
bacSetTemp = val;
desiredRoomTemp = val;
EEPROM_write_byte(DESIREDROOMTEMP, desiredRoomTemp);
}
}

//Maximum water temperature
else if (StrContains(keyname, "waterTemp")) {
if (maxWaterTemp != val)
EEPROM_write_byte(MAXWATERTEMP, val);
maxWaterTemp = val;
}

//Min time between defrost cycles
else if (StrContains(keyname, "defCycle")) {
if (timeBetweenDefrost != val)
EEPROM_write_byte(TIMEBETWEENDEFROST, val);
timeBetweenDefrost = val;
}

//System power state - on or off
else if (StrContains(keyname, "sysState")) {
if (operatingState != val)
{
res = sendBAC1000Command(POWERONOFF, 0, 1, (val << 4), 0, 0, 0);
operatingState = val;
powerStateBAC = val;
EEPROM_write_byte(LASTSYSTEMSTATE, operatingState);
}
}

//System operating mode heat/cool
else if (StrContains(keyname, "sysMode")) {
if (operatingMode != val)
{
res = sendBAC1000Command(MODESETTING, 0, 1, (val << 5), 0, 0, 0);
operatingMode = val;
operatingModeBAC = val;
EEPROM_write_byte(LASTSYSTEMMODE, operatingState);
}
}
}
}

//Set RTC time
void setTime(char * buf) {
tmElements_t tm;
char str1[150];
char *s = str1, *t = NULL;
char sbuf[50];
char keyname[10];
int val;
bool res;

str1[0] = '\0';
const char *start = strchr(buf, '?') + 1;
strncat(str1, start, strcspn(start, " "));
DEBUG_PRINTST("Extracted %s\n", str1);

while ((t = strtok(s, "&")) != NULL) {
s = NULL;
sprintf(sbuf, "%s", t);

sscanf(sbuf, "%32[^=]=%d", keyname, &val);
DEBUG_PRINTST("Name=%s, value=%d\n", keyname, val);

if (StrContains(keyname, "hour")) {
tm.Hour = val;
}
else if (StrContains(keyname, "minute")) {
tm.Minute = val;
}
else if (StrContains(keyname, "day")) {
tm.Day = val;
}
else if (StrContains(keyname, "month")) {
tm.Month = val;
}
else if (StrContains(keyname, "year")) {
tm.Year = val;
}
RTC.write(tm);
}
}

void returnSetupPage(EthernetClient cl, bool save) {
DEBUG_PRINTLN("Setup page");

// send a standard http response header
cl.println(F("Content-Type: text/html"));
cl.println(F("Connection: close")); // the connection will be closed after completion of the response
cl.println();
cl.println(F("<!DOCTYPE HTML>"));
cl.println(F("<html>"));
cl.println(F("<!DOCTYPE HTML><html><head>"));
cl.println(F("<link rel='stylesheet' type='text/css' href='http://www.tortosaforum.com/heatpump/heatpumpcss.css' />"));
cl.println(F("<title>Heat pump - SETUP</title></head><body>"));

cl.println(F("<H1>Heat Pump Controller - SETUP</H1>"));
//cl.println(F("<form action='savesettings' method='post'><table border='1' width='50%'>"));
cl.println(F("<form action='savesettings'><table border='1' width='30%'>"));
cl.println(F("<tr><th>Setting</th><th>Value</th></tr>"));
cl.print(F("<tr><td>Defrost run time</td><td><input type='number' name='defTime' min='1' max='10' value='"));
cl.print(defTime);
cl.println(F("'></td></tr>"));
cl.print(F("<tr><td>Defrost start temperature</td><td><input type='number' name='defStartT' min='-15' max='5' value='"));
cl.print(defTemp);
cl.println(F("'></td></tr>"));
cl.print(F("<tr><td>Defrost stop temperature</td><td><input type='number' name='defStopT' min='0' max='15' value='"));
cl.print(defUpperTemp);
cl.println(F("'></td></tr>"));
cl.print(F("<tr><td>Time between defrost cycles</td><td><input type='number' name='defCycle' min='15' max='60' value='"));
cl.print(timeBetweenDefrost);
cl.println(F("'></td></tr>"));
cl.print(F("<tr><td>Max water temperature</td><td><input type='number' name='waterTemp' min='1' max='40' value='"));
cl.print(maxWaterTemp);
cl.println(F("'></td></tr>"));
cl.print(F("<tr><td>Hysterisis</td><td><input type='number' name='hys' min='1' max='15' value='"));
cl.print(hysterisis);
cl.println(F("'></td></tr>"));

cl.print(F("<tr><td>Desired room temperature</td><td><input type='number' name='roomTemp' min='1' max='40' value='"));
cl.print(bacSetTemp);
cl.println(F("'></td></tr>"));

cl.print(F("<tr><td>System state</td><td><input type='radio' name='sysState' value='1'"));
if (operatingState == TRUE)
cl.println(F(" checked>ON<input type='radio' name='sysState' value='0'>Off</td></tr>"));
else
cl.println(F(">On<input type='radio' name='sysState' value='0' checked>Off</td></tr>"));

cl.println(F("<tr><td>Mode</td><td><input type='radio' name='sysMode' value='0'"));
if (operatingModeBAC == 0)
cl.println(F(" checked>Cool<input type='radio' name='sysMode' value='1'>Heat</td></tr>"));
else
cl.println(F(">Cool<input type='radio' name='sysMode' value='1' checked>Heat</td></tr>"));
cl.println(F("</table><br><input type='submit' value='Save'></form>"));
cl.println(F("<BR><BR>"));

//Table to set time/date
cl.println(F("<form action='settime'><table border='1' width='30%'>"));
cl.println(F("<tr><th>Setting</th><th>Value</th></tr>"));
cl.print(F("<tr><td>Hour</td><td><input type='number' name='hour' id='hour' min='1' max='23'></td></tr>"));
cl.print(F("<tr><td>Minute</td><td><input type='number' name='minute' id='minute' min='1' max='59'></td></tr>"));
cl.print(F("<tr><td>Day</td><td><input type='number' name='day' id='day' min='1' max='31'></td></tr>"));
cl.print(F("<tr><td>Month</td><td><input type='number' name='month' id='month' min='1' max='12'></td></tr>"));
cl.print(F("<tr><td>Year</td><td><input type='number' name='year' id='year' min='2015' max='2030'></td></tr>"));
cl.println(F("</table><br><input type='submit' value='Set'></form>"));
cl.println(F("<BR><BR>"));
cl.println(F("<script>"));
cl.println(F("var d = new Date();"));
cl.println(F("document.getElementById('hour').valu e = d.getHours();"));
cl.println(F("document.getElementById('minute').va lue = d.getMinutes();"));
cl.println(F("document.getElementById('day').value = d.getDate();"));
cl.println(F("var month = d.getMonth() + 1;"));
cl.println(F("document.getElementById('month').val ue = month;"));
cl.println(F("document.getElementById('year').valu e = d.getFullYear();"));
cl.println(F("</script>"));

cl.println(F("<a href=\"/?defrostOn\"\">Start Defrost</a>"));
cl.println(F("<a href=\"/?defrostOff\"\">Stop Defrost</a>"));

if (save == TRUE)
cl.println(F("<script>alert('Settings saved');</script>"));
if(defrostFlag == TRUE)
cl.println(F("<p>Defrost running..</p>"));
else
cl.println(F("<p>Defrost not running..</p>"));
cl.println(F("</body></html>"));
}

Acuario 01-03-16 02:30 PM

11. Ethernet.ino (part 3)
//Return initial web page - then updated by AJAX XML
void returnWebPage(EthernetClient cl) {

cl.println(F("Content-Type: text/html"));
cl.println(F("Connection: keep-alive"));
cl.println();
cl.println(F("<!DOCTYPE html>"));
cl.println(F("<html><head>"));
cl.println(F("<link rel='stylesheet' type='text/css' href='http://www.tortosaforum.com/heatpump/heatpumpcss.css' />"));
cl.println(F("<title>Heat Pump Controller status</title>"));
cl.println(F("<script>"));
cl.println(F("function GetArduinoInputs(){"));
cl.println(F("nocache = '&nocache=' + Math.random() * 1000000;"));
cl.println(F("var request = new XMLHttpRequest();"));
cl.println(F("request.onreadystatechange = function(){"));
cl.println(F("if (this.readyState == 4) {"));
cl.println(F("if (this.status == 200) {"));
cl.println(F("if (this.responseXML != null) {"));

cl.println(F("document.getElementById('roomTemp'). innerHTML=this.responseXML.getElementsByTagName('r oomTemp')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('bacSetTemp' ).innerHTML=this.responseXML.getElementsByTagName( 'bacSetTemp')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('compressor' ).innerHTML=this.responseXML.getElementsByTagName( 'compressor')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('evaporator' ).innerHTML=this.responseXML.getElementsByTagName( 'evaporator')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('flow').inne rHTML=this.responseXML.getElementsByTagName('flow' )[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('return').in nerHTML=this.responseXML.getElementsByTagName('ret urn')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('humidity'). innerHTML=this.responseXML.getElementsByTagName('h umidity')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('dewpoint'). innerHTML=this.responseXML.getElementsByTagName('d ewpoint')[0].childNodes[0].nodeValue;"));

cl.println(F("document.getElementById('bacMode').i nnerHTML=this.responseXML.getElementsByTagName('ba cMode')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('bacPowerSta te').innerHTML=this.responseXML.getElementsByTagNa me('bacPowerState')[0].childNodes[0].nodeValue;"));

cl.println(F("document.getElementById('defrost').i nnerHTML=this.responseXML.getElementsByTagName('de frost')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('systemTime' ).innerHTML=this.responseXML.getElementsByTagName( 'systemTime')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('defrostStep ').innerHTML=this.responseXML.getElementsByTagName ('defrostStep')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('hysterisis' ).innerHTML=this.responseXML.getElementsByTagName( 'hysterisis')[0].childNodes[0].nodeValue;"));

cl.println(F("document.getElementById('compStat'). innerHTML=this.responseXML.getElementsByTagName('c ompStat')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('fanLow').in nerHTML=this.responseXML.getElementsByTagName('fan Low')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('fanHigh').i nnerHTML=this.responseXML.getElementsByTagName('fa nHigh')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('valve').inn erHTML=this.responseXML.getElementsByTagName('valv e')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('pump').inne rHTML=this.responseXML.getElementsByTagName('pump' )[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('state').inn erHTML=this.responseXML.getElementsByTagName('stat e')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('maxWaterTem p').innerHTML=this.responseXML.getElementsByTagNam e('maxWaterTemp')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('ambient').i nnerHTML=this.responseXML.getElementsByTagName('am bient')[0].childNodes[0].nodeValue;"));
cl.println(F("document.getElementById('s4').innerH TML=this.responseXML.getElementsByTagName('s4')[0].childNodes[0].nodeValue;"));

cl.println(F("}}}}"));
cl.println(F("request.open('GET', 'ajax_inputs' + nocache, true);"));
cl.println(F("request.send(null);"));
cl.println(F("setTimeout('GetArduinoInputs()', 5000);}"));
cl.println(F("</script></head>"));
cl.println(F("<body onload='GetArduinoInputs()'>"));
cl.println(F("<h1>Heat Pump Controller status</h1>"));

//////////////////////////////////////////////////////////////////////////////////////////////
cl.println(F("<table border = '1' width = '30%'>"));
cl.println(F("<tr><th>Parameter</th><th>Reading</th><th>Setting</th></tr>"));
cl.print(F("<tr><td>Room temperature</td><td><span id='roomTemp'>...</span></td><td><span id='bacSetTemp'>...</span></td></tr>"));
cl.print(F("<tr><td>Flow temperature</td><td><span id='flow'>...</span></td><td><span id='maxWaterTemp'>...</span></td></tr>"));
cl.print(F("<tr><td>Return temperature</td><td><span id='return'>...</span></td><td>&nbsp;</td></tr>"));
cl.print(F("<tr><td>Evaporator</td><td><span id='evaporator'>...</span></td><td>"));
cl.print(defTemp);
cl.print(F("</td></tr>"));
cl.print(F("<tr><td>Compressor</td><td><span id='compressor'>...</span></td><td>&nbsp;</td></tr>"));
cl.println(F("</table>"));

//////////////////////////////////////////////////////////////////////////////////////////////
cl.println(F("<table border = '1' width = '30%'>"));
cl.println(F("<tr><th>Relay</th><th>Status</th></tr>"));
cl.print(F("<tr><td>Compressor</td><td><span id='compStat'>...</span></td></tr>"));
cl.print(F("<tr><td>Fan low</td><td><span id='fanLow'>...</span></td></tr>"));
cl.print(F("<tr><td>Fan high</td><td><span id='fanHigh'>...</span></td></tr>"));
cl.print(F("<tr><td>3W valve</td><td><span id='valve'>...</span></td></tr>"));
cl.print(F("<tr><td>Circulating</td><td><span id='pump'>...</span></td></tr>"));
cl.println(F("</table>"));

cl.println(F("<p>State: <span id='state'>...</span></p>"));

cl.println(F("<p>Ambient: <span id='ambient'>...</span></p>"));
cl.println(F("<p>S4: <span id='s4'>...</span></p>"));
cl.println(F("<p>Humidity: <span id='humidity'>...</span></p>"));
cl.println(F("<p>Dew point: <span id='dewpoint'>...</span></p>"));
cl.println(F("<p>Hysterisis: <span id='hysterisis'>...</span></p>"));

cl.println(F("<p>BAC Mode: <span id='bacMode'>...</span></p>"));
cl.println(F("<p>BAC Power: <span id='bacPowerState'>...</span></p>"));
cl.println(F("<p>Defrost running: <span id='defrost'>...</span></p>"));
cl.println(F("<p>Defrost step: <span id='defrostStep'>...</span></p>"));
cl.println(F("<p></p><p>System time: <span id='systemTime'>...</span></p>"));
cl.println(F("</body></html>"));
}

Acuario 01-03-16 02:30 PM

12. Ethernet.ino (part 4)

// send the XML file with status
void XML_response(EthernetClient cl)
{
char buf[20];

cl.print(F("<?xml version = \"1.0\" ?>"));
cl.print(F("<inputs>"));
cl.print(F("<systemTime>"));
sprintf(buf, "%02d:%02d:%02d %02d/%02d", tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month);
cl.print(buf);
cl.print(F("</systemTime>"));
cl.print(F("<bacSetTemp>"));
cl.print(bacSetTemp);
cl.print(F("</bacSetTemp>"));
cl.print(F("<maxWaterTemp>"));
cl.print(maxWaterTemp);
cl.print(F("</maxWaterTemp>"));
cl.print(F("<roomTemp>"));
cl.print(bacRoomTemp);
cl.print(F("</roomTemp>"));
cl.print(F("<compressor>"));
cl.print(tempCompressor);
cl.print(F("</compressor>"));
cl.print(F("<flow>"));
cl.print(flowTemp);
cl.print(F("</flow>"));
cl.print(F("<return>"));
cl.print(returnTemp);
cl.print(F("</return>"));
cl.print(F("<evaporator>"));
cl.print(tempEvaporator);
cl.print(F("</evaporator>"));
cl.print(F("<humidity>"));
cl.print(humidity);
cl.print(F("</humidity>"));
cl.print(F("<dewpoint>"));
cl.print(dewPointTemp);
cl.print(F("</dewpoint>"));
cl.print(F("<bacPowerState>"));
sprintf(buf, "%s", powerStateBAC == FALSE ? sOff : sOn);
cl.print(buf);
cl.print(F("</bacPowerState>"));
cl.print(F("<bacMode>"));
sprintf(buf, "%s", mode[operatingModeBAC]);
cl.print(buf);
cl.print(F("</bacMode>"));
cl.print(F("<defrost>"));
sprintf(buf, "%s", defrostFlag == TRUE ? "YES" : "NO");
cl.print(buf);
cl.print(F("</defrost>"));

cl.print(F("<defrostStep>"));
cl.print(defStep);
cl.print(F("</defrostStep>"));
cl.print(F("<hysterisis>"));
cl.print(hysterisis);
cl.print(F("</hysterisis>"));
cl.print(F("<defCycle>"));
cl.print(timeBetweenDefrost);
cl.print(F("</defCycle>"));
cl.print(F("<state>"));
cl.print(systemState);
cl.print(F("</state>"));
cl.print(F("<ambient>"));
cl.print(ambientTemp);
cl.print(F("</ambient>"));
cl.print(F("<s4>"));
cl.print(ambientTemp);
cl.print(F("</s4>"));

cl.print(F("<compStat>"));
sprintf(buf, "%s", compStat == OFF ? sOff : sOn);
cl.print(buf);
cl.print(F("</compStat>"));
cl.print(F("<fanLow>"));
sprintf(buf, "%s", fanLow == OFF ? sOff : sOn);
cl.print(buf);
cl.print(F("</fanLow>"));
cl.print(F("<fanHigh>"));
sprintf(buf, "%s", fanHigh == OFF ? sOff : sOn);
cl.print(buf);
cl.print(F("</fanHigh>"));
cl.print(F("<valve>"));
sprintf(buf, "%s", valve == OFF ? sOff : sOn);
cl.print(buf);
cl.print(F("</valve>"));
cl.print(F("<pump>"));
sprintf(buf, "%s", pump == OFF ? sOff : sOn);
cl.print(buf);
cl.print(F("</pump>"));

cl.println(F("</inputs>"));
}


// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
for (int i = 0; i < length; i++) {
str[i] = 0;
}
}

// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
char found = 0;
char index = 0;
char len;

len = strlen(str);

if (strlen(sfind) > len) {
return 0;
}
while (index < len) {
if (str[index] == sfind[found]) {
found++;
if (strlen(sfind) == found) {
return 1;
}
}
else {
found = 0;
}
index++;
}
return 0;
}

Acuario 01-03-16 02:32 PM

13. NTCThermistor.ino (still sort of in development/test)

///////////////////////////////////////////////////////////////////////////////////////////////////
// NTC Thermistor
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <math.h>




// enumerating 3 major temperature scales
enum {
T_KELVIN = 0,
T_CELSIUS,
T_FAHRENHEIT
};

// manufacturer data for episco k164 10k thermistor
// simply delete this if you don't need it
// or use this idea to define your own thermistors
#define EPISCO_K164_10k 4300.0f,298.15f,10000.0f // B,T0,R0
#define NTCRES 3380.0f,298.15f,10000.0f

NIL_THREAD(threadReadNTC, arg) {
while (TRUE) {

//while (readAnalog == TRUE) {
// nilThdSleepMilliseconds(100);
//}
for (int s = 2; s < 6; s++) {
ntcSensor[s - 2] = int(Thermistor(analogRead(s)));
nilThdSleepMilliseconds(200);
// DEBUG_PRINTST("Sensor %d: %d\n", s, ntcSensor[s - 2]);
}
//for (int s = 0; s < 4; s++) {
// ntcSensor[s] = int(Thermistor(analogRead(s)));
// //ntcSensor[s] = int(Temperature(s, T_CELSIUS, NTCRES, 9850));
// //ntcSensor[s] = thermNTC(s);
// //ntcSensor[s] = tempTable(s);
// nilThdSleepMilliseconds(200);
//}

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

// resistance at 25 degrees C
#define THERMISTORNOMINAL 10000
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 25
// how many samples to take and average, more takes longer but is more 'smooth'
#define NUMSAMPLES 5
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 3950
// the value of the 'other' resistor
#define SERIESRESISTOR 10500

int samples[NUMSAMPLES];

float thermNTC(int pin) {
uint8_t i;
float average;

// take N samples in a row, with a slight delay
for (i = 0; i < NUMSAMPLES; i++) {
samples[i] = analogRead(pin);
delay(10);
}

// average all the samples out
average = 0;
for (i = 0; i < NUMSAMPLES; i++) {
average += samples[i];
}
average /= NUMSAMPLES;
Serial.println(average);
// convert the value to resistance
average = 1023 / average - 1;
average = SERIESRESISTOR / average;

float steinhart;
steinhart = average / THERMISTORNOMINAL; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // convert to C
return(steinhart);
}


// Temperature function outputs float , the actual
// temperature
// Temperature function inputs
// 1.AnalogInputNumber - analog input to read from
// 2.OuputUnit - output in celsius, kelvin or fahrenheit
// 3.Thermistor B parameter - found in datasheet
// 4.Manufacturer T0 parameter - found in datasheet (kelvin)
// 5. Manufacturer R0 parameter - found in datasheet (ohms)
// 6. Your balance resistor resistance in ohms

float Temperature(int AnalogInputNumber, int OutputUnit, float B, float T0, float R0, float R_Balance)
{
float R, T;

// R=1024.0f*R_Balance/float(analogRead(AnalogInputNumber)))-R_Balance;
R = R_Balance*(1024.0f / float(analogRead(AnalogInputNumber)) - 1);

T = 1.0f / (1.0f / T0 + (1.0f / B)*log(R / R0));

switch (OutputUnit) {
case T_CELSIUS:
T -= 273.15f;
break;
case T_FAHRENHEIT:
T = 9.0f*(T - 273.15f) / 5.0f + 32.0f;
break;
default:
break;
};

return T;
}


///////////////////////////
// Stuff for NTC thermistor
///////////////////////////
// 666 ohms = 100C
//double Thermistor(int RawADC) {
// double Temp;
// Temp = log(9850.0*((1024.0 / RawADC - 1)));
// Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp))* Temp);
// Temp = Temp - 273.15; // Convert Kelvin to Celcius
// return Temp - 18;
//}

double Thermistor(int RawADC) {
double Temp;
Temp = log(10000.0*((1024.0 / RawADC - 1)));
//Temp = log(10000.0 / (1024.0 / RawADC - 1)); // for pull-up configuration
Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp))* Temp);
Temp = Temp - 273.15; // Convert Kelvin to Celcius
return Temp;
}

Acuario 01-03-16 02:33 PM

14. OLED.ino
/*
Universal 8 bit Graphics Library, u8glib - Universal Graphics Library for 8 Bit Embedded Systems - Google Project Hosting
*/
#include <Wire.h> // Comes with Arduino IDE
#include <U8glib.h>

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); // Fast I2C / TWI


void failureBAC1000(void) {
u8g.setFont(u8g_font_unifont);
u8g.drawStr(0, 10, "Fail to read BAC1000");
}

// graphic commands to redraw the complete screen should be placed here
void draw(void) {
char line1[20]; // used to store strings

u8g.setFont(u8g_font_unifont);

readTime(); //Get the time from the DS1307
if (lcd_page == LCD_PAGE0) {
sprintf(line1, "%02d:%02d:%02d %02d/%02d", tm.Hour, tm.Minute, tm.Second, tm.Day, tm.Month);
u8g.drawStr(0, 10, line1);

char t[3];
char u[3];
dtostrf(tempCompressor, 2, 0, t);
dtostrf(tempEvaporator, 2, 0, u);
sprintf(line1, "Cp:%s Ev:%s", t, u);
u8g.drawStr(0, 22, line1);

//Dewpoint
dtostrf(dewPointTemp, 5, 2, t);
sprintf(line1, "Dp:%s", t);
//u8g.drawBitmapP(0, 25, 1, 8, dropletIco);
u8g.drawStr(0, 34, line1);

if (defrostFlag == FALSE)
sprintf(line1, "Machine:%s", operatingState == FALSE ? "OFF" : "ON");
else
sprintf(line1, "Machine:%s Def:%d", operatingState == FALSE ? "OFF" : "ON", defrostStartTime - (int)(defrostRunTime / 1000));
u8g.drawStr(0, 46, line1);
sprintf(line1, "%s", systemState);
u8g.drawStr(0, 58, line1);
}
}

NIL_THREAD(threadDisplayOLED, arg) {
while (TRUE) {

wdt_reset(); //Reset watchdog timer

u8g.firstPage(); // picture loop
do {
draw();
nilThdSleepMilliseconds(50);

} while (u8g.nextPage());

// rebuild the picture after some delay
nilThdSleepMilliseconds(100);
}
}

Acuario 01-03-16 02:36 PM

And that's it for the moment.
It still lacks control of the heatpump in cooling mode when the temperature reaches the dewpoint that's to come later (when the summer approaches!)

Hope this helps or inspires someone.

By the way, as a development environment I use Visual Studio 2015 and Visual Micro extension for Arduino.

Acuario


All times are GMT -5. The time now is 05:26 AM.

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Ad Management by RedTyger