chpc/CHPC_firmware.ino

2192 lines
67 KiB
Arduino
Raw Normal View History

2019-04-01 03:02:55 +00:00
/*
Cheap Heat Pump Controller (CHPC) firmware.
Copyright (C) 2018-2019 Gonzho (gonzho@web.de)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
See https://github.com/gonzho000/chpc/ for more details
*/
//-----------------------USER OPTIONS-----------------------
2019-05-02 12:52:19 +00:00
#define BOARD_TYPE_G //Type "G"
//#define BOARD_TYPE_F //Type "F"
2019-04-01 03:02:55 +00:00
2019-05-02 12:52:19 +00:00
//#define DISPLAY_096 1 //1st tests, support WILL BE DROPPED OUT SOON! small OLEDs support
#define DISPLAY_1602 2 //if only 1st character appears: patch 1602 library "inline size_t LiquidCrystal_I2C::write(uint8_t value)" "return 1" instead of "return 0"
2019-04-01 03:02:55 +00:00
//#define DISPLAY_NONE -1
2019-05-02 12:52:19 +00:00
#define INPUTS_AS_BUTTONS 1 //pulldown resistors required!
2019-06-01 19:16:28 +00:00
//#define RS485_PYTHON 1
#define RS485_HUMAN 2
2019-05-02 12:52:19 +00:00
//#define RS485_NONE 3
2019-04-16 15:53:34 +00:00
#define EEV_SUPPORT
2019-04-16 20:31:36 +00:00
//#define EEV_ONLY //NO target, no relays. Oly EEV, Tae, Tbe, current sensor and may be additional T sensors
2019-04-16 15:53:34 +00:00
#define HUMAN_AUTOINFO 10000 //print stats to console
2019-04-01 03:02:55 +00:00
2019-05-02 12:52:19 +00:00
#define WATCHDOG //only if u know what to do
2019-04-01 03:02:55 +00:00
//-----------------------TUNING OPTIONS -----------------------
#define MAX_WATTS 1170.0 //user for power protection
2019-05-02 12:52:19 +00:00
#define DEFFERED_STOP_HOTCIRCLE 3000000 //50 mins
2019-04-01 03:02:55 +00:00
2019-04-16 15:53:34 +00:00
#define POWERON_PAUSE 300000 //5 mins
#define MINCYCLE_POWEROFF 300000 //5 mins
#define MINCYCLE_POWERON 3600000 //60 mins
2019-05-02 12:52:19 +00:00
#define POWERON_HIGHTIME 10000 //10 sec, defines time after start when power consumption can be 2 times greater than normal
2019-04-16 15:53:34 +00:00
//EEV
#define EEV_MAXPULSES 480
#define EEV_PULSE_FCLOSE_MILLIS 20 //fast close, set waiting pos., close on danger
2019-05-02 12:52:19 +00:00
#define EEV_PULSE_CLOSE_MILLIS 50000 //precise close
2019-04-16 15:53:34 +00:00
#define EEV_PULSE_WOPEN_MILLIS 20 //waiting pos. set
2019-05-02 12:52:19 +00:00
#define EEV_PULSE_FOPEN_MILLIS 1300 //fast open, fast search
#define EEV_PULSE_OPEN_MILLIS 60000 //precise open
2019-04-16 15:53:34 +00:00
#define EEV_STOP_HOLD 500 //0.1..1sec for Sanhua
#define EEV_CLOSE_ADD_PULSES 8 //read below, close algo
2019-05-02 12:52:19 +00:00
#define EEV_OPEN_AFTER_CLOSE 47 //0 - close to zero position, than close on EEV_CLOSE_ADD_PULSES
//N - close to zero position, than close on EEV_CLOSE_ADD_PULSES, than open on EEV_OPEN_AFTER_CLOSE pulses
#define EEV_MINWORKPOS 52 //position will be not less during normal work
#define EEV_PRECISE_START 8.6 //T difference, threshold: make slower pulses if less
#define EEV_EMERG_DIFF 2.5 //if dangerous condition: diff =< (target_diff - EEV_EMERG_DIFF) occured, ex: target diff 5.0, emerg. diff 2.0, if calculated nowtime diff <= 3.0 then EEV will be closed
#define EEV_HYSTERESIS 0.6 //must be less than EEV_PRECISE_START, ex: target difference = 4.0, hysteresis = 0.1, when difference in range 4.0..4.1 no EEV pulses will be done;
#define EEV_CLOSEEVERY 86400000 //86400000: every 24 hours, done while HP is NOT working
#define EEV_TARGET_TEMP_DIFF 4.0 //target difference between Before Evaporator and After Evaporator
2019-06-01 19:16:28 +00:00
//#define EEV_DEBUG //used to debug during fine tuning, "RS485_HUMAN" only
2019-05-02 12:52:19 +00:00
#define MAGIC 0x46 //change if u want to reinit T sensors
2019-04-01 03:02:55 +00:00
//-----------------------USER OPTIONS END -----------------------
2019-04-16 20:31:36 +00:00
//#define INPUTS_AS_INPUTS 2 //
//#define RS485_MACHINE 3 //?? or part of Python?
2019-04-01 03:02:55 +00:00
//-----------------------changelog-----------------------
/*
v1.0:
- Displays support
- define TYPE F/G and rearrange ports
- multi-DS18b20 support on lane
- skip non-important DS18B20 during init
- rewrite Main Cycle to unification: some sensors can be absent, ex: T_hot_out can be absent because i'ts used as target
- 2 on-board buttons support: +/- aim
- DISPLAY: indication: real and aim
- RS485_HUMAN: remote commands +,-,G,0x20/?/Enter
- buttons: < > increase_decrease t
- simpliest thermostat scheme: only T target
- rename all procs
- RS485_PYTHON: print to console inspite of mode diring init proc
- faster wattage overload processing
- write aim value to EE if needed, period: 15 mins (eq. 1041 days)
- deferred stop of hot side circle
- 80 microseconds at 9600
2019-04-16 15:53:34 +00:00
v1.1, 15 Apr 2019:
2019-04-01 03:02:55 +00:00
- HUMAN_AUTOINFO time
2019-04-16 15:53:34 +00:00
- EEV_ONLY mode
2019-04-01 03:02:55 +00:00
- EEV_Support
2019-04-16 15:53:34 +00:00
- EEV auto poweron/poweroff every 10 sec
2019-04-16 20:31:36 +00:00
- EEV_recalibration_time to stop HP and recalibrate EEV from zero level ex: every 24 hours
2019-04-16 15:53:34 +00:00
2019-04-16 16:35:20 +00:00
v1.2, 16 Apr 2019:
2019-04-16 20:31:36 +00:00
- "Type F" support
2019-04-16 16:35:20 +00:00
2019-05-02 12:52:19 +00:00
v1.3, 30 Apr 2019:
- EEV changed "overheating" to "delta T"
- EEV algo v1.1
2019-04-16 15:53:34 +00:00
//TODO:
2019-05-02 12:52:19 +00:00
- liquid ref. protection: start cold circle and sump heater if tsump =< tco/tci+1
- periodical start of hot side circle
2019-04-01 03:02:55 +00:00
- valve_4way
- emergency jumper support
2019-05-02 12:52:19 +00:00
- inputs support
- ? rewite re-init proc from MAGIC to emergency jumper removal at board start
- ? EEV target to EEPROM
- ? list T and other things on screen with buttons
- ? EEV define maximum working position
2019-06-01 19:16:28 +00:00
- ? few devices at same lane for RS485_HUMAN
2019-04-01 03:02:55 +00:00
*/
//-----------------------changelog END-----------------------
// DS18B20 pins: GND DATA VDD
//Connections:
//DS18B20 Pinout (Left to Right, pins down, flat side toward you)
//- Left = Ground
//- Center = Signal (Pin N of arduino): (with 3.3K to 4.7K resistor to +5 or 3.3 )
//- Right = +5 or +3.3 V
//
//
// high volume scheme: +---- +5V (12V not tested)
// |
// +----+
// 1MOhm piezo
// +----+
// |(C)
// pin -> 1.6 kOhms -> (B) 2n2222 < front here
// |(E)
// +--- GND
//
/*
scheme SCT-013-000:
2 pins used: tip and sleeve, center (ring) not used http://cms.35g.tw/coding/wp-content/uploads/2014/09/SCT-013-000_UNO-1.jpg
pins are interchangeable due to AC
32 Ohms (22+10) between sensor pins (35 == ideal)
Pin1:
- via elect. cap. to GND
- via ~10K..470K resistor to GND
- via ~10K..470K resistor to +5 (same as prev.)
if 10K+10K used: current is 25mA
use 100K+100K for 3 phases
Pin2:
- to analog pin
- via 32..35 Ohms resistor to Pin1
+5 -------------------------+
|
|
# R1 10K+
|
|
|~2.5 at this point
+---------------+--------------------------------------+----+
| | | |
#_ elect. cap. # R2 10K+ (same as R1) SCT-013-000 $ # R3 = 35 Ohms (ideal case), 32 used
| | | |
GND --------+---------------+ +----+--------> to Analog pin
WARNING: calibrate 3 sensors together, from different sellers, due to case of incorrectly worked 1 of 3 sensor
P(watts)=220*220/R(Ohms)
*/
//
//MAX 485 voltage - 5V
//
// use resistor at RS-485 GND
// 1st test: 10k result lot of issues
// 2nd test: 1k, issues
// 3rd test: 100, see discussions
//16-ch Multiplexer EN pin: active LOW, connect to GND
//used pins:
//!!! ACTUALISE
//2: Z
//3: S3
//4: S2
//5: S1
//6: S0
//7: relay 2
//8: relay 3
//9: speaker
//10: relay 4
//11-13: rs485
//A0: relay 1
//A1: power monitor
/*
relay 1: heat pump
relay 2: hot side pump
relay 3: cold side pump
relay 4: (future) heatpump sump heater
t0: room
t1: heatpump sump
t2: cold in
t3: cold out
t4: hot in
t5: hot out
t6: before condenser
t7: condenser-evaporator
t8: after evaporator
t9: outer
tA: warm floor
wattage1
*/
2019-06-01 19:16:28 +00:00
String fw_version = "1.4";
2019-04-01 03:02:55 +00:00
#ifdef DISPLAY_096
#define DISPLAY DISPLAY_096
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
#define I2C_ADDRESS 0x3C
SSD1306AsciiWire oled;
#endif
#ifdef DISPLAY_1602
#define DISPLAY DISPLAY_1602
#include <Wire.h>
#include "LiquidCrystal_I2C.h"
LiquidCrystal_I2C lcd(0x3f,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
#endif
#ifdef DISPLAY_NONE
#define DISPLAY DISPLAY_NONE
#endif
#ifndef DISPLAY
#define DISPLAY -1
#endif
//
#ifdef INPUTS_AS_BUTTONS
#define INPUTS INPUTS_AS_BUTTONS
#endif
#ifdef INPUTS_AS_INPUTS
#define INPUTS INPUTS_AS_INPUTS
#endif
//
#ifdef RS485_PYTHON
#define RS485 RS485_PYTHON
char ishuman = 0;
#endif
#ifdef RS485_HUMAN
#define RS485 RS485_HUMAN
char ishuman = 1;
#endif
2019-05-02 12:52:19 +00:00
#ifdef RS485_NONE
char ishuman = 0;
#endif
2019-04-01 03:02:55 +00:00
//hardware resources
#define OW_BUS_ALLTSENSORS 12
#define SerialTxControl 13 //RS485 Direction control DE and RE to this pin
#define speakerOut 6
#define em_pin1 A6
#define EMERGENCY_PIN A7
2019-04-16 17:38:55 +00:00
2019-04-16 16:35:20 +00:00
2019-04-01 03:02:55 +00:00
#ifdef BOARD_TYPE_G
2019-04-16 15:53:34 +00:00
String hw_version = "Type G v1.x";
2019-04-01 03:02:55 +00:00
#define RELAY_HEATPUMP 8
#define RELAY_HOTSIDE_CIRCLE 9
#define RELAY_COLDSIDE_CIRCLE 7
#define RELAY_SUMP_HEATER 10
#define RELAY_4WAY_VALVE 11
#ifdef INPUTS_AS_BUTTONS
2019-04-16 15:53:34 +00:00
#define BUT_RIGHT A3
#define BUT_LEFT A2
#endif
2019-04-16 17:38:55 +00:00
#ifdef EEV_SUPPORT
#define EEV_1 2
#define EEV_2 4
#define EEV_3 3
#define EEV_4 5
2019-04-01 03:02:55 +00:00
#endif
2019-04-16 16:35:20 +00:00
#endif
#ifdef BOARD_TYPE_F
String hw_version = "Type F v1.x";
#define RELAY_HEATPUMP 7
#define RELAY_COLDSIDE_CIRCLE 8
2019-04-16 20:31:36 +00:00
#define LATCH_595 10
#define CLK_595 11
#define DATA_595 9
//595.0: relay 3 RELAY_HOTSIDE_CIRCLE, 595.1: relay 4 RELAY_SUMP_HEATER, 595.2: relay 5 RELAY_4WAY_VALVE, 595.3: uln 6, 595.4: uln 7, 595.5: uln 8, 595.6: uln 9, 595.7: uln 10
2019-04-16 17:38:55 +00:00
#ifdef EEV_SUPPORT
#define EEV_1 5
#define EEV_2 3
#define EEV_3 4
#define EEV_4 2
#endif
2019-05-02 12:52:19 +00:00
#ifdef INPUTS_AS_BUTTONS //not sure
#define BUT_RIGHT A3
#define BUT_LEFT A2
#endif
2019-04-16 16:35:20 +00:00
2019-04-01 03:02:55 +00:00
#endif
//---------------------------memory debug
#ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
extern "C" char* sbrk(int incr);
#else // __ARM__
extern char *__brkval;
#endif // __arm__
int freeMemory() {
char top;
#ifdef __arm__
return &top - reinterpret_cast<char*>(sbrk(0));
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
return &top - __brkval;
#else // __arm__
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#endif // __arm__
}
//---------------------------memory debug END
#include <avr/wdt.h>
#include <EEPROM.h>
//#include <FastCRC.h>
/*FastCRC16 CRC16;
union _crc {
unsigned int integer;
char bytes[2];
} crc;
*/
#include <SoftwareSerial.h>
#define SerialRX 0 //RX connected to RO - Receiver Output
#define SerialTX 1 //TX connected to DI - Driver Output Pin
#define RS485Transmit HIGH
#define RS485Receive LOW
2019-05-02 12:52:19 +00:00
const char devID = 0x41;
2019-04-01 03:02:55 +00:00
const char hostID = 0x30;
SoftwareSerial RS485Serial(SerialRX, SerialTX); // RX, TX
#include <OneWire.h>
#include <DallasTemperature.h>
//library's DEVICE_DISCONNECTED_C -127.0
OneWire ow_ALLTSENSORS(OW_BUS_ALLTSENSORS);
DallasTemperature s_allTsensors(&ow_ALLTSENSORS);
typedef struct {
DeviceAddress addr;
bool e; //enabled
double T;
} st_tsens;
DeviceAddress dev_addr; //temp
st_tsens Tae ;
st_tsens Tbe ;
st_tsens Ttarget;
st_tsens Tsump;
st_tsens Tci ;
st_tsens Tco ;
st_tsens Thi ;
st_tsens Tho ;
st_tsens Tbc ;
st_tsens Tac ;
st_tsens Touter;
st_tsens Ts1 ;
st_tsens Ts2 ;
#define BIT_Tae 0
#define BIT_Tbe 1
#define BIT_Ttarget 2
#define BIT_Tsump 3
#define BIT_Tci 4
#define BIT_Tco 5
#define BIT_Thi 6
#define BIT_Tho 7
#define BIT_Tbc 8
#define BIT_Tac 9
#define BIT_Touter 10
#define BIT_Ts1 11
#define BIT_Ts2 12
unsigned int used_sensors = 0 ; //bit array
2019-05-02 12:52:19 +00:00
double T_setpoint = 26.5;
2019-04-01 03:02:55 +00:00
double T_setpoint_lastsaved = T_setpoint;
2019-05-02 12:52:19 +00:00
double T_EEV_setpoint = EEV_TARGET_TEMP_DIFF;
double T_EEV_dt = 0.0; //real, used during run
2019-04-01 03:02:55 +00:00
const double cT_setpoint_max = 45.0;
2019-05-02 12:52:19 +00:00
const double cT_hotcircle_delta_min = 2.0;
const double cT_sump_min = 9.0;
const double cT_sump_max = 110.0;
2019-04-01 03:02:55 +00:00
const double cT_sump_heat_threshold = 16.0;
//const double cT_sump_outerT_threshold = 18.0; //?? seems to be not useful
2019-05-02 12:52:19 +00:00
const double cT_before_condenser_max = 108.0;
2019-04-01 03:02:55 +00:00
const double cT_after_evaporator_min = -7.0; // working evaporation presure ~= -10, it is constant due to large evaporator volume // waterhouse v1: -12 is too high
const double cT_cold_min = -8.0;
const double cT_hotout_max = 50.0;
//const double cT_workingOK_cold_delta_min = 0.5; // 0.7 - 1st try, 2nd try 0.5
const double cT_workingOK_hot_delta_min = 0.5;
2019-04-16 15:53:34 +00:00
const double cT_workingOK_sump_min = 30.0; //need to be not very high to normal start after deep freeze
2019-04-01 03:02:55 +00:00
const double c_wattage_max = MAX_WATTS; //FUNAI: 1000W seems to be normal working wattage INCLUDING 1(one) CR25/4 at 3rd speed
//PH165X1CY : 920 Watts, 4.2 A
const double c_workingOK_wattage_min = c_wattage_max/2.5; //
2019-05-02 12:52:19 +00:00
bool heatpump_state = 0;
bool hotside_circle_state = 0;
bool coldside_circle_state = 0;
bool sump_heater_state = 0;
2019-04-01 03:02:55 +00:00
const long poweron_pause = POWERON_PAUSE ; //default 5 mins
const long mincycle_poweroff = MINCYCLE_POWEROFF; //default 5 mins
const long mincycle_poweron = MINCYCLE_POWERON ; //default 60 mins
2019-05-02 12:52:19 +00:00
bool _1st_start_sleeped = 0;
2019-04-01 03:02:55 +00:00
//??? TODO: periodical start ?
//const long floor_circle_maxhalted = 6000000; //circle NOT works max 100 minutes
const long deffered_stop_hotcircle = DEFFERED_STOP_HOTCIRCLE;
2019-04-16 15:53:34 +00:00
int EEV_cur_pos = 0;
int EEV_apulses = 0; //for async
bool EEV_adonotcare = 0;
const unsigned char EEV_steps[4] = {0b1010, 0b0110, 0b0101, 0b1001};
char EEV_cur_step = 0;
bool EEV_fast = 0;
2019-04-01 03:02:55 +00:00
//main cycle vars
unsigned long millis_prev = 0;
unsigned long millis_now = 0;
2019-04-16 15:53:34 +00:00
unsigned long millis_cycle = 1000;
2019-04-01 03:02:55 +00:00
unsigned long millis_last_heatpump_on = 0;
unsigned long millis_last_heatpump_off = 0;
unsigned long millis_notification = 0;
unsigned long millis_notification_interval = 33000;
unsigned long millis_displ_update = 0;
unsigned long millis_displ_update_interval = 10000;
unsigned long millis_escinput = 0;
unsigned long millis_charinput = 0;
unsigned long millis_lasteesave = 0;
2019-04-16 15:53:34 +00:00
unsigned long millis_last_printstats = 0;
unsigned long millis_eev_last_close = 0;
unsigned long millis_eev_last_on = 0;
unsigned long millis_eev_last_step = 0;
2019-04-01 03:02:55 +00:00
int skipchars = 0;
#define ERR_HZ 2500
char inData[50]; // Allocate some space for the string, do not change that size!
char inChar= -1; // space to store the character read
byte index = 0; // Index into array; where to store the character
//-------------temporary variables
char temp[10];
int i = 0;
int z = 0;
int x = 0;
2019-04-16 15:53:34 +00:00
int y = 0;
2019-04-01 03:02:55 +00:00
double tempdouble = 0.0;
int tempint = 0;
String outString;
//-------------EEPROM
int eeprom_magic_read = 0x00;
int eeprom_addr = 0x00;
//initial values, saved to EEPROM and can be modified later
//CHANGE eeprom_magic after correction!
const int eeprom_magic = MAGIC;
//-------------ERROR states
#define ERR_OK 0
#define ERR_T_SENSOR 1
#define ERR_HOT_PUMP 2
#define ERR_COLD_PUMP 3
#define ERR_HEATPUMP 4
#define ERR_WATTAGE 5
int errorcode = 0;
//--------------------------- for wattage
#define ADC_BITS 10 //10 fo regular arduino
#define ADC_COUNTS (1<<ADC_BITS)
2019-04-16 15:53:34 +00:00
float em_calibration = 62.5;
int em_samplesnum = 2960; // Calculate Irms only 1480 == full 14 periods for 50Hz
//double Irms = 0; //for tests with original procedure
2019-04-01 03:02:55 +00:00
int supply_voltage = 0;
int em_i = 0;
//phase 1
int sampleI_1 = 0;
double filteredI_1 = 0;
2019-04-16 15:53:34 +00:00
double offsetI_1 = ADC_COUNTS>>1; //Low-pass filter output
double sqI_1,sumI_1 = 0; //sq = squared, sum = Sum, inst = instantaneous
2019-04-01 03:02:55 +00:00
double async_Irms_1 = 0;
double async_wattage = 0;
//--------------------------- for wattage END
//--------------------------- functions
long ReadVcc() {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV(MUX3) | _BV(MUX2);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Start conversion
while (bit_is_set(ADCSRA,ADSC)); // measuring
uint8_t low = ADCL; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH; // unlocks both
long result = (high<<8) | low;
//constant NOT same as in battery controller!
result = 1126400L / result; // Calculate Vcc (in mV); (me: !!) 1125300 (!!) = 1.1*1023*1000
return result; // Vcc in millivolts
}
char CheckAddrExists(void) {
2019-06-01 19:16:28 +00:00
/*
2019-04-01 03:02:55 +00:00
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tae.addr[i]) break; }
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tbe.addr[i]) break; }
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Ttarget.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tsump.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tci.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tco.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Thi.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tho.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tbc.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Tac.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Touter.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Ts1.addr[i]) break;}
if (i == 8) return 1;
for (i = 0; i < 8; i++) { if (dev_addr[i] != Ts2.addr[i]) break;}
if (i == 8) return 1;
return 0;
2019-06-01 19:16:28 +00:00
*/
//!!!!! optimised v1.4, untested !!!!!
for (i = 0; i < 8; i++) {
if ( (dev_addr[i] != Tae.addr[i]) &&
(dev_addr[i] != Tbe.addr[i]) &&
(dev_addr[i] != Ttarget.addr[i]) &&
(dev_addr[i] != Tsump.addr[i]) &&
(dev_addr[i] != Tci.addr[i]) &&
(dev_addr[i] != Tco.addr[i]) &&
(dev_addr[i] != Thi.addr[i]) &&
(dev_addr[i] != Tho.addr[i]) &&
(dev_addr[i] != Tbc.addr[i]) &&
(dev_addr[i] != Tac.addr[i]) &&
(dev_addr[i] != Touter.addr[i]) &&
(dev_addr[i] != Ts1.addr[i]) &&
(dev_addr[i] != Ts2.addr[i])
)
break;
}
if (i == 8) return 1;
return 0;
2019-04-01 03:02:55 +00:00
}
void InitS_and_D(void) {
#ifdef DISPLAY_096
Wire.begin();
oled.begin(&Adafruit128x64, I2C_ADDRESS);
oled.setFont(Adafruit5x7);
#endif
#ifdef DISPLAY_1602
lcd.init(); // initialize the lcd
lcd.backlight(); // not really needed
#endif
RS485Serial.begin(9600);
}
2019-05-02 12:52:19 +00:00
void PrintS (String str) {
#ifdef RS485_HUMAN
char *outChar=&str[0];
digitalWrite(SerialTxControl, RS485Transmit);
delay(1);
RS485Serial.print(outChar);
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
}
2019-04-01 03:02:55 +00:00
void PrintS_and_D (String str, int printSerial = 1) {
char *outChar=&str[0];
//#ifdef RS485_HUMAN
if (ishuman != 0) {
if (printSerial == 1) {
digitalWrite(SerialTxControl, RS485Transmit);
delay(1);
RS485Serial.print(outChar);
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
}
}
//#endif
if (str == "") {
return;
}
#ifdef DISPLAY_096
oled.clear();
oled.println(str);
#endif
#ifdef DISPLAY_1602
lcd.backlight();
lcd.clear();
lcd.print(str);
#endif
}
2019-05-02 12:52:19 +00:00
void Print_D2 () {
#ifdef DISPLAY_1602
lcd.setCursor(0, 1);
lcd.print(outString);
#endif
}
2019-04-01 03:02:55 +00:00
void _PrintHelp(void) {
2019-05-02 12:52:19 +00:00
PrintS( "CHPC, https://github.com/gonzho000/chpc/ fw: " + fw_version + " board: "+ hw_version);
PrintS(F("Commands: \n (?) help\n (+) increase aim T\n (-) decrease aim T\n \n"));
2019-04-16 15:53:34 +00:00
#ifdef EEV_SUPPORT
2019-05-02 12:52:19 +00:00
PrintS(F("(<) decrease EEV T diff \n(>) increase EEV T diff"));
2019-04-16 15:53:34 +00:00
#endif
2019-05-02 12:52:19 +00:00
PrintS(F("(G) get stats"));
2019-04-01 03:02:55 +00:00
}
void PrintS_and_D_double (double double_to_print) {
dtostrf(double_to_print,1,2,temp);
PrintS_and_D(temp);
}
int Inc_T (void) {
if (T_setpoint + 0.5 > cT_setpoint_max) {
2019-05-02 12:52:19 +00:00
PrintS_and_D(F("Max!"));
2019-04-01 03:02:55 +00:00
delay (200);
return 0;
}
T_setpoint += 0.5;
PrintS_and_D_double(T_setpoint);
return 1;
}
int Dec_T (void) {
if (T_setpoint - 0.5 < 1.0) {
2019-05-02 12:52:19 +00:00
PrintS_and_D(F("Min!"));
2019-04-01 03:02:55 +00:00
delay (200);
return 0;
}
T_setpoint -= 0.5;
PrintS_and_D_double(T_setpoint);
return 1;
}
2019-04-16 15:53:34 +00:00
int Inc_E (void) { ///!!!!!! unprotected
T_EEV_setpoint += 0.25;
PrintS_and_D_double(T_EEV_setpoint);
return 1;
}
int Dec_E (void) { ///!!!!!! unprotected
T_EEV_setpoint -= 0.25;
PrintS_and_D_double(T_EEV_setpoint);
return 1;
}
2019-04-01 03:02:55 +00:00
void print_Serial_SaD (double num) { //global string + double
RS485Serial.print(outString);
RS485Serial.println(num);
}
void PrintStats_Serial (void) {
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
delay(1);
if (Tae.e == 1) {outString = "Tae: " ; print_Serial_SaD(Tae.T); }
if (Tbe.e == 1) {outString= "Tbe: " ; print_Serial_SaD(Tbe.T); }
if (Ttarget.e == 1) {outString = "Ttarget: "; print_Serial_SaD(Ttarget.T); }
if (Tsump.e == 1) {outString = "Tsump: " ; print_Serial_SaD(Tsump.T); }
if (Tci.e == 1) {outString = "Tci: " ; print_Serial_SaD(Tci.T); }
if (Tco.e == 1) {outString = "Tco: " ; print_Serial_SaD(Tco.T); }
if (Thi.e == 1) {outString = "Thi: " ; print_Serial_SaD(Thi.T); }
if (Tho.e == 1) {outString = "Tho: " ; print_Serial_SaD(Tho.T); }
if (Tbc.e == 1) {outString = "Tbc: " ; print_Serial_SaD(Tbc.T); }
if (Tac.e == 1) {outString = "Tac: " ; print_Serial_SaD(Tac.T); }
if (Touter.e == 1) {outString = "Touter: " ; print_Serial_SaD(Touter.T); }
if (Ts1.e == 1) {outString = "Ts1: " ; print_Serial_SaD(Ts1.T); }
if (Ts2.e == 1) {outString = "Ts2: " ; print_Serial_SaD(Ts2.T); }
outString = "Err: " + String(errorcode) + "\n\rWatts:" + String(async_wattage) + "\n\rAim: "; print_Serial_SaD(T_setpoint);
2019-04-16 15:53:34 +00:00
#ifdef EEV_SUPPORT
outString = "EEV_pos:" + String (EEV_cur_pos);
RS485Serial.print(outString);
#endif
2019-04-01 03:02:55 +00:00
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
}
void ReadEECheckAddr(unsigned char *to_addr) {
for (i=0 ; i<8 ; i++) {
to_addr[i] = EEPROM.read(eeprom_addr);
eeprom_addr++;
}
i = 0;
CheckIsInvalidCRCAddr(to_addr);
if (i != 0) {
while (1) {
2019-05-02 12:52:19 +00:00
//PrintAddr(to_addr);
2019-04-01 03:02:55 +00:00
PrintS_and_D(F("Err:EEPROM, reinit!"));
2019-05-02 12:52:19 +00:00
delay(5000);
2019-04-01 03:02:55 +00:00
}
}
}
void CheckIsInvalidCRCAddr(unsigned char *addr) {
if (OneWire::crc8( addr, 7) != addr[7] ) {
i+= 1;
}
}
void CopyAddrStoreEE(unsigned char *addr_to, int bit_offset) { //get result from dev_addr, autoincrement eeprom_addr
//dev_addr and z from globals used
for (i=0 ; i<8 ; i++) { //no matter
if (z == 0) {
dev_addr[i] = 0x00;
}
addr_to[i] = dev_addr[i];
EEPROM.write(eeprom_addr, dev_addr[i]);
eeprom_addr++;
}
bitWrite(used_sensors, bit_offset, z);
}
void WriteFloatEEPROM(int addr, float val) {
byte *x = (byte *)&val;
for(byte u = 0; u < 4; u++) EEPROM.write(u+addr, x[u]);
}
float ReadFloatEEPROM(int addr) {
byte x[4];
for(byte u = 0; u < 4; u++) x[u] = EEPROM.read(u+addr);
float *y = (float *)&x;
return y[0];
}
void SaveSetpointEE(void) {
if( (T_setpoint_lastsaved != T_setpoint) &&
( ((unsigned long)(millis_now - millis_lasteesave) > 15*60*1000 ) || (millis_lasteesave == 0) ) ) {
eeprom_addr = 1;
WriteFloatEEPROM(eeprom_addr, T_setpoint);
millis_lasteesave = millis_now;
T_setpoint_lastsaved = T_setpoint;
}
}
void PrintAddr(unsigned char *str) {
outString = "";
for (i = 0; i < 8; i++) {
if (str[i] < 0x10) outString += "0";
outString += String(str[i], HEX);
}
PrintS_and_D(outString);
}
unsigned char FindAddr(String what, int required = 0) {
i = 1;
while (RS485Serial.available() > 0) {
inChar = RS485Serial.read();
delay(1);
}
inChar = 0x00;
while (1) {
while (!s_allTsensors.getAddress(dev_addr, 0)) {
if (required == 0) {
PrintS_and_D(F("Press > to skip"));
delay(500);
while (RS485Serial.available() > 0) {
inChar = RS485Serial.read();
if (inChar == 0x3E) {
PrintS_and_D("Skipped: " + what);
return 0;
}
}
#ifdef INPUTS_AS_BUTTONS
i = digitalRead(BUT_RIGHT);
if (i == 1) {
PrintS_and_D("Skipped: " + what);
delay(4000);
return 0;
}
#endif
}
PrintS_and_D("Insert " + what);
delay(1000);
}
if ( OneWire::crc8( dev_addr, 7) != dev_addr[7]) {
2019-05-02 12:52:19 +00:00
PrintS_and_D(F("Invalid CRC! Remove and insert same sensor!\n"));
2019-04-01 03:02:55 +00:00
delay(200);
continue;
} else if (CheckAddrExists() == 1) {
PrintS_and_D(F("USED! Remove!"));
delay(1000);
continue;
} else {
break;
}
}
while (1) {
2019-05-02 12:52:19 +00:00
//PrintAddr(dev_addr);
2019-04-01 03:02:55 +00:00
delay(1000);
if (s_allTsensors.getAddress(dev_addr, 0)) {
2019-05-02 12:52:19 +00:00
PrintS_and_D("OK! Remove " + what);
2019-04-01 03:02:55 +00:00
delay(1000);
} else {
delay(100);
break;
}
}
return i;
}
double GetT (unsigned char *str) {
tempdouble = -127.0;
for ( i = 0; i < 8; i++) {
#ifdef WATCHDOG
wdt_reset();
#endif
if ( (tempdouble == 85.0) || (tempdouble == -127.0) ) {
if ( tempdouble == 85.0 ) { //initial value in dallas register after poweron
delay (375); //375 actual for 11 bits resolution, 2-3 retries OK for 12-bits resolution
} else {
delay (37);
}
tempdouble = s_allTsensors.getTempC(str);
} else {
break;
}
}
return tempdouble;
}
void Get_Temperatures(void) {
if (Tae.e) Tae.T = GetT(Tae.addr);
if (Tbe.e) Tbe.T = GetT(Tbe.addr);
2019-04-16 15:53:34 +00:00
if (Ttarget.e) Ttarget.T = GetT(Ttarget.addr);
2019-04-01 03:02:55 +00:00
if (Tsump.e) Tsump.T = GetT(Tsump.addr);
if (Tci.e) Tci.T = GetT(Tci.addr);
if (Tco.e) Tco.T = GetT(Tco.addr);
if (Thi.e) Thi.T = GetT(Thi.addr);
if (Tho.e) Tho.T = GetT(Tho.addr);
if (Tbc.e) Tbc.T = GetT(Tbc.addr);
if (Tac.e) Tac.T = GetT(Tac.addr);
if (Touter.e) Touter.T = GetT(Touter.addr);
if (Ts1.e) Ts1.T = GetT(Ts1.addr);
if (Ts2.e) Ts2.T = GetT(Ts2.addr);
s_allTsensors.requestTemperatures(); //global request
//---------DEBUG and self-test !!!--------
/*PrintS_and_D("");
PrintS_and_D_double(Tae.T);
PrintS_and_D_double(Tbe.T);
PrintS_and_D_double(Ttarget.T);
PrintS_and_D_double(Tsump.T);
PrintS_and_D("");*/
/*
PrintS_and_D("Sensor 1 ");
PrintS_and_D_double(tr_sens_1);
PrintS_and_D(",\tci ");
PrintS_and_D_double(tr_cold_in);
PrintS_and_D(",\tcout ");
PrintS_and_D_double(tr_cold_out);
PrintS_and_D(",\thin ");
PrintS_and_D_double(tr_hot_in);
PrintS_and_D(",\tho ");
PrintS_and_D_double(tr_hot_out);
PrintS_and_D(",\tbcond ");
PrintS_and_D_double(tr_before_condenser);
PrintS_and_D(",\t outer ");
PrintS_and_D_double(tr_outer);
PrintS_and_D(",\t Sensor 2 ");
PrintS_and_D_double(tr_sens_2);
*/
//---------DEBUG END--------
}
2019-04-16 15:53:34 +00:00
#ifdef EEV_SUPPORT
void on_EEV(){ //1 = do not take care of position
x = EEV_steps[EEV_cur_step];
digitalWrite (EEV_1, bitRead(x, 0));
digitalWrite (EEV_2, bitRead(x, 1));
digitalWrite (EEV_3, bitRead(x, 2));
digitalWrite (EEV_4, bitRead(x, 3));
}
2019-04-01 03:02:55 +00:00
2019-04-16 15:53:34 +00:00
void off_EEV(){ //1 = do not take care of position
digitalWrite (EEV_1, 0);
digitalWrite (EEV_2, 0);
digitalWrite (EEV_3, 0);
digitalWrite (EEV_4, 0);
2019-05-02 12:52:19 +00:00
//PrintS_and_D("off_EEV");
2019-04-16 15:53:34 +00:00
}
#endif
2019-04-16 20:31:36 +00:00
void halifise(void){
#ifdef BOARD_TYPE_F
/*#define LATCH_595 = 10;
#define CLK_595 = 11;
#DEFINE DATA_595 = 9;
//595.0: relay 3 RELAY_HOTSIDE_CIRCLE, 595.1: relay 4 RELAY_SUMP_HEATER, 595.2: relay 5 RELAY_4WAY_VALVE, 595.3: uln 6, 595.4: uln 7, 595.5: uln 8, 595.6: uln 9, 595.7: uln 10
*/
digitalWrite(LATCH_595, 0);
//7
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//6
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//5
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//4
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//3
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//2
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, 0); //4way valve here
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//1
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, sump_heater_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//0
digitalWrite(CLK_595, 0);
digitalWrite(DATA_595, hotside_circle_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(CLK_595, 0);
//
digitalWrite(LATCH_595, 1);
#endif
#ifdef BOARD_TYPE_G
digitalWrite (RELAY_SUMP_HEATER, sump_heater_state);
digitalWrite (RELAY_HOTSIDE_CIRCLE, hotside_circle_state);
#endif
}
2019-04-16 15:53:34 +00:00
//--------------------------- functions END
2019-04-01 03:02:55 +00:00
void setup(void) {
2019-04-16 20:31:36 +00:00
pinMode (RELAY_HEATPUMP, OUTPUT);
pinMode (RELAY_COLDSIDE_CIRCLE, OUTPUT);
2019-04-01 03:02:55 +00:00
digitalWrite (RELAY_HEATPUMP, LOW);
digitalWrite (RELAY_COLDSIDE_CIRCLE, LOW);
2019-04-16 20:31:36 +00:00
#ifdef BOARD_TYPE_G
pinMode (RELAY_SUMP_HEATER, OUTPUT);
pinMode (RELAY_HOTSIDE_CIRCLE, OUTPUT);
digitalWrite (RELAY_SUMP_HEATER, LOW);
digitalWrite (RELAY_HOTSIDE_CIRCLE, LOW);
#endif
#ifdef BOARD_TYPE_F
pinMode (LATCH_595, OUTPUT);
pinMode (CLK_595, OUTPUT);
pinMode (DATA_595, OUTPUT);
digitalWrite (LATCH_595, LOW);
digitalWrite (CLK_595, LOW);
digitalWrite (DATA_595, LOW);
#endif
2019-04-01 03:02:55 +00:00
#ifdef WATCHDOG
wdt_disable();
delay(2000);
#endif
InitS_and_D();
pinMode(SerialTxControl, OUTPUT);
digitalWrite(SerialTxControl, RS485Receive);
//digitalWrite(SerialTxControl, RS485Transmit);
//RS485Serial.println("starting..."); //!!!debug
delay(100);
PrintS_and_D("ID: 0x" + String(devID, HEX));
//Print_Lomem(C_ID);
2019-04-16 15:53:34 +00:00
delay(200);
#ifdef EEV_SUPPORT
pinMode (EEV_1, OUTPUT);
pinMode (EEV_2, OUTPUT);
pinMode (EEV_3, OUTPUT);
pinMode (EEV_4, OUTPUT);
off_EEV();
#endif
pinMode (em_pin1, INPUT);
2019-04-01 03:02:55 +00:00
//PrintS_and_D("setpoint (C):");
//PrintS_and_D(setpoint);
//PrintS_and_D(String(freeMemory())); //!!! debug
s_allTsensors.begin();
s_allTsensors.setWaitForConversion(false); //ASYNC mode, request before get, see Dallas library for details
eeprom_magic_read = EEPROM.read(eeprom_addr);
#ifdef INPUTS_AS_BUTTONS
pinMode (BUT_RIGHT, INPUT);
//digitalWrite (BUT_RIGHT, LOW);
pinMode (BUT_LEFT, INPUT);
//digitalWrite (BUT_LEFT, LOW);
#endif
//EEPROM content:
//0x00 - magic,
//0x01 .. 0x04 Target value,
//0x05 and 0x06 if sensor enabled or not, used_sensors HI and LO
//0x07 .. 0x0e 1st addr, etc..
// tr_after_evaporator(0); tr_before_evaporator(1); tr_target(2); tr_sump(3);
// tr_cold_in(4); tr_cold_out(5); tr_hot_in(6); tr_hot_out(7);
// tr_before_condenser(8); tr_after_condenser(9); tr_outer(10); tr_sens_1(11);
// tr_sens_2(12);
eeprom_addr = 0x00;
if (eeprom_magic_read == eeprom_magic) {
eeprom_addr += 1;
T_setpoint = ReadFloatEEPROM(eeprom_addr);
eeprom_addr += 4;
2019-05-02 12:52:19 +00:00
//PrintS_and_D("EEPROM->T " + String(T_setpoint));
2019-04-01 03:02:55 +00:00
z = EEPROM.read(eeprom_addr); //high
eeprom_addr += 1;
i = EEPROM.read(eeprom_addr); //lo
eeprom_addr += 1;
used_sensors= word (z,i);
Tae.e = bitRead(used_sensors, BIT_Tae );
Tbe.e = bitRead(used_sensors, BIT_Tbe );
Ttarget.e = bitRead(used_sensors, BIT_Ttarget );
Tsump.e = bitRead(used_sensors, BIT_Tsump );
Tci.e = bitRead(used_sensors, BIT_Tci );
Tco.e = bitRead(used_sensors, BIT_Tco );
Thi.e = bitRead(used_sensors, BIT_Thi );
Tho.e = bitRead(used_sensors, BIT_Tho );
Tbc.e = bitRead(used_sensors, BIT_Tbc );
Tac.e = bitRead(used_sensors, BIT_Tac );
Touter.e = bitRead(used_sensors, BIT_Touter );
Ts1.e = bitRead(used_sensors, BIT_Ts1 );
Ts2.e = bitRead(used_sensors, BIT_Ts2 );
#ifdef EEV_SUPPORT
if (Tae.e != 1 || Tbe.e != 1) {
while (1) {
PrintS_and_D("ERR: no Tae or Tbe for EEV!");
delay (1000);
}
}
#endif
ReadEECheckAddr(Tae.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tae");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tbe.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tbe");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Ttarget.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Ttarget");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tsump.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tsump");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tci.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tci");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tco.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tco");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Thi.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Thi");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tho.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tho");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tbc.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tbc");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Tac.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Tac");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Touter.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Touter");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Ts1.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Ts1");
2019-04-01 03:02:55 +00:00
ReadEECheckAddr(Ts2.addr); //eeprom_addr incremeneted here
2019-05-02 12:52:19 +00:00
//PrintS_and_D("k:Ts2");
2019-04-01 03:02:55 +00:00
2019-05-02 12:52:19 +00:00
/*
//?code duplicated, see ReadEECheckAddr
2019-04-01 03:02:55 +00:00
i = 0;
if (Tae.e == 1) CheckIsInvalidCRCAddr(Tae.addr );
if (Tbe.e == 1) CheckIsInvalidCRCAddr(Tbe.addr );
if (Ttarget.e == 1) CheckIsInvalidCRCAddr(Ttarget.addr);
if (Tsump.e == 1) CheckIsInvalidCRCAddr(Tsump.addr);
if (Tci.e == 1) CheckIsInvalidCRCAddr(Tci.addr );
if (Tco.e == 1) CheckIsInvalidCRCAddr(Tco.addr );
if (Thi.e == 1) CheckIsInvalidCRCAddr(Thi.addr );
if (Tho.e == 1) CheckIsInvalidCRCAddr(Tho.addr );
if (Tbc.e == 1) CheckIsInvalidCRCAddr(Tbc.addr );
if (Tac.e == 1) CheckIsInvalidCRCAddr(Tac.addr );
if (Touter.e == 1) CheckIsInvalidCRCAddr(Touter.addr);
if (Ts1.e == 1) CheckIsInvalidCRCAddr(Ts1.addr );
if (Ts2.e == 1) CheckIsInvalidCRCAddr(Ts2.addr );
if (i != 0) {
while ( 1 ) { PrintS_and_D(F("EEPROM err1!")); delay (1000); }
}
2019-05-02 12:52:19 +00:00
*/
2019-04-01 03:02:55 +00:00
} else {
eeprom_addr += 1;
ishuman += 1;
WriteFloatEEPROM(eeprom_addr, T_setpoint);
2019-05-02 12:52:19 +00:00
//PrintS_and_D(F("init EEPROM"));
2019-04-01 03:02:55 +00:00
eeprom_addr += 4;
eeprom_addr += 2; //used sensors, skip
//Ttarget -needed, other - optional
#ifdef EEV_SUPPORT
z = FindAddr("Tae", 1); //holds result in dev_addr, returns "is used"
#else
z = FindAddr("Tae"); //holds result in dev_addr, returns "is used"
#endif
Tae.e = z;
CopyAddrStoreEE (Tae.addr, BIT_Tae); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
#ifdef EEV_SUPPORT
z = FindAddr("Tbe", 1);
#else
z = FindAddr("Tbe");
#endif
Tbe.e = z;
CopyAddrStoreEE (Tbe.addr, BIT_Tbe); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
2019-04-16 15:53:34 +00:00
#ifdef EEV_ONLY
//z = FindAddr("Ttarget");
z = 0;
#else
z = FindAddr("Ttarget", 1);
#endif
2019-04-01 03:02:55 +00:00
Ttarget.e = z;
CopyAddrStoreEE (Ttarget.addr, BIT_Ttarget); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Tsump");
Tsump.e = z;
CopyAddrStoreEE (Tsump.addr, BIT_Tsump); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Tci");
Tci.e = z;
CopyAddrStoreEE (Tci.addr, BIT_Tci); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Tco");
Tco.e = z;
CopyAddrStoreEE (Tco.addr, BIT_Tco); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Thi");
Thi.e = z;
CopyAddrStoreEE (Thi.addr, BIT_Thi); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Tho");
Tho.e = z;
CopyAddrStoreEE (Tho.addr, BIT_Tho); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Tbc");
Tbc.e = z;
CopyAddrStoreEE (Tbc.addr, BIT_Tbc); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Tac");
Tac.e = z;
CopyAddrStoreEE (Tac.addr, BIT_Tac); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Touter");
Touter.e = z;
CopyAddrStoreEE (Touter.addr, BIT_Touter); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Ts1");
Ts1.e = z;
CopyAddrStoreEE (Ts1.addr, BIT_Ts1); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr("Ts2");
Ts2.e = z;
CopyAddrStoreEE (Ts2.addr, BIT_Ts2); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
//
//final, off-the-sequence
EEPROM.write(0+1+4+0,highByte(used_sensors));
EEPROM.write(0+1+4+1,lowByte(used_sensors));
EEPROM.write(0x00, eeprom_magic);
ishuman -= 1;
}
T_setpoint_lastsaved = T_setpoint;
//s_allTsensors.setResolution(ad_Tae, 12);
/*PrintAddr(Tae.addr);
PrintAddr(Tbe.addr);
PrintAddr(Ttarget.addr);
PrintAddr(Tsump.addr);
PrintAddr(Tci.addr);
PrintAddr(Tco.addr);
PrintAddr(Thi.addr);
PrintAddr(Tho.addr);
PrintAddr(Tbc.addr);
PrintAddr(Tac.addr);
PrintAddr(Touter.addr);
PrintAddr(Ts1.addr);
PrintAddr(Ts2.addr);*/
2019-04-16 20:31:36 +00:00
#ifdef WATCHDOG
wdt_enable (WDTO_8S);
#endif
2019-04-01 03:02:55 +00:00
Get_Temperatures();
tone(speakerOut, 2250);
delay (1500); // like ups power on
noTone(speakerOut);
2019-04-16 15:53:34 +00:00
2019-04-01 03:02:55 +00:00
outString.reserve(200);
//PrintS_and_D(String(freeMemory())); //!!! debug
}
void loop(void) {
digitalWrite(SerialTxControl, RS485Receive);
millis_now = millis();
//----------------------------- self-test !!!
2019-04-16 15:53:34 +00:00
/*
2019-04-01 03:02:55 +00:00
digitalWrite(RELAY_HEATPUMP,HIGH);
2019-05-02 12:52:19 +00:00
delay(300);
2019-04-01 03:02:55 +00:00
digitalWrite(RELAY_HOTSIDE_CIRCLE,HIGH);
2019-05-02 12:52:19 +00:00
delay(300);
2019-04-01 03:02:55 +00:00
digitalWrite(RELAY_COLDSIDE_CIRCLE,HIGH);
2019-05-02 12:52:19 +00:00
delay(300);
2019-04-01 03:02:55 +00:00
digitalWrite(RELAY_SUMP_HEATER,HIGH);
2019-04-16 15:53:34 +00:00
delay(2000);
2019-04-01 03:02:55 +00:00
digitalWrite(RELAY_HEATPUMP,LOW);
delay(300);
digitalWrite(RELAY_HOTSIDE_CIRCLE,LOW);
delay(300);
digitalWrite(RELAY_COLDSIDE_CIRCLE,LOW);
delay(300);
digitalWrite(RELAY_SUMP_HEATER,LOW);
*/
// step one revolution in one direction:
2019-04-16 15:53:34 +00:00
//!!! write self-test for EEV
2019-04-01 03:02:55 +00:00
//----------------------------- self-test END
2019-04-16 15:53:34 +00:00
#ifdef RS485_HUMAN
if (((unsigned long)(millis_now - millis_last_printstats) > HUMAN_AUTOINFO) || (millis_last_printstats == 0) ) {
PrintStats_Serial();
millis_last_printstats = millis_now;
}
#endif
//--------------------async fuctions start
2019-04-01 03:02:55 +00:00
if (em_i == 0) {
supply_voltage = ReadVcc();
}
if (em_i < em_samplesnum) {
sampleI_1 = analogRead(em_pin1);
// Digital low pass filter extracts the 2.5 V or 1.65 V dc offset, then subtract this - signal is now centered on 0 counts.
offsetI_1 = (offsetI_1 + (sampleI_1-offsetI_1)/1024);
filteredI_1 = sampleI_1 - offsetI_1;
// Root-mean-square method current
// 1) square current values
sqI_1 = filteredI_1 * filteredI_1;
// 2) sum
sumI_1 += sqI_1;
em_i += 1;
} else {
em_i = 0;
double I_RATIO = em_calibration *((supply_voltage/1000.0) / (ADC_COUNTS));
async_Irms_1 = I_RATIO * sqrt(sumI_1 / em_samplesnum);
async_wattage = async_Irms_1*220.0;
//Reset accumulators
sumI_1 = 0;
//----------------------------- self-test !!!
/*
PrintS_and_D("Async impl. results 1: ");
2019-04-16 15:53:34 +00:00
PrintS_and_D(String(async_wattage)); // Apparent power
2019-04-01 03:02:55 +00:00
PrintS_and_D(" ");
2019-04-16 15:53:34 +00:00
PrintS_and_D(String(async_Irms_1)); // Irms
2019-04-01 03:02:55 +00:00
PrintS_and_D(" voltage: ");
2019-04-16 15:53:34 +00:00
PrintS_and_D(String(supply_voltage));
2019-04-01 03:02:55 +00:00
*/
//----------------------------- self-test END
}
2019-04-16 15:53:34 +00:00
#ifdef EEV_SUPPORT
if ( ((( EEV_apulses < 0 ) && (EEV_fast == 1)) && ((unsigned long)(millis_now - millis_eev_last_step) > (EEV_PULSE_FCLOSE_MILLIS)) ) ||
((( EEV_apulses < 0 ) && (EEV_fast == 0)) && ((unsigned long)(millis_now - millis_eev_last_step) > (EEV_PULSE_CLOSE_MILLIS) ) ) ||
((( EEV_apulses > 0 ) && (EEV_cur_pos < EEV_MINWORKPOS )) && ((unsigned long)(millis_now - millis_eev_last_step) > (EEV_PULSE_WOPEN_MILLIS) ) ) ||
((( EEV_apulses > 0 ) && (EEV_fast == 1) && (EEV_cur_pos >= EEV_MINWORKPOS )) && ((unsigned long)(millis_now - millis_eev_last_step) > (EEV_PULSE_FOPEN_MILLIS) ) ) ||
((( EEV_apulses > 0 ) && (EEV_fast == 0) && (EEV_cur_pos >= EEV_MINWORKPOS )) && ((unsigned long)(millis_now - millis_eev_last_step) > (EEV_PULSE_OPEN_MILLIS) ) ) ||
(millis_eev_last_step == 0)
) {
if ( EEV_apulses != 0 ) {
if ( EEV_apulses > 0 ) {
if (EEV_cur_pos + 1 <= EEV_MAXPULSES) {
EEV_cur_pos += 1;
EEV_cur_step += 1;
EEV_apulses -= 1;
} else {
EEV_apulses = 0;
//PrintS_and_D("EEmax!");
}
}
if ( EEV_apulses < 0 ) {
if ( (EEV_cur_pos - 1 >= EEV_MINWORKPOS) || (EEV_adonotcare == 1) ) {
EEV_cur_pos -= 1;
EEV_cur_step -= 1;
EEV_apulses += 1;
} else {
EEV_apulses = 0;
//PrintS_and_D("EEmin!");
}
}
if (EEV_cur_step > 3) EEV_cur_step = 0;
if (EEV_cur_step < 0) EEV_cur_step = 3;
x = EEV_steps[EEV_cur_step];
digitalWrite (EEV_1, bitRead(x, 0));
digitalWrite (EEV_2, bitRead(x, 1));
digitalWrite (EEV_3, bitRead(x, 2));
digitalWrite (EEV_4, bitRead(x, 3));
}
if (EEV_cur_pos < 0) {
EEV_cur_pos = 0;
}
millis_eev_last_step = millis_now;
2019-05-02 12:52:19 +00:00
#ifdef EEV_DEBUG
PrintS(String(EEV_cur_pos));
#endif
2019-04-16 15:53:34 +00:00
}
#endif
//--------------------async fuctions END
2019-04-01 03:02:55 +00:00
2019-05-02 12:52:19 +00:00
if ( heatpump_state == 1 && async_wattage > c_wattage_max ) {
if ( ((unsigned long)(millis_now - millis_last_heatpump_off) > POWERON_HIGHTIME ) || (async_wattage > c_wattage_max*2)) {
#ifdef RS485_HUMAN
PrintS(("Overload." + String(async_wattage)));
#endif
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
digitalWrite(RELAY_HEATPUMP, heatpump_state);
}
2019-04-01 03:02:55 +00:00
}
//-------------------buttons processing
#ifdef INPUTS_AS_BUTTONS
2019-04-16 15:53:34 +00:00
z = digitalRead(BUT_LEFT);
i = digitalRead(BUT_RIGHT);
if ( (z == 1) && ( i == 1) ) {
//
} else if ( (z == 1) || ( i == 1) ) {
#ifndef EEV_ONLY
if ( z == 1 ) {
x = Dec_T();
}
if ( i == 1 ) {
x = Inc_T();
}
if (x == 1) {
PrintS_and_D("New aim: " + String(T_setpoint));
delay(300);
}
#else
if ( z == 1 ) {
T_EEV_setpoint -= 0.25;
}
if ( i == 1 ) {
T_EEV_setpoint += 0.25;
}
2019-05-02 12:52:19 +00:00
PrintS_and_D("New EEV Td: " + String(T_EEV_setpoint));
2019-04-16 15:53:34 +00:00
delay(300);
#endif
2019-04-01 03:02:55 +00:00
}
2019-04-16 15:53:34 +00:00
2019-04-01 03:02:55 +00:00
#endif
//-------------------buttons processing END
//-------------------display
#if (DISPLAY == 2) || (DISPLAY == 1)
if( ((unsigned long)(millis_now - millis_displ_update) > millis_displ_update_interval ) || (millis_displ_update == 0) ) {
2019-04-16 15:53:34 +00:00
//EEV_ONLY SUPPORT!!!!!!!
#ifndef EEV_ONLY
outString = "A:" + String(T_setpoint, 1) + " Real:";
if (Ttarget.e == 1) {
outString += String(Ttarget.T, 1);
} else {
outString += "ERR";
}
PrintS_and_D(outString, 1); //do not print serial
2019-05-02 12:52:19 +00:00
//2
//#ifdef EEV_SUPPORT
// outString = "Tbe:" + String(Tbe.T, 1) + "Tae:" + String(Tbe.T, 1);
// Print_D2();
//#endif
if (Touter.e == 1){
outString = "Outer:" + String(Touter.T, 1);
Print_D2();
}
2019-04-16 15:53:34 +00:00
#else
outString = "be:";
if (Tbe.e == 1){
outString += String(Tbe.T, 1);
}
outString += " ae:";
if (Tae.e == 1){
outString += String(Tae.T, 1);
}
PrintS_and_D(outString, 1); //do not print serial
#endif
2019-04-01 03:02:55 +00:00
millis_displ_update = millis_now;
}
#endif
//-------------------display END
//-------------------check cycle
if( ((unsigned long)(millis_now - millis_prev) > millis_cycle ) || (millis_prev == 0) ) {
millis_prev = millis_now;
Get_Temperatures(); // wdt_reset here due to 85.0'C filtration
SaveSetpointEE();
//--------------------important logic
//check T sensors
2019-06-01 19:16:28 +00:00
if ( ( errorcode == ERR_OK ) && ( (Tae.e == 1 && Tae.T == -127 ) ||
(Tbe.e == 1 && Tbe.T == -127 ) ||
(Ttarget.e == 1 && Ttarget.T == -127 ) ||
(Tsump.e == 1 && Tsump.T == -127 ) ||
(Tci.e == 1 && Tci.T == -127 ) ||
(Tco.e == 1 && Tco.T == -127 ) ||
(Thi.e == 1 && Thi.T == -127 ) ||
(Tho.e == 1 && Tho.T == -127 ) ||
(Tbc.e == 1 && Tbc.T == -127 ) ||
(Tac.e == 1 && Tac.T == -127 ) ||
(Touter.e == 1 && Touter.T == -127 ) ||
(Ts1.e == 1 && Ts1.T == -127 ) ||
(Ts2.e == 1 && Ts2.T == -127 ) ) ) {
2019-04-01 03:02:55 +00:00
errorcode = ERR_T_SENSOR;
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS_and_D("ERR:T.sens." + String(errorcode));
#endif
2019-04-01 03:02:55 +00:00
}
//auto-clean sensor error on sensor appear
// add 1xor enable here!
if ( ( errorcode == ERR_T_SENSOR ) && ( ( (Tae.e == 1 && Tae.T != -127 ) ||(Tae.e ^1) ) &&
( (Tbe.e == 1 && Tbe.T != -127 ) ||(Tbe.e ^1) ) &&
2019-04-16 15:53:34 +00:00
( (Ttarget.e == 1 && Ttarget.T != -127) ||(Ttarget.e ^1) ) &&
2019-04-01 03:02:55 +00:00
( (Tsump.e == 1 && Tsump.T != -127 ) ||(Tsump.e ^1) ) &&
( (Tci.e == 1 && Tci.T != -127 ) ||(Tci.e ^1) ) &&
( (Tco.e == 1 && Tco.T != -127 ) ||(Tco.e ^1) ) &&
( (Thi.e == 1 && Thi.T != -127 ) ||(Thi.e ^1) ) &&
( (Tho.e == 1 && Tho.T != -127 ) ||(Tho.e ^1) ) &&
( (Tbc.e == 1 && Tbc.T != -127 ) ||(Tbc.e ^1) ) &&
( (Tac.e == 1 && Tac.T != -127 ) ||(Tac.e ^1) ) &&
( (Touter.e == 1 && Touter.T != -127 ) ||(Touter.e ^1) ) &&
( (Ts1.e == 1 && Ts1.T != -127 ) ||(Ts1.e ^1) ) &&
2019-06-01 19:16:28 +00:00
( (Ts2.e == 1 && Ts2.T != -127 ) ||(Ts2.e ^1) ) ) ) {
2019-04-01 03:02:55 +00:00
errorcode = ERR_OK;
}
//process errors
//beep N times error
if ( errorcode != ERR_OK ) {
if ( ((unsigned long)(millis_now - millis_notification) > millis_notification_interval) || millis_notification == 0 ) {
millis_notification = millis_now;
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS_and_D("Error:" + String(errorcode));
#endif
2019-04-01 03:02:55 +00:00
for ( i = 0; i < errorcode; i++) {
tone(speakerOut, ERR_HZ); delay (500);
noTone(speakerOut); delay (500);
}
}
}
2019-04-16 15:53:34 +00:00
//-------------- EEV cycle
#ifdef EEV_SUPPORT
2019-05-02 12:52:19 +00:00
/*
//v1 algo
2019-04-16 15:53:34 +00:00
if ( EEV_apulses == 0 ) {
if ( ((async_wattage < c_workingOK_wattage_min) && ((unsigned long)(millis_now - millis_eev_last_close) > EEV_CLOSEEVERY)) || millis_eev_last_close == 0 ){
PrintS_and_D("EEV: FULL closing");//!!!
if ( millis_eev_last_close != 0 ) {
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
} else {
EEV_apulses = -(EEV_MAXPULSES + EEV_CLOSE_ADD_PULSES);
}
EEV_adonotcare = 1;
EEV_fast = 1;
//delay(EEV_STOP_HOLD);
millis_eev_last_close = millis_now;
} else if (errorcode != 0 || async_wattage <= c_workingOK_wattage_min) { //err or sleep
PrintS_and_D("EEV: err or sleep");//!!!
if (EEV_cur_pos <= 0 && EEV_OPEN_AFTER_CLOSE != 0) { //set waiting pos
EEV_apulses = +EEV_OPEN_AFTER_CLOSE;
EEV_adonotcare = 0;
EEV_fast = 1;
}
if (EEV_cur_pos > 0 && EEV_cur_pos != EEV_OPEN_AFTER_CLOSE && EEV_cur_pos <= EEV_MAXPULSES) { //waiting pos. set
PrintS_and_D("EEV: close");//!!!
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
EEV_adonotcare = 1;
EEV_fast = 1;
}
} else if (errorcode == 0 && async_wattage > c_workingOK_wattage_min) {
2019-05-02 12:52:19 +00:00
T_EEV_dt = Tae.T - Tbe.T;
PrintS_and_D("EEV: driving " + String(T_EEV_dt));//!!!
2019-04-16 15:53:34 +00:00
if (EEV_cur_pos <= 0){
PrintS_and_D("EEV: full close protection");
if (EEV_OPEN_AFTER_CLOSE != 0) { //full close protection
EEV_apulses = +EEV_OPEN_AFTER_CLOSE;
2019-05-02 12:52:19 +00:00
EEV_adonotcare = 0;
2019-04-16 15:53:34 +00:00
EEV_fast = 1;
}
} else if (EEV_cur_pos > 0) {
2019-05-02 12:52:19 +00:00
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
2019-04-16 17:38:55 +00:00
PrintS_and_D("EEV: emergency closing!");//!!!
2019-04-16 15:53:34 +00:00
EEV_apulses = -EEV_EMERG_STEPS;
EEV_adonotcare = 0;
EEV_fast = 1;
2019-05-02 12:52:19 +00:00
} else if (T_EEV_dt < T_EEV_setpoint) { //too
2019-04-16 17:38:55 +00:00
PrintS_and_D("EEV: closing");//!!!
2019-04-16 15:53:34 +00:00
//EEV_apulses = -EEV_NONPRECISE_STEPS;
EEV_apulses = -1;
EEV_adonotcare = 0;
EEV_fast = 0;
2019-05-02 12:52:19 +00:00
} else if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
2019-04-16 17:38:55 +00:00
PrintS_and_D("EEV: fast opening");//!!!
2019-04-16 15:53:34 +00:00
//EEV_apulses = +EEV_NONPRECISE_STEPS;
EEV_apulses = +1;
EEV_adonotcare = 0;
EEV_fast = 1;
2019-05-02 12:52:19 +00:00
} else if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS) { //too
2019-04-16 17:38:55 +00:00
PrintS_and_D("EEV: opening");//!!!
2019-04-16 15:53:34 +00:00
EEV_apulses = +1;
EEV_adonotcare = 0;
EEV_fast = 0;
2019-05-02 12:52:19 +00:00
} else if (T_EEV_dt > T_EEV_setpoint) { //ok
2019-04-16 17:38:55 +00:00
PrintS_and_D("EEV: OK");//!!!
2019-04-16 15:53:34 +00:00
//
}
}
off_EEV();
}
}
2019-05-02 12:52:19 +00:00
*/
//v1.1 algo
if ( errorcode == 0 && async_wattage > c_workingOK_wattage_min && EEV_cur_pos > 0 ) {
T_EEV_dt = Tae.T - Tbe.T;
#ifdef EEV_DEBUG
PrintS("EEV Td: " + String(T_EEV_dt));
#endif
if ( EEV_apulses >= 0 && EEV_cur_pos >= EEV_MINWORKPOS) {
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
#ifdef EEV_DEBUG
PrintS(F("EEV: 1 emergency closing!"));
#endif
EEV_apulses = -1;
EEV_adonotcare = 0;
EEV_fast = 1;
} else if (T_EEV_dt < T_EEV_setpoint) { //too
#ifdef EEV_DEBUG
PrintS(F("EEV: 2 closing"));
#endif
//EEV_apulses = -EEV_NONPRECISE_STEPS;
EEV_apulses = -1;
EEV_adonotcare = 0;
EEV_fast = 0;
}
//faster open when needed, condition copypasted (see EEV_apulses <= 0)
if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
#ifdef EEV_DEBUG
PrintS(F("EEV: 3 enforce faster opening"));
#endif
//EEV_apulses = +EEV_NONPRECISE_STEPS;
//EEV_apulses = +1;
EEV_adonotcare = 0;
EEV_fast = 1;
}
}
if ( EEV_apulses <= 0 ) {
if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
#ifdef EEV_DEBUG
PrintS(F("EEV: 4 fast opening"));
#endif
//EEV_apulses = +EEV_NONPRECISE_STEPS;
EEV_apulses = +1;
EEV_adonotcare = 0;
EEV_fast = 1;
} else if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS) { //too
#ifdef EEV_DEBUG
PrintS(F("EEV: 5 opening"));
#endif
EEV_apulses = +1;
EEV_adonotcare = 0;
EEV_fast = 0;
} else if (T_EEV_dt > T_EEV_setpoint) { //ok
#ifdef EEV_DEBUG
PrintS(F("EEV: 6 OK"));
#endif
//
}
//faster closing when needed, condition copypasted (see EEV_apulses >= 0)
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
#ifdef EEV_DEBUG
PrintS(F("EEV: 7 enforce faster closing!"));
#endif
//EEV_apulses = -EEV_EMERG_STEPS;
EEV_adonotcare = 0;
EEV_fast = 1;
}
}
off_EEV();
}
if ( EEV_apulses == 0 ) {
if ( ((async_wattage < c_workingOK_wattage_min) && ((unsigned long)(millis_now - millis_eev_last_close) > EEV_CLOSEEVERY)) || millis_eev_last_close == 0 ){ //close every 24h by default
#ifdef EEV_DEBUG
PrintS(F("EEV: 10 FULL closing"));
#endif
if ( millis_eev_last_close != 0 ) {
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
} else {
EEV_apulses = -(EEV_MAXPULSES + EEV_CLOSE_ADD_PULSES);
}
EEV_adonotcare = 1;
EEV_fast = 1;
//delay(EEV_STOP_HOLD);
millis_eev_last_close = millis_now;
} else if (errorcode != 0 || async_wattage < c_workingOK_wattage_min) { //err or sleep
if (EEV_cur_pos > 0 && EEV_cur_pos > EEV_OPEN_AFTER_CLOSE) { //waiting pos. set
#ifdef EEV_DEBUG
PrintS(F("EEV: 11 close before open"));
#endif
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
EEV_adonotcare = 1;
EEV_fast = 1;
}
}
off_EEV();
}
if ( EEV_apulses == 0 && async_wattage < c_workingOK_wattage_min && EEV_cur_pos < EEV_OPEN_AFTER_CLOSE) {
#ifdef EEV_DEBUG
PrintS(F("EEV: 12 full close protection"));
#endif
if (EEV_OPEN_AFTER_CLOSE != 0) { //full close protection
EEV_apulses = EEV_OPEN_AFTER_CLOSE - EEV_cur_pos;
EEV_adonotcare = 0;
EEV_fast = 1;
}
off_EEV();
}
if ( EEV_apulses == 0 && async_wattage >= c_workingOK_wattage_min && EEV_cur_pos < EEV_MINWORKPOS) {
#ifdef EEV_DEBUG
PrintS(F("EEV: 13 open to work"));
#endif
2019-06-01 19:16:28 +00:00
if (EEV_MINWORKPOS != 0) { //full close protection
2019-05-02 12:52:19 +00:00
EEV_apulses = EEV_MINWORKPOS - EEV_cur_pos;
EEV_adonotcare = 0;
EEV_fast = 1;
}
off_EEV();
}
2019-04-16 15:53:34 +00:00
if ( ((unsigned long)(millis_now - millis_eev_last_on) > 10000) || millis_eev_last_on == 0 ) {
2019-05-02 12:52:19 +00:00
//PrintS_and_D("EEV: ON/OFF");
2019-04-16 15:53:34 +00:00
on_EEV();
2019-05-02 12:52:19 +00:00
//delay(30);
//off_EEV(); //off_EEV called everywhere takes care of it
2019-04-16 15:53:34 +00:00
millis_eev_last_on = millis_now;
}
#endif
//-------------- EEV cycle END
#ifndef EEV_ONLY
//process heatpump sump heater
if (Tsump.e == 1) {
if ( Tsump.T < cT_sump_heat_threshold && sump_heater_state == 0 && Tsump.T != -127) {
sump_heater_state = 1;
} else if (Tsump.T >= cT_sump_heat_threshold && sump_heater_state == 1) {
sump_heater_state = 0;
} else if (Tsump.T == -127) {
sump_heater_state = 0;
}
2019-04-16 20:31:36 +00:00
halifise();
2019-04-01 03:02:55 +00:00
}
2019-04-16 15:53:34 +00:00
//main logic
if (_1st_start_sleeped == 0) {
2019-05-02 12:52:19 +00:00
//PrintS_and_D("!!!!sleep disabled!!!!");
//_1st_start_sleeped = 1;
2019-04-16 15:53:34 +00:00
if ( (millis_now < poweron_pause) && (_1st_start_sleeped == 0) ) {
PrintS_and_D("Wait: " + String(((poweron_pause-millis_now))/1000) + " s.");
return;
2019-04-01 03:02:55 +00:00
} else {
2019-04-16 15:53:34 +00:00
_1st_start_sleeped = 1;
2019-04-01 03:02:55 +00:00
}
}
2019-04-16 15:53:34 +00:00
//process_heatpump:
//start if
// (last_on > N or not_started_yet)
// and (no errors)
// and (t hot out < t target + heat_delta_min)
// and (sump t > min'C)
// and (sump t < max'C)
// and (t watertank < target)
// and (t after evaporator > after evaporator min)
// and (t cold in > cold min)
// and (t cold out > cold min)
if ( heatpump_state == 0 &&
(((unsigned long)(millis_now - millis_last_heatpump_on) > mincycle_poweroff) || (millis_last_heatpump_on == 0) ) &&
2019-05-02 12:52:19 +00:00
//( tr_hot_out < (tr_sens_1 + cT_hotcircle_delta_min) ) &&
2019-04-16 15:53:34 +00:00
errorcode == 0 &&
( (Tsump.e == 1 && Tsump.T > cT_sump_min) || (Tsump.e^1)) &&
( (Tsump.e == 1 && Tsump.T < cT_sump_max) || (Tsump.e^1)) &&
//t1_sump > t2_cold_in && ???
Ttarget.T < T_setpoint && //was room here, change to advanced algo with room temperature
( (Tae.e == 1 && Tae.T > cT_after_evaporator_min) || (Tae.e^1)) &&
( (Tbc.e == 1 && Tbc.T < cT_before_condenser_max) || (Tbc.e^1)) &&
( (Tci.e == 1 && Tci.T > cT_cold_min) || (Tci.e^1)) &&
( (Tco.e == 1 && Tco.T > cT_cold_min) || (Tco.e^1)) ) {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Start"));
#endif
2019-04-16 15:53:34 +00:00
millis_last_heatpump_off = millis_now;
heatpump_state = 1;
}
//stop if
// ( (last_off > N) and (t watertank > target) )
if ( heatpump_state == 1 && ((unsigned long)(millis_now - millis_last_heatpump_off) > mincycle_poweron) && (Ttarget.T > T_setpoint)) {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Normal stop"));
#endif
2019-04-16 15:53:34 +00:00
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
}
2019-04-01 03:02:55 +00:00
2019-04-16 15:53:34 +00:00
//process_hot_side_pump:
//start if (heatpump_enabled)
//stop if (heatpump_disabled and (t hot out or in < t target + heat delta min) )
if ( (heatpump_state == 1) && (hotside_circle_state == 0) ) {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Hot WP ON"));
#endif
2019-04-01 03:02:55 +00:00
hotside_circle_state = 1;
}
2019-04-16 15:53:34 +00:00
if ( (heatpump_state == 0) && (hotside_circle_state == 1) ) {
if ( (deffered_stop_hotcircle != 0 && ((unsigned long)(millis_now - millis_last_heatpump_on) > deffered_stop_hotcircle) ) ) {
2019-05-02 12:52:19 +00:00
if ( (Tho.e == 1 && Tho.T < (Ttarget.T + cT_hotcircle_delta_min)) ||
(Thi.e == 1 && Thi.T < (Ttarget.T + cT_hotcircle_delta_min)) ) {
#ifdef RS485_HUMAN
PrintS(F("Hot WP OFF 1"));
#endif
2019-04-16 15:53:34 +00:00
hotside_circle_state = 0;
} else {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Hot WP OFF 2"));
#endif
2019-04-16 15:53:34 +00:00
hotside_circle_state = 0;
}
}
2019-04-01 03:02:55 +00:00
}
2019-04-16 15:53:34 +00:00
//heat if we can, just in case, ex. if lost power
if ( (hotside_circle_state == 0) &&
2019-05-02 12:52:19 +00:00
( Tho.e == 1 && Tho.T > (Ttarget.T + cT_hotcircle_delta_min) ) ||
( Thi.e == 1 && Thi.T > (Ttarget.T + cT_hotcircle_delta_min) ) ) {
#ifdef RS485_HUMAN
PrintS(F("Hot WP ON"));
#endif
2019-04-16 15:53:34 +00:00
hotside_circle_state = 1;
}
//process_cold_side_pump:
//start if (heatpump_enabled)
//stop if (heatpump_disbled)
if ( (heatpump_state == 1) && (coldside_circle_state == 0) ) {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Cold WP ON"));
#endif
2019-04-16 15:53:34 +00:00
coldside_circle_state = 1;
}
if ( (heatpump_state == 0) && (coldside_circle_state == 1) ) {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Cold WP OFF"));
#endif
2019-04-16 15:53:34 +00:00
coldside_circle_state = 0;
}
//protective_cycle:
//stop if
// (error)
// (t hot out > hot out max)
// (sump t > max'C)
// or (t after evaporator < after evaporator min)
// or (t cold in < cold min)
// or (t cold out < cold min)
//
if ( heatpump_state == 1 &&
( errorcode != 0 ||
(Tho.e == 1 && Tho.T > cT_hotout_max) ||
(Tsump.e == 1 && Tsump.T > cT_sump_max) ||
(Tae.e == 1 && Tae.T < cT_after_evaporator_min) ||
(Tbc.e == 1 && Tbc.T > cT_before_condenser_max) ||
(Tci.e == 1 && Tci.T < cT_cold_min ) ||
(Tco.e == 1 && Tco.T < cT_cold_min) ) ) {
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS(F("Protective stop"));
#endif
2019-04-16 15:53:34 +00:00
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
digitalWrite(RELAY_HEATPUMP, heatpump_state);
}
//alive_check_cycle_after_5_mins:
//error if
2019-05-02 12:52:19 +00:00
//v1.3: not error, just poweroff all
2019-04-16 15:53:34 +00:00
// or (t cold in - t cold out < t workingok min)
// or (t hot out - t hot in < t workingok min)
// or (sump t < 25'C)
// or wattage too low
2019-05-02 12:52:19 +00:00
2019-04-16 15:53:34 +00:00
if ( heatpump_state == 1 && ((unsigned long)(millis_now - millis_last_heatpump_off) > 300000) ) {
//cold side processing simetimes works incorrectly, after long period of inactivity, due to T inertia on cold tube sensor, commented out
//if ( ( errorcode == ERR_OK ) && ( tr_cold_in - tr_cold_out < cT_workingOK_cold_delta_min ) ) {
// errorcode = ERR_COLD_PUMP;
//}
//if ( ( errorcode == ERR_OK ) && ( Tho.e == 1 && Thi.e == 1 && (Tho.T - Thi.T < cT_workingOK_hot_delta_min )) ) {
// errorcode = ERR_HOT_PUMP;
//}
if ( ( errorcode == ERR_OK ) && ( Tsump.e == 1 && Tsump.T < cT_workingOK_sump_min ) ) {
2019-05-02 12:52:19 +00:00
//errorcode = ERR_HEATPUMP;
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
2019-04-16 15:53:34 +00:00
}
if ( ( errorcode == ERR_OK ) && ( async_wattage < c_workingOK_wattage_min ) ) {
2019-05-02 12:52:19 +00:00
//errorcode = ERR_WATTAGE;
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
2019-04-16 15:53:34 +00:00
}
2019-05-02 12:52:19 +00:00
digitalWrite(RELAY_HEATPUMP, heatpump_state);
}
2019-04-16 15:53:34 +00:00
//disable pump by error
if ( errorcode != ERR_OK ) {
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
digitalWrite(RELAY_HEATPUMP, heatpump_state);
2019-05-02 12:52:19 +00:00
#ifdef RS485_HUMAN
PrintS("Error stop: " + String(errorcode, HEX));
#endif
2019-04-16 15:53:34 +00:00
}
//!!! self-test
///heatpump_state = 1;
//write states to relays
digitalWrite (RELAY_HEATPUMP, heatpump_state);
digitalWrite (RELAY_COLDSIDE_CIRCLE, coldside_circle_state);
2019-04-16 20:31:36 +00:00
halifise();
2019-04-16 15:53:34 +00:00
#endif
2019-04-01 03:02:55 +00:00
}
if (RS485Serial.available() > 0) {
//RS485Serial.println("some on serial.."); //!!!debug
#ifdef RS485_HUMAN
if (RS485Serial.available()) {
inChar = RS485Serial.read();
//RS485Serial.print(inChar); //!!!debug
if ( inChar == 0x1B ) {
skipchars += 3;
inChar = 0x00;
millis_escinput = millis();
}
if ( skipchars != 0 ) {
millis_charinput = millis();
//if (millis_escinput + 2 > millis_charinput)
if ((unsigned long)(millis_charinput - millis_escinput) < 16*2 ) { //2 chars for 2400
if (inChar != 0x7e) {
skipchars -= 1;
}
if (inChar == 0x7e) {
skipchars = 0;
}
if (inChar >= 0x30 && inChar <= 0x35) {
skipchars += 1;
}
inChar = 0x00;
} else {
skipchars = 0;
}
}
//- RS485_HUMAN: remote commands +,-,G,0x20/?/Enter
switch (inChar) {
case 0x00:
break;
case 0x20:
_PrintHelp();
break;
case 0x3F:
_PrintHelp();
break;
case 0x0D:
_PrintHelp();
break;
case 0x2B:
Inc_T();
break;
case 0x2D:
Dec_T();
break;
2019-04-16 15:53:34 +00:00
case 0x3C:
Dec_E();
break;
case 0x3E:
Inc_E();
break;
2019-04-01 03:02:55 +00:00
case 0x47:
PrintStats_Serial();
break;
case 0x67:
PrintStats_Serial();
break;
}
}
#endif
#ifdef RS485_PYTHON
index = 0;
while (RS485Serial.available() > 0) { // Don't read unless you know there is data
if(index < 49) { // size of the array minus 1
inChar = RS485Serial.read(); // Read a character
inData[index] = inChar; // Store it
index++; // Increment where to write next
inData[index] = '\0'; // clear next symbol, null terminate the string
delayMicroseconds(80); //80 microseconds - the best choice at 9600, "no answer"disappeared
//40(20??) microseconds seems to be good, 9600, 49 symbols
//
} else { //too long message! read it to nowhere
inChar = RS485Serial.read();
delayMicroseconds(80);
//break; //do not break if symbols!!
}
}
//!!!debug, be carefull, can cause strange results
2019-04-16 15:53:34 +00:00
/*
if (inData[0] != 0x00) {
2019-04-01 03:02:55 +00:00
RS485Serial.println("-");
RS485Serial.println(inData);
RS485Serial.println("-");
}*/
//or this debug
/*
digitalWrite(SerialTxControl, RS485Transmit);
delay(10);
RS485Serial.println(inData);
RS485Serial.flush();
RS485Serial.println(index);
*/
//ALL lines must be terminated with \n!
if ( (inData[0] == hostID) && (inData[1] == devID) ) {
// COMMANDS:
// G (0x47): (G)et main data
// TNN.NN (0x54): set aim (T)emperature
digitalWrite(SerialTxControl, RS485Transmit);
delay(1);
//PrintS_and_D(freeMemory());
outString = "";
outString += devID;
outString += hostID;
outString += "A "; //where A is Answer, space after header
if ( (inData[2] == 0x47 ) ) {
//PrintS_and_D("G");
//WARNING: this procedure can cause "NO answer" effect if no or few T sensors connected
outString += "{";
2019-04-16 15:53:34 +00:00
outString += "\"E1\":" + String(errorcode);
2019-04-01 03:02:55 +00:00
if (Ts1.e == 1) {
2019-04-16 15:53:34 +00:00
outString += ",\"TS1\":" + String(Ts1.T);
2019-04-01 03:02:55 +00:00
}
if (Tsump.e == 1) {
outString += ",\"TS\":" + String(Tsump.T);
}
if (Tho.e == 1) {
outString += ",\"THO\":" + String(Tho.T);
}
if (Tae.e == 1) {
outString += ",\"TAE\":" + String(Tae.T);
}
char *outChar=&outString[0];
2019-06-01 19:16:28 +00:00
RS485Serial.write(outChar); //dirty hack to transfer long string
2019-04-01 03:02:55 +00:00
RS485Serial.flush();
delay (1); //lot of errors without delay
outString = "";
if (Tbe.e == 1) {
outString += ",\"TBE\":" + String(Tbe.T);
}
if (Touter.e == 1) {
outString += ",\"TO\":" + String(Touter.T);
}
if (Tco.e == 1) {
outString += ",\"TCO\":" + String(Tco.T);
}
outString += ",\"W1\":" + String(async_wattage);
2019-04-16 15:53:34 +00:00
#ifndef EEV_ONLY
2019-05-02 12:52:19 +00:00
outString += ",\"A1\":" + String(T_setpoint); //(A)im (target)
2019-04-16 15:53:34 +00:00
outString += ",\"RP\":" + String(heatpump_state*RELAY_HEATPUMP);
#endif
2019-04-01 03:02:55 +00:00
if (Tci.e == 1) {
outString += ",\"TCI\":" + String(Tci.T);
}
2019-06-01 19:16:28 +00:00
RS485Serial.write(outChar); //dirty hack to transfer long string
2019-04-01 03:02:55 +00:00
RS485Serial.flush();
delay (1); //lot of errors without delay
outString = "";
if (Thi.e == 1) {
outString += ",\"THI\":" + String(Thi.T);
}
2019-04-16 15:53:34 +00:00
#ifndef EEV_ONLY
2019-05-02 12:52:19 +00:00
outString += ",\"RSH\":" + String(sump_heater_state*3);
outString += ",\"RH\":" + String(hotside_circle_state*2);
outString += ",\"RC\":" + String(coldside_circle_state*1);
2019-04-16 15:53:34 +00:00
#endif
2019-04-01 03:02:55 +00:00
if (Tbc.e == 1) {
outString += ",\"TBC\":" + String(Tbc.T);
}
RS485Serial.write(outChar); //dirty hack to transfer long string
RS485Serial.flush();
delay (1); //lot of errors without delay
outString = "";
if (Ts2.e == 1) {
outString += ",\"TS2\":" + String(Ts2.T);
}
if (Tac.e == 1) {
outString += ",\"TAC\":" + String(Tac.T);
}
2019-04-16 15:53:34 +00:00
if (Ttarget.e == 1) {
outString += ",\"TT\":" + String(Ttarget.T);
}
#ifdef EEV_SUPPORT
2019-05-02 12:52:19 +00:00
outString += ",\"EEVP\":" + String(EEV_cur_pos);
outString += ",\"EEVA\":" + String(T_EEV_setpoint);
2019-04-16 15:53:34 +00:00
#endif
2019-04-01 03:02:55 +00:00
outString += "}";
2019-04-16 15:53:34 +00:00
} else if ( (inData[2] == 0x54 ) || (inData[2] == 0x45 )) { //(T)arget or (E)EV target format NN.NN, text
2019-04-01 03:02:55 +00:00
if ( isDigit(inData[ 3 ]) && isDigit(inData[ 4 ]) && (inData[ 5 ] == 0x2e) && isDigit(inData[ 6 ]) && isDigit(inData[ 7 ]) && ( ! isDigit(inData[ 8 ])) ) {
tone(speakerOut, 2250);
delay (100); // like ups power on
noTone(speakerOut);
char * carray = &inData[ 3 ];
tempdouble = atof(carray);
2019-04-16 15:53:34 +00:00
if (inData[2] == 0x54 ){
if (tempdouble > cT_setpoint_max) {
outString += "{\"err\":\"too hot!\"}";
} else if (tempdouble < 1.0) {
outString += "{\"err\":\"too cold!\"}";
} else {
T_setpoint = tempdouble;
outString += "{\"result\":\"ok, new value is: ";
outString += String(T_setpoint);
outString += "\"}";
}
}
if (inData[2] == 0x45 ) {
if (tempdouble > 10.0) { //!!!!!!! hardcode !!!
outString += "{\"err\":\"too hot!\"}";
} else if (tempdouble < 0.1) { //!!!!!!! hardcode !!!
outString += "{\"err\":\"too cold!\"}";
} else {
T_EEV_setpoint = tempdouble;
outString += "{\"result\":\"ok, new EEV value is: ";
outString += String(T_EEV_setpoint);
outString += "\"}";
}
2019-04-01 03:02:55 +00:00
}
} else {
outString += "{\"err\":\"NaN, format: NN.NN\"}";
}
} else {
//default, just for example
2019-06-01 19:16:28 +00:00
outString += "{\"err\":\"no_command\"}";
2019-04-01 03:02:55 +00:00
}
//crc.integer = CRC16.xmodem((uint8_t& *) outString, outString.length());
//outString += (crc, HEX);
outString += "\n";
char *outChar=&outString[0];
RS485Serial.write(outChar);
}
index = 0;
for (i=0;i<49;i++) { //clear buffer
inData[i]=0;
}
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
delay(1);
#endif
}
}