/*
Valden Heat Pump.
Heat Pump Controller firmware.
https://github.com/OpenHP/
Copyright (C) 2018-2021 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 .
*/
//-----------------------USER OPTIONS-----------------------
//#define SELFTEST_RELAYS_LEDS_SPEAKER //speaker and relays QA test, uncomment to enable
//#define SELFTEST_EEV //EEV QA test, uncomment to enable
//#define SELFTEST_T_SENSORS //temperature sensors QA test, uncomment to enable
//communication protocol with external world
//#define RS485_JSON 1 //json, external systems integration
//#define RS485_HUMAN 2 //RS485 is used in the same way as the local console, warning: Use only if 2 devices (server and this controller) connected to the same RS485 line
#define RS485_MODBUS 3 //default, MODBUS via RS485, connection to the display (both sensor or 1602, see https://GitHub.com/OpenHP/Display/) or connection to any other MODBUS application or device
//system type, comment both if HP with EEV
//#define EEV_ONLY //Valden controller as EEV controller: NO target T sensor. No relays. Oly EEV. Sensors required: Tae, Tbe, current sensor. Additional T sensors can be used but not required.
//#define NO_EEV //capillary tube or TXV, EEV not used
//which sensor is used to check setpoint, uncomment one of options
#define SETPOINT_THI //"warm floor" scheme: "hot in" (Thi) temperature used as setpoint
//#define SETPOINT_TS1 //"swimming pool" or "water tank heater" scheme: "sensor 1" (Ts1) is used as setpoint and located somewhere in a water tank
#define HUMAN_AUTOINFO 30000 //print stats to console, every milliseconds
#define WATCHDOG //disable for older bootloaders
//-----------------------USER OPTIONS END-----------------------
//-----------------------Fine Tuning OPTIONS-----------------------
//next sections: advanced options
//-----------------------T Sensors -----------------------
//temperature sensors used in a system, comment to disable
#define T_cold_in; //cold side (heat source) inlet sensor
#define T_cold_out; //cold side outlet sensor
#define T_before_evaporator; //"before" and "after evaporator" sensors required to control EEV, both "EEV_ONLY" and "full" schemes
#define T_after_evaporator; //"before" and "after evaporator" sensors required to control EEV, both "EEV_ONLY" and "full" schemes
//#define T_separator_gas; //no longer used (PCB 1.3 MI +) artifact from experimental scheme with separator
//#define T_separator_liquid; //no longer used (PCB 1.3 MI +) artifact from experimental scheme with separator
//#define T_before_valve; //no longer used (PCB 1.3 MI +) artifact from experimental scheme with separator
//#define T_suction; //no longer used (PCB 1.3 MI +) artifact from experimental scheme with separator
#ifdef SETPOINT_TS1
#define T_sensor_1; //T values from the additional sensor S1 used as a "setpoint" in "pool" or "water tank heater" schemes
#endif
//!!!
#define T_sensor_2; //additional sensor, any source; for example, outdoor temperature, in-case temperature, and so on
#define T_crc; //if defined, enables the crankcase T sensor and crankcase heater on the relay "Crankcase heater"
//#define T_regenerator; //an additional sensor, the regenerator temperature sensor (inlet or outlet or housing); used only to obtain a temperature data if necessary
#define T_afrer_condenser; //after condenser (and before valve)
//!!!#define T_before_condenser; //before condenser (discharge)
#define T_hot_out; //hot side outlet
//In full scheme Hot IN required! Optional in "EEV_ONLY" scheme (see "EEV_ONLY" option),
#define T_hot_in; //hot side inlet
//-----------------------TEMPERATURES-----------------------
#define MAGIC 0x66; //change this value if you want to rewrite the T setpoint in EEPROM
#define T_SETPOINT 26.0; //This is a predefined target temperature value (start temperature). EEPROM-saved. Ways to change this value: 1. Console command 2. Change the "setpoint" on a display 3. Change value here AND change "magic number" 4. JSON command
#define T_SETPOINT_MAX 48.0; //maximum "setpoint" temperature that an ordinary user can set
#define T_SETPOINT_MIN 10.0; //min. "setpoint" temperature that an ordinary user can set, lower values not recommended until antifreeze fluids at hot side used.
#define T_CRANKCASE_MIN 8.0; //compressor (crankcase) min. temperature, HP will not start if T lower
#define T_CRANKCASE_MAX 110.0; //compressor (crankcase) max. temperature, overheating protection, HP will stop if T higher
#define T_CRANKCASE_HEAT_THRESHOLD 16.0; //crankcase heater threshold, the compressor heater will be powered on if T lower
#define T_WORKINGOK_CRANKCASE_MIN 25.0; //compressor temperature: additional check. HP will stop if T is lower than this value after 5 minutes of work. Do not set the value too high to ensure normal operation after long pauses.
#define T_BEFORE_CONDENSER_MAX 108.0; //discharge MAX, system stops if discharge higher
#define T_COLDREF_MIN -14.0; //suction min., HP stops if T lower, cold side (glycol) loop freeze protection and compressor protection against liquid
#define T_BEFORE_EVAP_WORK_MIN -25.5; //!!!before evaporator (after valve) min. T; can be very low for a few minutes after a startup, ex: capillary tube in some conditions; and for all systems: after long shut-off, lack of refrigerant, 1st starts, and many others
#define T_COLD_MIN -15.5; //cold side (glycol) loop freeze protection: HP stops if inlet or outlet temperature lower
#define T_HOT_MAX 50.0; //hot loop: HP stops if hot side inlet or outlet temperature higher than this threshold
//#define T_REG_HEAT_THRESHOLD 17.0; //no longer used (PCB 1.3 MI +) artifact from experimental scheme with separator
//#define T_HOTCIRCLE_DELTA_MIN 2.0; //not used since ~FW v1.6, "water heater with intermediate heat exchanger" scheme, where Ts1 == "sensor in water"; hot side CP will be switched on if "Ts1 - hot_out > T_HOTCIRCLE_DELTA_MIN"
//-----------------------WATTS AND CYCLES TIMES-----------------------
//time: milliseconds, power: watts
#define MAX_WATTS 1000.0 + 70.0 + 80.0 //power limit, watt, HP stops if exceeded, examples: // installation1: compressor 165: 920 Watts, + 35 watts 25/4 circ. pump at 1st speed + 53 watts 25/4 circ. pump at 2nd speed
// installation2: compressor unk: ~1000 + hot CP 70 + cold CP 80 = 1150 watts
// installation3: and so on
#define POWERON_PAUSE 300000 //after power on: wait 5 minutes before starting HP (power faults protection)
#define MINCYCLE_POWEROFF 600000 //after a normal compressor stop: 10 minutes pause (max 99999 seconds)
#define MINCYCLE_POWERON 3600000 //after compressor start: minimum compressor operation time, i.e. work time is not less than this value (or more, depending on the setpoint temperature) 60 minutes = 3.6 KK 120mins = 5.4 kK.
#define POWERON_HIGHTIME 7000 //after compressor start: defines time when power consumption can be 3 times greater than normal, 7 sec. by default
#define COLDCIRCLE_PREPARE 90000 //before compressor start: power on cold CP and wait 90 sec.; if false start: CP will off twice this time; and (hotcircle_stop_after - this_value) must be > hotcircle_check_prepare or HP will go sleep cycle instead of start
#define DEFFERED_STOP_HOTCIRCLE 1200000 //after compressor stop: wait 20 minutes, if no need to start compressor: stop hot WP; value must be > 0
#define HOTCIRCLE_START_EVERY 2400000 //while pauses: pump on "hot side" starts every 40 minutes (by default) (max 9999 seconds) to circulate water and get exact temperature reading, option used if "warm floor" installation (Thi as setpoint)...
#define HOTCIRCLE_CHECK_PREPARE 150000 //while pauses: ...and wait for temperature stabilization 2.5 minutes (by default), after that do setpoint checks...
#define HOTCIRCLE_STOP_AFTER (HOTCIRCLE_CHECK_PREPARE + COLDCIRCLE_PREPARE + 30000) //...and then stop after few minutes of circulating, if temperature is high and no need to start compressor; value must be check_prepare + coldcircle_prepare + 30 seconds (or more)
//-----------------------EEV-----------------------
//If you are using a capillary tube or TXV: simply skip next section.
//Depending on how many milliseconds allocated per step, the speed of automatic tuning will change.
//Remember that your refrigeration system reaction on every step is not immediate. The system reacts after a few minutes, sometimes after tens of minutes.
#define EEV_MAXPULSES 250 //max steps, 250 is tested for sanhua 1.3
//steps tuning: milliseconds per fast and slow (precise) steps
#define EEV_PULSE_FCLOSE_MILLIS 20 //(20 tube evaporator) fast closing, closing on danger (milliseconds per step)
#define EEV_PULSE_CLOSE_MILLIS 45000 //(50000 tube evaporator) accurate closing while the compressor works (milliseconds per step)
#define EEV_PULSE_WOPEN_MILLIS 20 //(20 tube evaporator) standby (waiting) pos. set (milliseconds per step)
#define EEV_PULSE_FOPEN_MILLIS 1400 //(1300 tube evaporator) fast opening, fast search (milliseconds per step)
#define EEV_PULSE_OPEN_MILLIS 30000 //(60000 tube evaporator) accurate opening while the compressor works (milliseconds per step)
#define EEV_STOP_HOLD 500 //0.1..1sec for Sanhua hold time (milliseconds per step)
#define EEV_CLOSEEVERY 86400000 //86400000: EEV full close (zero calibration) every 24 hours, executed while HP is NOT working (milliseconds per cycle)
//positions
#define EEV_CLOSE_ADD_PULSES 8 //read below, additional steps after zero position while full closing
#define EEV_OPEN_AFTER_CLOSE 45 //0 - set the zero position, then add EEV_CLOSE_ADD_PULSES (zero insurance, read EEV guides for this value) and stop, EEV will be in zero position.
//N - set the zero position, then add EEV_CLOSE_ADD_PULSES, than open EEV on EEV_OPEN_AFTER_CLOSE pulses
//i.e. it's a "waiting position" while HP isn't working, value must be <= MINWORKPOS
#define EEV_MINWORKPOS 50 //position will be not less during normal work, open EEV to this position after compressor start
//temperatures
#define EEV_PRECISE_START 8.6 //(8.6 tube evaporator) precise tuning threshold: make slower pulses if (real_diff-target_diff) less than this value. Used for fine auto-tuning
#define EEV_EMERG_DIFF 1.7 //(2.5 tube evaporator) liquid at suction threshold: if dangerous condition occurred, real_diff =< (target_diff - EEV_EMERG_DIFF) then EEV will be closed to min. work position //Ex: EEV_EMERG_DIFF = 2.0, target diff 5.0, if real_diff =< (5.0 - 2.0) then EEV will be closed to EEV_MINWORKPOS
#define EEV_HYSTERESIS 0.45 //(0.6 tube evaporator) hysteresis, to stop fine tuning: must be less than EEV_PRECISE_START, ex: target difference = 4.0, hysteresis = 0.3, no EEV pulses will be done while real difference in range 4.0..4.3
#define EEV_TARGET_TEMP_DIFF 3.6 //(3.6 tube evaporator) target difference between Before Evaporator and After Evaporator, the head of the whole algorithm
//additional options
#define EEV_REOPENLAST 1 ///1 = reopen to last position on compressor start, useful for ordinary schemes with everyday working cycles, 0 = not
#define EEV_REOPENMINTIME 40000 //after system start: min. delay between "min. work pos." (must be > 0 in this case and > waiting position) set and reopening start
//#define EEV_MANUAL //comment to disable, manual set of EEV position via a console; warning: this option will stop all EEV auto-activities, including zero position find procedure; so this option not recommended: switch auto/manual mode from a console
//do not use next option if you're not sure what are you doing
//#define EEV_DEBUG //debug, useful during system fine-tuning, works both with local serial and RS485_HUMAN
//-----------------------ADDRESSES-----------------------
const char devID = 0x45; //used only if JSON communication, does not matter for MODBUS and Valden display https://github.com/OpenHP/Display/
const char hostID = 0x30; //used only if JSON communication, not used for MODBUS
//-----------------------OTHER-----------------------
#define MAX_SEQUENTIAL_ERRORS 15 //max cycles to wait auto-clean error, ex: T sensor appears, stop compressor after counter exceeded (millis_cycle * MAX_SEQUENTIAL_ERRORS)
//-----------------------Fine Tuning OPTIONS END -----------------------
//-----------------------changelog-----------------------
/*
v1.0, 01 Sep 2019:
+ initial version, hardware and software branch ready
v1.1: 21 Sep 2019:
+ Dev and Host ID to header
v1.2: 20 Dec 2019:
+- ?seems to be fixed minor bug while HP stopped: wattage is 0, if tCrc < T_CRANKCASE_HEAT_THRESHOLD and may be few sensors absence
+ min_user_t/max_user_t to header
v1.3: 05 Jan 2020:
+ manual EEV mode (high priority, ex: new system 1st starts and charge)
+ rs485_modbus
+ reopen to last EEV value at startup
v1.4: 22 Jan 2020
+ crankcase naming
v1.5: 05 Jun 2020
+ minor modbus updates
v1.6: 09 Dec 2020
+ NO_EEV option
+ some variables renames
+ Tho instead of Thi (stop conditions) bugfix
+ Last Start Message added
v1.7: 03 Feb 2021
+ 1.3 PCB revision support, previous revisions also supported
+ enable cold circle if tci < col_min (circulate ground loop, if outdoor installation and very cold and deep freeze)
+ inputs support
+ add option "Thi" and "Ts1" to header, enable Ts1 by this option
+ temperature check after start of hot side circle + 5 mins for Thi target
v1.8: 06 Feb 2021
+ very rare case: 0.0 readings, 2-3 attempts then pass 0.0
+ countdown for compressor relay after cold CP start (stab. cold loop T)
+ self-test options to header
v1.9-1.11: 25-27 Feb 2021:
+ lot of small workflow logic and user terminal changes
v1.12: 21 Mar 2021:
+ TS1/THO #define way fix
+ CWP and HWP prepare optimisation
v1.13: 26 Mar 2021:
+ rounding error via Modbus found and fixed
//TODO:
? lower bit resolution for all sensors, except Tbe, Tae, Ts1 ?
? poss. DoS: infinite read to nowhere, fix it, set finite counter (ex: 200)
? add "heater start" and "cold circle start" and "not start HP" if t_crc < t_coldin/coldout(?)/tae/tbe(?) + 2.0
? ref. migration protection for summer season with long waiting periods: start cold circle and crankcase heater if tCrc =< tci+1, add option to header
? EEV manual mode and position by RS485 python or modbus command ?
? add speaker and err code for ""ERR: no Tae or Tbe for EEV!""
? deffered HWP stop: check HP stop cause, stop HWP if protective/error stop
? wclose and fclose to EEV
? valve_4way
? rewite re-init proc from MAGIC to another way
? EEV: target to EEPROM (?? no need ?)
? EEV: define maximum working position
*/
//-----------------------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
//
//Speaker
//
// 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
/*
relay 1: heat pump
relay 2: hot side circulator pump
relay 3: cold side circulator pump
relay 4: crankcase heater
relay 5: (1.3+: not used anymore)
relay 6: reserved
relay 7: reserved
T sensors:
0 cold_in;
1 cold_out;
2 before_evaporator;
3 after_evaporator;
4 separator_gas; //if flooded evaporator: separator out
5 separator_liquid; //if flooded evaporator: separator out
6 before_valve; //before expansion valve, if regenerator used
7 suction; //compressor suction, if regenerator
8 sensor_1; //additional sensor 1
9 sensor_2; //additional sensor 2
A crankcase; //compressor case
B regenerator;
C afrer_condenser;
D before_condenser;
E hot_out;
F hot_in;
*/
String fw_version = "1.13";
//hardware resources
#define RELAY_HEATPUMP A2
#define RELAY_HOTSIDE_CIRCLE A1
#define PR_LOW A6
#define PR_HIGH A7
#define OW_BUS_ALLTSENSORS 9
#define speakerOut 6
#define em_pin1 A3
String hw_version = "v1.1+";
#define LATCH_595 3
#define CLK_595 2
#define DATA_595 7
#define OE_595 4
//---------------------------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(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
#include
#define SEED 0xFFFF
#define POLY 0xA001
unsigned int crc16;
int cf;
#define MODBUS_MR 50 //50 ok now
#include
#define SerialRX 12 //RX connected to RO - Receiver Output
#define SerialTX 11 //TX connected to DI - Driver Output Pin
#define SerialTxControl 13 //RS485 Direction control DE and RE to this pin
#define RS485Transmit HIGH
#define RS485Receive LOW
SoftwareSerial RS485Serial(SerialRX, SerialTX); // RX, TX
#include
#include
//library's DEVICE_DISCONNECTED_C -127.0
OneWire ow_ALLTSENSORS(OW_BUS_ALLTSENSORS);
DallasTemperature s_allTsensors(&ow_ALLTSENSORS);
DeviceAddress dev_addr; //temp
//short names used to prevent unreadeable source
#ifdef T_cold_in
bool TciE = 1;
#else
bool TciE = 0;
#endif
double Tci = -127.0;
#ifdef T_cold_out
bool TcoE = 1;
#else
bool TcoE = 0;
#endif
double Tco = -127.0;
#ifdef T_before_evaporator
bool TbeE = 1;
#else
bool TbeE = 0;
#endif
double Tbe = -127.0;
#ifdef T_after_evaporator
bool TaeE = 1;
#else
bool TaeE = 0;
#endif
double Tae = -127.0;
/*
#ifdef T_separator_gas
bool TsgE = 1;
#else
bool TsgE = 0;
#endif
double Tsg = -127.0;
#ifdef T_separator_liquid
bool TslE = 1;
#else
bool TslE = 0;
#endif
double Tsl = -127.0;
#ifdef T_before_valve
bool TbvE = 1;
#else
bool TbvE = 0;
#endif
double Tbv = -127.0;
#ifdef T_suction
bool TsucE = 1;
#else
bool TsucE = 0;
#endif
double Tsuc = -127.0;
*/
#ifdef T_sensor_1
bool Ts1E = 1;
#else
bool Ts1E = 0;
#endif
double Ts1 = -127.0;
#ifdef T_sensor_2
bool Ts2E = 1;
#else
bool Ts2E = 0;
#endif
double Ts2 = -127.0;
#ifdef T_crc
bool TcrcE = 1;
#else
bool TcrcE = 0;
#endif
double Tcrc = -127.0;
#ifdef T_regenerator
bool TregE = 1;
#else
bool TregE = 0;
#endif
double Treg = -127.0;
#ifdef T_afrer_condenser
bool TacE = 1;
#else
bool TacE = 0;
#endif
double Tac = -127.0;
#ifdef T_before_condenser
bool TbcE = 1;
#else
bool TbcE = 0;
#endif
double Tbc = -127.0;
#ifdef T_hot_out
bool ThoE = 1;
#else
bool ThoE = 0;
#endif
double Tho = -127.0;
#ifdef T_hot_in
bool ThiE = 1;
#else
bool ThiE = 0;
#endif
double Thi = -127.0;
double T_setpoint = T_SETPOINT;
double T_setpoint_lastsaved = T_setpoint;
double T_EEV_setpoint = EEV_TARGET_TEMP_DIFF;
double T_EEV_dt = 0.0; //real, used during run
const double cT_setpoint_max = T_SETPOINT_MAX;
const double cT_setpoint_min = T_SETPOINT_MIN;
//const double cT_hotcircle_delta_min = T_HOTCIRCLE_DELTA_MIN;
const double cT_crc_min = T_CRANKCASE_MIN;
const double cT_crc_max = T_CRANKCASE_MAX;
const double cT_crc_heat_threshold = T_CRANKCASE_HEAT_THRESHOLD;
//const double cT_reg_heat_threshold = T_REG_HEAT_THRESHOLD;
const double cT_before_condenser_max = T_BEFORE_CONDENSER_MAX;
const double cT_coldref_min = T_COLDREF_MIN;
const double cT_before_evap_work_min = T_BEFORE_EVAP_WORK_MIN;
const double cT_cold_min = T_COLD_MIN;
const double cT_hot_max = T_HOT_MAX;
//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;
const double cT_workingOK_crc_min = T_WORKINGOK_CRANKCASE_MIN; //need to be not very high to normal start after deep freeze
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/5; //
unsigned int pr_low_state_anal = 0; //sensors are NC for spec. conditions, so 1 == ok, 0 == error
unsigned int pr_high_state_anal = 0; //
bool pr_low_state_bool = 1; //sensors are NC for spec. conditions, so 1 == ok, 0 == error
bool pr_high_state_bool = 1; //
bool heatpump_state = 0;
bool hotside_circle_state = 0;
bool coldside_circle_state = 0;
bool crc_heater_state = 0;
//bool reg_heater_state = 0;
//bool relay6_state = 0;
//bool relay7_state = 0;
bool LED_OK_state = 0;
bool LED_ERR_state = 0;
bool S0_state = 0;
bool S1_state = 0;
bool S2_state = 0;
bool S3_state = 0;
bool EEV1_state = 0;
bool EEV2_state = 0;
bool EEV3_state = 0;
bool EEV4_state = 0;
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
bool _1st_start_sleeped = 0;
//??? TODO: periodical start ?
//const long floor_circle_maxhalted = 6000000; //circle NOT works max 100 minutes
const long deffered_stop_hotcircle = DEFFERED_STOP_HOTCIRCLE;
int EEV_cur_pos = 0;
int EEV_reopen_pos = 0;
bool EEV_must_reopen_flag = 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;
#ifdef EEV_MANUAL
bool EEV_manual = 1;
#else
bool EEV_manual = 0;
#endif
const bool c_EEV_reopenlast = EEV_REOPENLAST;
//main cycle vars
unsigned long millis_prev = 0;
unsigned long millis_now = 0;
unsigned long millis_cycle = 1000;
unsigned long millis_last_heatpump_on = 0;
unsigned long millis_last_heatpump_off = 0;
unsigned long millis_last_hotWP_on = 0;
unsigned long millis_last_hotWP_off = 0;
unsigned long millis_last_coldWP_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_485 = 0;
unsigned long millis_charinput_485 = 0;
unsigned long millis_escinput_local = 0;
unsigned long millis_charinput_local = 0;
unsigned long millis_lasteesave = 0;
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;
unsigned long millis_eev_minworkpos_time = 0;
unsigned long millis_eev_last_work = 0;
unsigned long tmic1 = 0;
unsigned long tmic2 = 0;
int skipchars_485 = 0;
int skipchars_local = 0;
#define BUFSIZE 150
unsigned char dataBuf[BUFSIZE+1]; // 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 u = 0;
int z = 0;
int x = 0;
int y = 0;
double tempdouble = 0.0;
double tempdouble_intpart = 0.0;
int tempint = 0;
bool tempbool = 0;
char fp_integer = 0;
char fp_fraction = 0;
String outString;
String lastStopCauseTxt; //20 reserved, but use 12 chars of text max
bool fl_printSS_lastStopCauseTxt = 0; //flag to call printSS
#define LSCint_normal 0
#define LSCint_protective 1
#define LSCint_error 2
int LSCint = LSCint_normal; //0 = normal, 1 = protective, 2 = error
String lastStartMsgTxt; //same as LSC
bool fl_printSS_lastStartMsgTxt = 0; //flag to call printSS
String t_sensorErrString;
char convBuf[13];
//-------------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_P_HI 2
#define ERR_P_LO 3
int errorcode = 0;
unsigned char sequential_errors = 0;
//--------------------------- for wattage
#define ADC_BITS 10 //10 fo regular arduino
#define ADC_COUNTS (1<>1; //Low-pass filter output
double sqI_1,sumI_1 = 0; //sq = squared, sum = Sum, inst = instantaneous
double async_Irms_1 = 0;
double async_wattage = 0;
//--------------------------- for wattage END
const char str1[] PROGMEM = "Valden Heat Pump Controller, https://github.com/OpenHP/\n\r\n\rCommands: \n\r(?) help\n\r(-) decrease setpoint T\n\r\n\r(+) increase setpoint T";
const char str2[] PROGMEM = "(<) decrease EEV T diff \n\r(>) increase EEV T diff\n\r\n\r(M) manual EEV mode\n\r(A) auto EEV mode\n\r\n\r(z) -1 EEV\t(Z) -10 EEV\n\r(x) +1 EEV\t(X) +10 EEV\n\r(G) get stats";
const char str3[] PROGMEM = "EEV:auto";
const char str4[] PROGMEM = "EEV:manual";
const char str5[] PROGMEM = "N/A,auto";
const char str6[] PROGMEM = "+10 ok";
const char str7[] PROGMEM = "-10 ok";
const char str8[] PROGMEM = "+1 ok";
const char str9[] PROGMEM = "-1 ok";
const char str10[] PROGMEM = "Max!";
const char str11[] PROGMEM = "Min!";
const char str12[] PROGMEM = "HWP ON by Setp. update";
const char str13[] PROGMEM = "EE->mem";
const char str14[] PROGMEM = "mem->EE";
const char str15[] PROGMEM = "OK:E.T.Sens.";
const char str16[] PROGMEM = "OK:Pr.Cold";
const char str17[] PROGMEM = "OK:Pr.Hot";
const char str18[] PROGMEM = "HWP_ON";
const char str19[] PROGMEM = "unkn_F";
PGM_P const const_strs[] PROGMEM = {
str1, str2, str3, str4, str5, str6, str7, str8, str9, str10,
str11, str12, str13, str14, str15, str16, str17, str18, str19
};
#define IDX_HELP1 0
#define IDX_HELP2 1
#define IDX_EEVAUTO 2
#define IDX_EEVMANUAL 3
#define IDX_NAAUTO 4
#define IDX_PLUS10_OK 5
#define IDX_MINUS10_OK 6
#define IDX_PLUS1_OK 7
#define IDX_MINUS1_OK 8
#define IDX_MAX 9
#define IDX_MIN 10
#define IDX_HWP_ONBYUPD 11
#define IDX_EEtoMEM 12
#define IDX_MEMtoEE 13
#define IDX_OK_ETSENS 14
#define IDX_OK_PRCOLD 15
#define IDX_OK_PRHOT 16
#define IDX_HWPON 17
#define IDX_UNKNF 18
//--------------------------- 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
}
/*void PrintS (String str) {
#ifdef RS485_HUMAN
char *outChar=&str[0];
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outChar);
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
}*/
void PrintSS (String str) {
char *outChar=&str[0];
if (str == "") {
return;
}
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outChar);
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
Serial.println(outChar);
Serial.flush();
}
void PrintSSch(char idx) {
strcpy_P(dataBuf, (PGM_P)pgm_read_word(&const_strs[idx]));
Serial.println((const char *) dataBuf);
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print((const char *) dataBuf);
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
}
void PrintSS_SaD (double num) { //global string + double
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outString);
RS485Serial.println(num);
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
Serial.print(outString);
Serial.println(num);
Serial.flush();
}
void PrintSS_SaBl (bool num) {
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outString);
RS485Serial.println(num);
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
Serial.print(outString);
Serial.println(num);
Serial.flush();
}
void ApToOut_D (double num) {
outString += String(num);
}
void PrintSS_SaI (int num) {
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outString);
RS485Serial.println(num);
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
Serial.print(outString);
Serial.println(num);
Serial.flush();
}
/*void PrintSS_SaI (int num) { //global string + double
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outString);
RS485Serial.println(num);
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
Serial.print(outString);
Serial.println(num);
Serial.flush();
}*/
void _PrintHelp(void) {
PrintSS("fw: " + fw_version + " board: "+ hw_version);
PrintSSch(IDX_HELP1);
#ifndef NO_EEV
PrintSSch(IDX_HELP2);
#endif
}
void PrintSS_double (double double_to_print) {
dtostrf(double_to_print,1,2,temp);
PrintSS(temp);
}
void Add_Double_To_Buf_IntFract (double float_to_convert) { //uses tempdouble tempdouble_intpart fp_integer fp_fraction
if (float_to_convert > 255.0 || float_to_convert < -127.0) {
fp_integer = -127;
fp_fraction = 0;
} else {
tempdouble = modf (float_to_convert , &tempdouble_intpart);
fp_integer = trunc(tempdouble_intpart);
tempdouble = tempdouble * 100;
fp_fraction = round(tempdouble);
}
dataBuf[i] = fp_integer;
i++;
dataBuf[i] = fp_fraction;
i++;
/*
Serial.println(float_to_convert);
Serial.println(fp_integer, DEC);
Serial.println(fp_fraction, DEC);*/
}
void IntFract_to_tempdouble (char _int_to_convert, char _fract_to_convert) { //fract is also signed now!
tempdouble = (double) _fract_to_convert / 100;
tempdouble += _int_to_convert;
/*Serial.println(_int_to_convert);
Serial.println(_fract_to_convert);
Serial.println(tempdouble);*/
}
void _ProcessInChar(void){
//remote commands +,-,G,0x20/?/Enter/A/M/x/X/z/Z
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;
#ifndef NO_EEV
case 0x3C:
Dec_E();
break;
case 0x3E:
Inc_E();
break;
case 0x41:
EEV_manual = 0;
PrintSSch(IDX_EEVAUTO);
break;
#endif
case 0x47:
PrintStats_SS();
millis_last_printstats = millis_now;
break;
#ifndef NO_EEV
case 0x4D:
EEV_manual = 1;
PrintSSch(IDX_EEVMANUAL);
break;
case 0x58: //+10
if (EEV_manual != 1){
PrintSSch(IDX_NAAUTO);
break;
}
EEV_apulses += 10;
EEV_fast = 1;
PrintSSch(IDX_PLUS10_OK);
break;
case 0x5A: //-10
if (EEV_manual != 1){
PrintSSch(IDX_NAAUTO);
break;
}
EEV_apulses -= 10;
EEV_fast = 1;
PrintSSch(IDX_MINUS10_OK);
break;
case 0x78: //+1
if (EEV_manual != 1){
PrintSSch(IDX_NAAUTO);
break;
}
EEV_apulses += 1;
EEV_fast = 1;
PrintSSch(IDX_PLUS1_OK);
break;
case 0x7A: //-1
if (EEV_manual != 1){
PrintSSch(IDX_NAAUTO);
break;
}
EEV_apulses += 10;
EEV_fast = 1;
PrintSSch(IDX_MINUS1_OK);
break;
#endif
}
}
int Inc_T (void) {
if (T_setpoint + 0.5 > cT_setpoint_max) {
PrintSSch(IDX_MAX);
delay (200);
return 0;
}
T_setpoint += 0.5;
PrintSS_double(T_setpoint);
return 1;
}
int Dec_T (void) {
if (T_setpoint - 0.5 < cT_setpoint_min) {
PrintSSch(IDX_MIN);
delay (200);
return 0;
}
T_setpoint -= 0.5;
PrintSS_double(T_setpoint);
return 1;
}
int Inc_E (void) { ///!!! unprotected
T_EEV_setpoint += 0.25;
PrintSS_double(T_EEV_setpoint);
return 1;
}
int Dec_E (void) { ///!!! unprotected
T_EEV_setpoint -= 0.25;
PrintSS_double(T_EEV_setpoint);
return 1;
}
void _HotWPon_by_Setpoint_update(void){ //if setpoint updated: start hot circle to check temperature
if ( (heatpump_state == 0) && (hotside_circle_state == 0) && ((unsigned long)(millis_now - millis_last_hotWP_on) < HOTCIRCLE_START_EVERY) ) { //process START_EVERY for hot side
millis_last_hotWP_off = millis_now;
hotside_circle_state = 1;
PrintSSch(IDX_HWP_ONBYUPD);
}
}
void PrintStats_SS (void) {
if (TciE) { outString = F("\n\r---\n\r\tTbe:\t") ; PrintSS_SaD(Tbe); }
if (TaeE) { outString = F("\tTae:\t") ; PrintSS_SaD(Tae); }
if (TcoE) { outString = F("\tTci:\t"); PrintSS_SaD(Tci); }
if (TcoE) { outString = F("\tTco:\t") ; PrintSS_SaD(Tco); }
//if (TsgE) { outString = F("\tTsg: ") ; PrintSS_SaD(Tsg); }
//if (TslE) { outString = F("\tTsl: ") ; PrintSS_SaD(Tsl); }
//if (TbvE) { outString = F("\tTbv: ") ; PrintSS_SaD(Tbv); }
//if (TsucE) { outString = F("\tTsuc: ") ; PrintSS_SaD(Tsuc); }
if (Ts1E) { outString = F("\tTs1:\t") ; PrintSS_SaD(Ts1); }
if (Ts2E) { outString = F("\tTs2:\t") ; PrintSS_SaD(Ts2); }
//Tcrc misorder due to large string
if (TregE) { outString = F("\tTreg:\t") ; PrintSS_SaD(Treg); }
if (TbcE) { outString = F("\tTbc:\t") ; PrintSS_SaD(Tbc); }
if (TacE) { outString = F("\tTac:\t") ; PrintSS_SaD(Tac); }
if (ThiE) { outString = F("\tThi:\t") ; PrintSS_SaD(Thi); }
if (ThoE) { outString = F("\tTho:\t") ; PrintSS_SaD(Tho); }
if (TcrcE) { outString = F("\tTcrankcase:\t"); PrintSS_SaD(Tcrc); }//misorder due to large string
outString = F("\tSetpoint:\t");
PrintSS_SaD(T_setpoint);
outString = F("\n\r\tHP:\t");
PrintSS_SaBl(heatpump_state);
outString = F("\tHWP:\t");
PrintSS_SaBl(hotside_circle_state);
outString = F("\tCWP:\t");
PrintSS_SaBl(coldside_circle_state);
outString = F("\tCRCheat:");
PrintSS_SaBl(crc_heater_state);
outString = F("\tWatts:\t") ;
PrintSS_SaD(async_wattage);
#ifndef NO_EEV
outString = F("\n\r\tT_EEV_setpoint: ");
PrintSS_SaD(T_EEV_setpoint);
outString = "\tEEV_pos:\t";
PrintSS_SaI(EEV_cur_pos);
#endif
outString = "\n\r\tErr:\t";
PrintSS_SaI(errorcode);
outString = F("\tPr.Cold:") ;
if (pr_low_state_bool == 1) {
outString += F("OK");
} else {
outString += F("ERR");
}
outString += F("\n\r\tPr.Hot:\t") ;
if (pr_high_state_bool == 1) {
outString += F("OK");
} else {
outString += F("ERR");
}
outString += F("\n\r\n\r\tLast Stop Cause:\t");
outString += lastStopCauseTxt;
outString += F("\n\r\tLast Start Message:\t");
outString += lastStartMsgTxt;
outString += F("\n\r---\n\r");
PrintSS(outString);
#ifdef RS485_HUMAN
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
RS485Serial.print(outString);
RS485Serial.println();
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
#endif
}
void Calc_CRC(unsigned char b) { //uses/changes y
crc16 ^= b & 0xFF;
for (y=0; y<8; y++) {
cf = crc16 & 0x0001;
crc16>>=1;
if (cf) { crc16 ^= POLY; }
}
}
void CheckIsInvalidCRCAddr(unsigned char *addr) {
if (OneWire::crc8( addr, 7) != addr[7] ) {
i+= 1;
}
}
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;
}
}
double GetT (int channel) {
S0_state = bitRead(channel,0);
S1_state = bitRead(channel,1);
S2_state = bitRead(channel,2);
S3_state = bitRead(channel,3);
halifise();
tempdouble = -127.0;
for ( i = 0; i < 8; i++) {
#ifdef WATCHDOG
wdt_reset();
#endif
eevise();
tempdouble = s_allTsensors.getTempCByIndex(0);
if ( (tempdouble == 85.0) || (tempdouble < -55.0) || (tempdouble == 0.0) || (tempdouble > 125.0) ) { //0.0 added to test
//outString = F("Warn:T_SensReRead!");
//PrintSS_SaD(tempdouble);
if ( tempdouble == 85.0 || tempdouble == 0.0 ) { //initial value in dallas register after poweron
s_allTsensors.requestTemperatures(); //!!!added to test, seems to work ok
delay (375); //375 actual for 11 bits resolution, 2-3 retries OK for 12-bits resolution
} else {
delay (37);
}
} else {
break;
}
}
s_allTsensors.requestTemperatures();
if ( (tempdouble > 125.0) || (tempdouble < -55.0)) { //incorrect readings protection, rare
tempdouble = -127.0;
}
return tempdouble;
}
//older version of GetT
/*double GetT (int channel) {
S0_state = bitRead(channel,0);
S1_state = bitRead(channel,1);
S2_state = bitRead(channel,2);
S3_state = bitRead(channel,3);
halifise();
tempdouble = -127.0;
for ( i = 0; i < 8; i++) {
#ifdef WATCHDOG
wdt_reset();
#endif
eevise();
tempdouble = s_allTsensors.getTempCByIndex(0);
if ( (tempdouble == 85.0) || (tempdouble == -127.0) ) {
if ( tempdouble == 85.0 ) { //initial value in dallas register after poweron
s_allTsensors.requestTemperatures();//!!! added to test
delay (375); //375 actual for 11 bits resolution, 2-3 retries OK for 12-bits resolution
} else {
delay (37);
}
} else {
break;
}
}
s_allTsensors.requestTemperatures();
return tempdouble;
}*/
void GetTemperatures(void){
if (TciE) { Tci = GetT(0);}
if (TcoE) { Tco = GetT(1);}
if (TbeE) { Tbe = GetT(2);}
if (TaeE) { Tae = GetT(3);}
//if (TsgE) { Tsg = GetT(4);}
//if (TslE) { Tsl = GetT(5);}
//if (TbvE) { Tbv = GetT(6);}
//if (TsucE) { Tsuc = GetT(7);}
if (Ts1E) { Ts1 = GetT(8);}
if (Ts2E) { Ts2 = GetT(9);}
if (TcrcE) { Tcrc = GetT(10);}
if (TregE) { Treg = GetT(11);}
if (TacE) { Tac = GetT(12);}
if (TbcE) { Tbc = GetT(13);}
if (ThoE) { Tho = GetT(14);}
if (ThiE) { Thi = GetT(15);}
}
void on_EEV(){
x = EEV_steps[EEV_cur_step];
EEV1_state = bitRead(x, 0);
EEV2_state = bitRead(x, 1);
EEV3_state = bitRead(x, 2);
EEV4_state = bitRead(x, 3);
halifise();
}
void off_EEV(){
EEV1_state = 0;
EEV2_state = 0;
EEV3_state = 0;
EEV4_state = 0;
//PrintSS("off_EEV");
halifise();
}
void halifise(void){
/*
relay 1: heat pump
relay 2: hot side circulator pump
relay 3: cold side circulator pump
relay 4: crankcase heater
(no more v1.3mi) relay 5:
#define RELAY_HEATPUMP A2
#define RELAY_HOTSIDE_CIRCLE A1
Reg 1:
595.0: 4067 S3
595.1: 4067 S0
595.2: 4067 S1
595.3: 4067 S2
595.4: EEV_1
595.5: EEV_2
595.6: EEV_3
595.7: EEV_4
Reg 2:
595.8: !! free
595.9: ok/err LED 2
595.A: Relay 6
595.B: Relay 7
595.C: Relay 5
595.D: Relay 4
595.E: Relay 3
595.F: ok/err LED 1
Reg 3:
595.10: LED "EEV opening"
595.11: LED "EEV closing"
595.12: LED "EEV Fast"
595.13: LED "485 RX"
595.14: LED "485 TX"
595.15: LED "Manual mode"
595.16: LED "LSC: error"
595.17: LED "LSC: protection"
*/
digitalWrite(LATCH_595, 0);
//17
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
if (LSCint == LSCint_protective) {
digitalWrite(DATA_595, 1);
} else {
digitalWrite(DATA_595, 0);
}
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//16
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
if (LSCint == LSCint_error) {
digitalWrite(DATA_595, 1);
} else {
digitalWrite(DATA_595, 0);
}
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//15
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, EEV_manual);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//14
tempbool = digitalRead (13);
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, tempbool);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//13
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, !tempbool);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//12
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, EEV_fast);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//11
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
if ( EEV_apulses < 0 ) {
digitalWrite(DATA_595, 1);
} else {
digitalWrite(DATA_595, 0);
}
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//10
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
if ( EEV_apulses > 0 ) {
digitalWrite(DATA_595, 1);
} else {
digitalWrite(DATA_595, 0);
}
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//F
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, LED_ERR_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//E
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, coldside_circle_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//D
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, crc_heater_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//C
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
//digitalWrite(DATA_595, reg_heater_state);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//B
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
//digitalWrite(DATA_595, relay7_state);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//A
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
//digitalWrite(DATA_595, relay6_state);
digitalWrite(DATA_595, 0);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//9
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, LED_OK_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//8
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, 0); //FREE
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//7
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, EEV4_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//6
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, EEV3_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//5
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, EEV2_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//4
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, EEV1_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//3
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, S2_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//2
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, S1_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//1
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, S0_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
//0
digitalWrite(CLK_595, 0);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(DATA_595, S3_state);
digitalWrite(CLK_595, 1);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(CLK_595, 0);
//
digitalWrite(LATCH_595, 1);
__asm__ __volatile__ ("nop\n\t");
digitalWrite(LATCH_595, 0);
digitalWrite (RELAY_HEATPUMP, heatpump_state);
digitalWrite (RELAY_HOTSIDE_CIRCLE, hotside_circle_state);
}
void eevise(void) {
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;
//PrintSS("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;
//PrintSS("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];
EEV1_state = bitRead(x, 0);
EEV2_state = bitRead(x, 2); //!!!here pins are swapped fot sanhua
EEV3_state = bitRead(x, 1); //!!!here pins are swapped fot sanhua
EEV4_state = bitRead(x, 3);
}
if (EEV_cur_pos < 0) {
EEV_cur_pos = 0;
}
millis_eev_last_step = millis_now;
#ifdef EEV_DEBUG
PrintSS(String(EEV_cur_pos));
#endif
halifise();
}
}
//--------------------------- functions END
void setup(void) {
pinMode (LATCH_595, OUTPUT);
pinMode (CLK_595, OUTPUT);
pinMode (DATA_595, OUTPUT);
pinMode (OE_595, OUTPUT);
pinMode (RELAY_HEATPUMP, OUTPUT);
pinMode (RELAY_HOTSIDE_CIRCLE, OUTPUT);
pinMode (PR_LOW, INPUT);
pinMode (PR_HIGH, INPUT);
digitalWrite (LATCH_595, LOW);
digitalWrite (CLK_595, LOW);
digitalWrite (DATA_595, LOW);
digitalWrite (OE_595, LOW);
digitalWrite (RELAY_HEATPUMP, LOW);
digitalWrite (RELAY_HOTSIDE_CIRCLE, LOW);
halifise();
#ifdef WATCHDOG
wdt_disable();
delay(2000);
#endif
// start serial port
Serial.begin(9600);
//Serial.print("Starting, dev_id:");
//Serial.println(devID);
RS485Serial.begin(9600);
pinMode(SerialTxControl, OUTPUT);
pinMode(SerialTX, OUTPUT);
pinMode(SerialRX, INPUT);
digitalWrite(SerialTxControl, RS485Receive);
delay(100);
PrintSS("ID: 0x" + String(devID, HEX));
delay(200);
off_EEV();
pinMode (em_pin1, INPUT);
//PrintSS("setpoint (C):");
//PrintSS(setpoint);
//PrintSS(String(freeMemory()));
s_allTsensors.begin();
s_allTsensors.setWaitForConversion(false); //ASYNC mode, request before get, see Dallas library for details
//----------------------------- self-tests ----------------------------- ----------------------------- -----------------------------
/*
index = 0;
outChar[index] = 0xFF;
index++;
outChar[index] = 0xAA;
index++;
outChar[index] = 0xBB;
index++;
outChar[index] = 0xCC;
index++;
crc16 = SEED;
for (z = 0; z < index; z++) {
Calc_CRC(outChar[z]);
}
outChar[index]=crc16 & 0xFF;
index++;
outChar[index]=crc16 >> 8;
index++;
outChar[index]=0x00;
index++;
Serial.println(crc16, HEX);
for (z = 0; z < index; z++) {
Serial.print(" ");
Serial.print(outChar[z], HEX);
}
Serial.println(" ");
*/
//Relays self-test
#if (defined SELFTEST_RELAYS_LEDS_SPEAKER || defined SELFTEST_EEV || defined SELFTEST_T_SENSORS)
while ( 1 == 1) {
#if defined SELFTEST_RELAYS_LEDS_SPEAKER
PrintSS(F("Relays and LEDS self-test"));
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
heatpump_state = 1; halifise(); delay(1000);
hotside_circle_state = 1; halifise(); delay(1000);
coldside_circle_state = 1; halifise(); delay(1000);
crc_heater_state = 1; halifise(); delay(1000);
//reg_heater_state = 1; halifise(); delay(1000);
//relay6_state = 1; halifise(); delay(1000);
//relay7_state = 1; halifise(); delay(1000);
EEV_apulses = 10; halifise(); delay(1000);
EEV_apulses = -10; halifise(); delay(1000);
EEV_fast = 1; halifise(); delay(1000);
digitalWrite(SerialTxControl, RS485Transmit); halifise(); delay(1000);
EEV_manual = 1; halifise(); delay(1000);
LSCint = LSCint_error; halifise(); delay(1000);
LSCint = LSCint_protective; halifise(); delay(1000);
LED_OK_state = 1; halifise(); delay(1000);
LED_ERR_state = 1; halifise(); delay(1000);
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
heatpump_state = 0; halifise(); delay(1000);
hotside_circle_state = 0; halifise(); delay(1000);
coldside_circle_state = 0; halifise(); delay(1000);
crc_heater_state = 0; halifise(); delay(1000);
//reg_heater_state = 0; halifise(); delay(1000);
//relay6_state = 0; halifise(); delay(1000);
//relay7_state = 0; halifise(); delay(1000);
EEV_apulses = 10; halifise(); delay(1000);
EEV_apulses = -10; halifise(); delay(1000);
EEV_fast = 0; halifise(); delay(1000);
digitalWrite(SerialTxControl, RS485Receive); halifise(); delay(1000);
digitalWrite(SerialTxControl, RS485Transmit); halifise(); delay(1000);
EEV_manual = 0; halifise(); delay(1000);
LSCint = LSCint_error; halifise(); delay(1000);
LSCint = LSCint_protective; halifise(); delay(1000);
LED_OK_state = 0; halifise(); delay(1000);
LED_ERR_state = 0; halifise(); delay(1000);
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
#endif
#if defined SELFTEST_EEV
EEV_apulses = 0;
EEV_fast = 0;
halifise();
delay(1000);
//EEV self-test, also can be used for compressor test
//vacuuming/charge via low pressure side: leave EEV opened
//PrintSS("EEV self-test");
EEV_apulses = -(EEV_MAXPULSES + EEV_CLOSE_ADD_PULSES);
EEV_adonotcare = 1;
EEV_fast = 1;
while (EEV_apulses < 0){
millis_now = millis();
eevise();
}
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
delay(1000);
//EEV_apulses = EEV_MAXPULSES;
EEV_apulses = 50;
EEV_fast = 1;
while (EEV_apulses > 0){
millis_now = millis();
eevise();
}
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
delay(1000);
#endif
#if defined SELFTEST_T_SENSORS
GetTemperatures();
outString=F("Tbe: "); PrintSS_SaD(Tbe);
outString=F("Tae: "); PrintSS_SaD(Tae);
outString=F("Tci: "); PrintSS_SaD(Tci);
outString=F("Tco: "); PrintSS_SaD(Tco);
outString=F("Tbc: "); PrintSS_SaD(Tbc);
outString=F("Tac: "); PrintSS_SaD(Tac);
outString=F("Thi: "); PrintSS_SaD(Thi);
outString=F("Tho: "); PrintSS_SaD(Tho);
outString=F("Ts1: "); PrintSS_SaD(Ts1);
outString=F("Tcrc: "); PrintSS_SaD(Tcrc);
outString=F("Ts2: "); PrintSS_SaD(Ts2);
outString=F("Treg: "); PrintSS_SaD(Treg);
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
delay(1000);
#endif
//---------DEBUG END--------
}
#endif
//----------------------------- self-test END----------------------------- ----------------------------- -----------------------------
eeprom_magic_read = EEPROM.read(eeprom_addr);
eeprom_addr += 1;
//EEPROM content: 0x00 - magic, 0x01..0x04 target value
if (eeprom_magic_read == eeprom_magic){
PrintSSch(IDX_EEtoMEM);
} else {
PrintSSch(IDX_MEMtoEE);
WriteFloatEEPROM(eeprom_addr, T_setpoint);
EEPROM.write(0x00, eeprom_magic);
}
T_setpoint = ReadFloatEEPROM(eeprom_addr);
PrintSS_double(T_setpoint);
//eeprom_addr += 4;
T_setpoint_lastsaved = T_setpoint;
#ifdef WATCHDOG
wdt_enable (WDTO_8S);
#endif
GetTemperatures();
outString.reserve(80);
lastStopCauseTxt.reserve(20);
lastStartMsgTxt.reserve(20);
t_sensorErrString.reserve(12);
//PrintSS(String(freeMemory()));
LED_OK_state = 1;
_PrintHelp();
analogWrite(speakerOut, 10);
delay (1500);
analogWrite(speakerOut, 0);
lastStopCauseTxt = F("Start Pause");
lastStartMsgTxt = "";
}
void loop(void) {
digitalWrite(SerialTxControl, RS485Receive);
millis_now = millis();
halifise();
eevise();
if (((unsigned long)(millis_now - millis_last_printstats) > HUMAN_AUTOINFO) || (millis_last_printstats == 0) ) {
PrintStats_SS();
millis_last_printstats = millis_now;
}
//--------------------async fuctions start
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 !!!
/*
PrintSS("Async impl. results 1: ");
PrintSS(String(async_wattage)); // Apparent power
PrintSS(String(async_Irms_1)); // Irms
PrintSS(" voltage: ");
PrintSS(String(supply_voltage));
*/
//----------------------------- self-test END
}
eevise();
//--------------------async fuctions END
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*3)) {
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
LSCint = LSCint_protective;
halifise();
lastStopCauseTxt = ("P.WtMax:") + String(async_wattage);
PrintSS(lastStopCauseTxt);
}
}
//-------------------check cycle
if( ((unsigned long)(millis_now - millis_prev) > millis_cycle ) || (millis_prev == 0) ) {
millis_prev = millis_now;
GetTemperatures(); // wdt_reset here due to 85.0'C filtration
SaveSetpointEE();
pr_low_state_anal = analogRead(PR_LOW); //
pr_high_state_anal = analogRead(PR_HIGH); //shotrcut test shows 993-994 for analogRead (10.4ma)
if (pr_low_state_anal > 200) {
pr_low_state_bool = 1;
} else {
pr_low_state_bool = 0;
}
if (pr_high_state_anal > 200) {
pr_high_state_bool = 1;
} else {
pr_high_state_bool = 0;
}
//--------------------important logic
//check T sensors
if ( errorcode == ERR_OK ) {
if (TbeE == 1 && Tbe == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tbe");}
if (TaeE == 1 && Tae == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tae");}
if (TciE == 1 && Tci == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tci");}
if (TcoE == 1 && Tco == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tco");}
if (TbcE == 1 && Tbc == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tbc");}
if (TacE == 1 && Tac == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tac");}
if (ThiE == 1 && Thi == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Thi");}
if (ThoE == 1 && Tho == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tho");}
//if (TsgE == 1 && Tsg == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tsg");}
//if (TslE == 1 && Tsl == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tsl");}
//if (TbvE == 1 && Tbv == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tbv");}
//if (TsucE == 1 && Tsuc == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tsuc");}
if (Ts1E == 1 && Ts1 == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Ts1");}
if (Ts2E == 1 && Ts2 == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Ts2");}
if (TcrcE == 1 && Tcrc == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Tcrc");}
if (TregE == 1 && Treg == -127 ) {errorcode = ERR_T_SENSOR; outString = F("E.Treg");}
if (errorcode == ERR_T_SENSOR){
//PrintSS(String(outString));
t_sensorErrString = String(outString);
//printed to console below
}
}
//auto-clean sensor error on sensor appears
// add 1xor enable here!
if ( ( errorcode == ERR_T_SENSOR ) && ( ((TciE == 1 && Tci != -127 ) || (TciE ^1)) &&
((TcoE == 1 && Tco != -127 ) || (TcoE ^1)) &&
((TbeE == 1 && Tbe != -127 ) || (TbeE ^1)) &&
((TaeE == 1 && Tae != -127 ) || (TaeE ^1)) &&
//((TsgE == 1 && Tsg != -127 ) || (TsgE ^1)) &&
//((TslE == 1 && Tsl != -127 ) || (TslE ^1)) &&
//((TbvE == 1 && Tbv != -127 ) || (TbvE ^1)) &&
//((TsucE == 1 && Tsuc != -127 ) || (TsucE ^1)) &&
((Ts1E == 1 && Ts1 != -127 ) || (Ts1E ^1)) &&
((Ts2E == 1 && Ts2 != -127 ) || (Ts2E ^1)) &&
((TcrcE == 1 && Tcrc != -127 ) || (TcrcE ^1)) &&
((TregE == 1 && Treg != -127 ) || (TregE ^1)) &&
((TacE == 1 && Tac != -127 ) || (TacE ^1)) &&
((TbcE == 1 && Tbc != -127 ) || (TbcE ^1)) &&
((ThoE == 1 && Tho != -127 ) || (ThoE ^1)) &&
((ThiE == 1 && Thi != -127 ) || (ThiE ^1)) )) {
errorcode = ERR_OK;
PrintSSch(IDX_OK_ETSENS);
sequential_errors = 0;
t_sensorErrString = "";
}
//check pressure sensors
//auto-clean prev. errors first
if ( errorcode == ERR_P_LO ) {
if (pr_low_state_bool == 1) {
errorcode = ERR_OK;
PrintSSch(IDX_OK_PRCOLD);
}
}
if ( errorcode == ERR_P_HI ) {
if (pr_high_state_bool == 1) {
errorcode = ERR_OK;
PrintSSch(IDX_OK_PRHOT);
}
}
//recheck, if another sensor
if ( errorcode == ERR_OK ) {
if (pr_low_state_bool == 0) {errorcode = ERR_P_LO;} //for PrintSS scroll down
if (pr_high_state_bool == 0) {errorcode = ERR_P_HI;} //for PrintSS scroll down
}
//-------------- EEV cycle
/*
//v1 algo
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 ){
PrintSS("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
PrintSS("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
PrintSS("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) {
T_EEV_dt = Tae.T - Tbe.T;
PrintSS("EEV: driving " + String(T_EEV_dt));
if (EEV_cur_pos <= 0){
PrintSS("EEV: full close protection");
if (EEV_OPEN_AFTER_CLOSE != 0) { //full close protection
EEV_apulses = +EEV_OPEN_AFTER_CLOSE;
EEV_adonotcare = 0;
EEV_fast = 1;
}
} else if (EEV_cur_pos > 0) {
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
PrintSS("EEV: emergency closing!");
EEV_apulses = -EEV_EMERG_STEPS;
EEV_adonotcare = 0;
EEV_fast = 1;
} else if (T_EEV_dt < T_EEV_setpoint) { //too
PrintSS("EEV: closing");
//EEV_apulses = -EEV_NONPRECISE_STEPS;
EEV_apulses = -1;
EEV_adonotcare = 0;
EEV_fast = 0;
} else if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
PrintSS("EEV: fast opening");
//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
PrintSS("EEV: opening");
EEV_apulses = +1;
EEV_adonotcare = 0;
EEV_fast = 0;
} else if (T_EEV_dt > T_EEV_setpoint) { //ok
PrintSS("EEV: OK");
//
}
}
off_EEV();
}
}
*/
//v1.2 algo: reopen added
#ifndef NO_EEV
if ( EEV_manual == 0 && errorcode == 0 && async_wattage >= c_workingOK_wattage_min && EEV_cur_pos > 0 ) {
T_EEV_dt = Tae - Tbe;
#ifdef EEV_DEBUG
PrintSS("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
PrintSS(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
PrintSS(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
PrintSS(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 ( EEV_must_reopen_flag == 1 && (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS) && ((unsigned long)(millis_now - millis_eev_minworkpos_time) > EEV_REOPENMINTIME) && (millis_eev_last_work < millis_eev_minworkpos_time) ) { //reopen
EEV_must_reopen_flag = 0;
EEV_apulses = EEV_reopen_pos - EEV_cur_pos;
EEV_adonotcare = 0;
EEV_fast = 1;
#ifdef EEV_DEBUG
PrintSS(F("EEV: 14 reopening last"));
PrintSS(String(EEV_apulses));
PrintSS(String(millis_now));
PrintSS(String(millis_eev_minworkpos_time));
PrintSS(String(millis_eev_last_work));
#endif
} else if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
#ifdef EEV_DEBUG
PrintSS(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
PrintSS(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
PrintSS(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
PrintSS(F("EEV: 7 enforce faster closing!"));
#endif
//EEV_apulses = -EEV_EMERG_STEPS;
EEV_adonotcare = 0;
EEV_fast = 1;
}
}
off_EEV();
}
if ( EEV_manual == 0 && 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
PrintSS(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
EEV_reopen_pos = EEV_cur_pos; //reopen pos. set
EEV_must_reopen_flag = 1;
millis_eev_last_work = millis_now;
#ifdef EEV_DEBUG
PrintSS(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_manual == 0 && EEV_apulses == 0 && async_wattage < c_workingOK_wattage_min && EEV_cur_pos < EEV_OPEN_AFTER_CLOSE) {
#ifdef EEV_DEBUG
PrintSS(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_manual == 0 && EEV_apulses == 0 && async_wattage >= c_workingOK_wattage_min && EEV_cur_pos < EEV_MINWORKPOS) {
#ifdef EEV_DEBUG
PrintSS(F("EEV: 13 open to work"));
#endif
if (EEV_MINWORKPOS != 0) {
EEV_apulses = EEV_MINWORKPOS - EEV_cur_pos;
EEV_adonotcare = 0;
EEV_fast = 1;
//millis_eev_minworkpos_time = millis_now;
}
off_EEV();
}
if (EEV_cur_pos < EEV_MINWORKPOS) { //for reopen
millis_eev_minworkpos_time = millis_now;
}
if ( EEV_manual == 0 && EEV_apulses == 0 && EEV_fast == 1 ) {//just for LED
EEV_fast = 0;
}
if ( ((unsigned long)(millis_now - millis_eev_last_on) > 10000) || millis_eev_last_on == 0 ) {
//PrintSS("EEV: ON/OFF");
on_EEV();
//delay(30);
//off_EEV(); //off_EEV called somewhere else takes care of it
millis_eev_last_on = millis_now;
}
//-------------- EEV cycle END
#endif
#ifndef EEV_ONLY
//process heatpump crankcase heater
if (TcrcE == 1) {
if ( Tcrc < cT_crc_heat_threshold && crc_heater_state == 0 && Tcrc != -127) {
crc_heater_state = 1;
} else if (Tcrc >= cT_crc_heat_threshold && crc_heater_state == 1) {
crc_heater_state = 0;
} else if (Tcrc == -127) {
crc_heater_state = 0;
}
halifise();
}
//main logic
if (_1st_start_sleeped == 0) {
//enable hot WP immidiately
if (hotside_circle_state == 0){
millis_last_hotWP_off = millis_now;
hotside_circle_state = 1;
}
//_1st_start_sleeped = 1;
if ( (millis_now < poweron_pause) && (_1st_start_sleeped == 0) ) {
outString = String(((poweron_pause-millis_now))/1000);
//PrintSS("Wait: " + outString + " s.");
lastStartMsgTxt = "StCntd:" + outString; //start countdown, max 5 numerical places
fl_printSS_lastStartMsgTxt = 1;
//PrintSS(lastStartMsgTxt);
//return;
} else {
_1st_start_sleeped = 1;
lastStopCauseTxt="";
lastStartMsgTxt="";
}
}
//process_heatpump:
//start if
// (last_on > N or not_started_yet)
// and (no errors)
// and (t hot out < t target)
// and (t hot out < t hot max)
// and (t hot in < t hot max)
// and (crc t > min'C)
// and (crc 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 && errorcode == ERR_T_SENSOR) {
lastStartMsgTxt = t_sensorErrString;
//fl_printSS_lastStartMsgTxt = 1;
}
if (heatpump_state == 0 && errorcode == ERR_P_LO ) {
lastStartMsgTxt = F("E.PresCold");
}
if (heatpump_state == 0 && errorcode == ERR_P_HI ) {
lastStartMsgTxt = F("E.PresHot");
}
if (heatpump_state == 0 && errorcode == ERR_OK && _1st_start_sleeped == 1) {
i = 0;
#ifdef SETPOINT_THI
if ( Thi < T_setpoint ) {i+=1;} else { lastStartMsgTxt = F("#Thi>Setp."); } //or1 //Thi = warm floor heat pump
#endif
#ifdef SETPOINT_TS1
if ( Ts1 < T_setpoint ) {i+=1;} else { lastStartMsgTxt = F("#Ts1>Setp."); } //or1 //Ts1 = tank heater
#endif
//2 wait cold circe if needed
if ( coldside_circle_state == 1 && ((unsigned long)(millis_now - millis_last_coldWP_off) > COLDCIRCLE_PREPARE) ){
i+= 1;
//only if hot runned and T < setpoint
} else if ((coldside_circle_state == 0) && (hotside_circle_state == 1) && ((unsigned long)(millis_now - millis_last_hotWP_off) > HOTCIRCLE_CHECK_PREPARE) ) {
#ifdef SETPOINT_THI
if ( Thi < T_setpoint ) {
#endif
#ifdef SETPOINT_TS1
if ( Ts1 < T_setpoint ) {
#endif
lastStartMsgTxt = F("#CPpStart");
millis_last_coldWP_off = millis_now;
coldside_circle_state = 1;
fl_printSS_lastStartMsgTxt = 1;
//PrintSS(lastStartMsgTxt);
}
} else if (coldside_circle_state == 1) {
lastStartMsgTxt = "#CPp:" + String( (COLDCIRCLE_PREPARE -(unsigned long)(millis_now - millis_last_coldWP_off))/1000 );
}
//3 wait hot circe if needed
#ifdef SETPOINT_THI
if ((hotside_circle_state == 1) && ((unsigned long)(millis_now - millis_last_hotWP_off) > HOTCIRCLE_CHECK_PREPARE) ) {
i+=1;
} else if (hotside_circle_state == 1) { //waiting for T stabilisation
lastStartMsgTxt = "#HotPrp:" + String( (HOTCIRCLE_CHECK_PREPARE -(unsigned long)(millis_now - millis_last_hotWP_off))/1000 );
} else if (hotside_circle_state == 0) { //sleeping, hot CP off, waiting for next check cycle
lastStartMsgTxt = "#HotSlp:" + String( (HOTCIRCLE_START_EVERY -(unsigned long)(millis_now - millis_last_hotWP_on))/1000 );
}
#else ifdef SETPOINT_TS1
i+=1;
#endif
//4 countdown, compressor min. cycle
if (((unsigned long)(millis_now - millis_last_heatpump_on) > mincycle_poweroff) || (millis_last_heatpump_on == 0) ) {
i+=1;
} else {
if (millis_last_heatpump_on != 0){
lastStartMsgTxt = "#HPSlp:" + String( (mincycle_poweroff -(unsigned long)(millis_now - millis_last_heatpump_on))/1000 );
}
}
if ( (TcrcE == 1 && Tcrc > cT_crc_min) || (TcrcE^1)) {i+=1;} else { lastStartMsgTxt = F("#CaseCold"); } //5
if ( (TaeE == 1 && Tae > cT_coldref_min) || (TaeE^1)) {i+=1;} else { lastStartMsgTxt = F("#Tae cT_coldref_min) || (TbeE^1)) {i+=1;} else { lastStartMsgTxt = F("#Tbe cT_cold_min) || (TciE^1)) {i+=1;} else { lastStartMsgTxt = F("#Tci cT_cold_min) || (TcoE^1)) {i+=1;} else { lastStartMsgTxt = F("#TcoMax"); } //10
if ( (ThiE == 1 && Thi < cT_hot_max) || (ThiE^1)) {i+=1;} else { lastStartMsgTxt = F("#Thi>Max"); } //11
//t1_crc > t2_cold_in && ???
if ( (TcrcE == 1 && Tcrc < cT_crc_max) || (TcrcE^1)) {i+=1;} else { lastStartMsgTxt = F("#CaseHot"); } //12
if ( (TbcE == 1 && Tbc < cT_before_condenser_max) || (TbcE^1)) {i+=1;} else { lastStartMsgTxt = F("#Tbc>Max"); } //13
//if ( (TregE == 1 && Treg > cT_crc_min) || (TregE^1)) {i+=1;} else { lastStartMsgTxt = F("RegCold"); } //14
//if ( (TsucE == 1 && Tsuc > cT_coldref_min) || (TsucE^1)) {i+=1;} else { lastStartMsgTxt = F("Suc N) and (t watertank > target) )
#ifdef SETPOINT_THI
if ( heatpump_state == 1 && ((unsigned long)(millis_now - millis_last_heatpump_off) > mincycle_poweron) && (Thi > T_setpoint) && errorcode == ERR_OK) {//or Ts1, if tank heater
#endif
#ifdef SETPOINT_TS1
if ( heatpump_state == 1 && ((unsigned long)(millis_now - millis_last_heatpump_off) > mincycle_poweron) && (Ts1 > T_setpoint) && errorcode == ERR_OK) {//or Thi, if default warm floor heat pump
#endif
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
LSCint = LSCint_normal;
lastStopCauseTxt=F("Normal_stop");
fl_printSS_lastStopCauseTxt = 1;
//PrintSS(lastStopCauseTxt);
}
//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) ) || ((_1st_start_sleeped == 0 ) && (hotside_circle_state == 0)) ){
PrintSSch(IDX_HWPON);
millis_last_hotWP_off = millis_now;
hotside_circle_state = 1;
}
#ifdef SETPOINT_THI
if ( (heatpump_state == 0) && (hotside_circle_state == 0) && ((unsigned long)(millis_now - millis_last_hotWP_on) > HOTCIRCLE_START_EVERY) ) { //process START_EVERY for hot side
millis_last_hotWP_off = millis_now;
hotside_circle_state = 1;
//PrintSS(F("HWP ON by startevery"));
lastStartMsgTxt = F("HWP_ON_by_ev");
fl_printSS_lastStartMsgTxt = 1;
}
#endif
if ( (heatpump_state == 0) && (hotside_circle_state == 1) ) {
if ( ( (unsigned long)(millis_now - millis_last_heatpump_on) > deffered_stop_hotcircle) || millis_last_heatpump_on == 0) { //deffered stop aftret heat pump stop and correct processing of 1st start, 1st_start sleeped flag not used - there's another logic
/*
//useful for tank heater with Ts1 as setpont control and large intermediate water reservoir
if ( (ThoE == 1 && Tho < (Ts1 + cT_hotcircle_delta_min)) ||
(ThiE == 1 && Thi < (Ts1 + cT_hotcircle_delta_min)) ) {
PrintSS(F("Hot CP OFF 1"));
millis_last_hotWP_on = millis_now;
hotside_circle_state = 0;
} else {
PrintSS(F("Hot CP OFF 2"));
millis_last_hotWP_on = millis_now;
hotside_circle_state = 0;
}
*/
if ( (unsigned long)(millis_now - millis_last_hotWP_off) > HOTCIRCLE_STOP_AFTER) { //and START_EVERY processing
#ifdef SETPOINT_THI
if ( Thi > T_setpoint ) {
#endif
#ifdef SETPOINT_TS1
if ( Ts1 > T_setpoint ) {
#endif
//PrintSS(F("HWP OFF"));
lastStartMsgTxt = F("HWP_OFF");
fl_printSS_lastStartMsgTxt = 1;
millis_last_hotWP_on = millis_now;
hotside_circle_state = 0;
}
}
}
}
//heat if we can, just in case, ex. if lost power, usefull for tank heater with large intermediate water reservoir
/*
if ( (hotside_circle_state == 0) &&
( ThoE == 1 && Tho > (Ts1 + cT_hotcircle_delta_min) ) ||
( ThiE == 1 && Thi > (Ts1 + cT_hotcircle_delta_min) ) ) {
PrintSS(F("Hot WP ON"));
hotside_circle_state = 1;
}
*/
//process_cold_side_pump:
//start if (heatpump_enabled)
//stop if (heatpump_disbled)
//start if tci < cold_min
if ( (heatpump_state == 1) && (coldside_circle_state == 0) ) {
//PrintSS(F("CWP_ON"));
millis_last_coldWP_off = millis_now;
coldside_circle_state = 1;
}
if ( (heatpump_state == 0) && (TciE == 1) && (Tci > -127.0) && (Tci < cT_cold_min) && (coldside_circle_state == 0) ) {
//PrintSS(F("CWP ON by ColdMin"));
lastStartMsgTxt = F("CWP_ON_CoMin");
fl_printSS_lastStartMsgTxt = 1;
millis_last_coldWP_off = millis_now;
coldside_circle_state = 1;
}
if ( (heatpump_state == 0) && (coldside_circle_state == 1) ) { //is on
if ( (TciE == 1 && Tci > cT_cold_min) || (TciE^1)) { //does not overfrozen
//next: deal with unstable env. to prevent false starts (water tank with dynamic flows, maybe air heating): stop CWP while waiting period if false start
//stop if T>S OR if not needed by prepare
#ifdef SETPOINT_THI
if ( ( Thi > T_setpoint ) || ((unsigned long)(millis_now - millis_last_coldWP_off) > (COLDCIRCLE_PREPARE*2)) ) {
#endif
#ifdef SETPOINT_TS1
if ( ( Ts1 > T_setpoint ) || ((unsigned long)(millis_now - millis_last_coldWP_off) > (COLDCIRCLE_PREPARE*2)) ) {
#endif
//PrintSS(F("CWP_OFF"));
coldside_circle_state = 0;
}
}
}
//protective_cycle:
//stop if
// (error)
// (t hot out > hot max)
// (t hot in > hot max)
// (crc 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 == ERR_OK ){
if (ThoE == 1 && Tho > cT_hot_max) {heatpump_state = 0; lastStopCauseTxt = F("P.Tho"); }
if (ThiE == 1 && Thi > cT_hot_max) {heatpump_state = 0; lastStopCauseTxt = F("P.Thi"); }
if (TcrcE == 1 && Tcrc > cT_crc_max) {heatpump_state = 0; lastStopCauseTxt = F("P.Tcrc"); }
if (TaeE == 1 && Tae < cT_coldref_min) {heatpump_state = 0; lastStopCauseTxt = F("P.Tae"); }
if (TbeE == 1 && Tbe < cT_before_evap_work_min) {heatpump_state = 0; lastStopCauseTxt = F("P.Tbe"); }
//if (TsucE == 1 && Tsuc < cT_coldref_min) {heatpump_state = 0; lastStopCauseTxt = F("P.Tsuc"); }
if (TbcE == 1 && Tbc > cT_before_condenser_max) {heatpump_state = 0; lastStopCauseTxt = F("P.Tbc"); }
if (TciE == 1 && Tci < cT_cold_min) {heatpump_state = 0; lastStopCauseTxt = F("P.Tci"); }
if (TcoE == 1 && Tco < cT_cold_min) {heatpump_state = 0; lastStopCauseTxt = F("P.Tco"); }
if (heatpump_state == 0){
LSCint = LSCint_protective;
fl_printSS_lastStopCauseTxt = 1;
//PrintSS(lastStopCauseTxt);
millis_last_heatpump_on = millis_now;
}
}
//5 minutes workout checks
//alive_check_cycle_after_5_mins:
//(old)error if
//(new)not error, just poweroff all
//next disabled: issues after a deep freeze, long time needed for stabilisation
//DISABLED// or (t cold in - t cold out < t workingok min diff)
//DISABLED// or (t hot out - t hot in < t workingok min diff)
// or (crc t < 25'C)
// or wattage too low
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 ) && ( TcrcE == 1 && Tcrc < cT_workingOK_crc_min ) ) {
//errorcode = ERR_HEATPUMP;
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
LSCint = LSCint_protective;
lastStopCauseTxt = F("P.W.TcrcMIN");
fl_printSS_lastStopCauseTxt = 1;
//PrintSS(lastStopCauseTxt);
}
if ( ( errorcode == ERR_OK ) && ( async_wattage < c_workingOK_wattage_min ) ) {
//errorcode = ERR_WATTAGE;
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
LSCint = LSCint_protective;
lastStopCauseTxt = F("P.W.wattMIN");
fl_printSS_lastStopCauseTxt = 1;
//PrintSS(lastStopCauseTxt);
}
//digitalWrite(RELAY_HEATPUMP, heatpump_state); ////!!! old, now halifised
}
//disable pump by t.sensor error, sequentially
if ( heatpump_state == 1 && errorcode == ERR_T_SENSOR ) {
sequential_errors += 1;
if (sequential_errors > MAX_SEQUENTIAL_ERRORS) {
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
LSCint = LSCint_error;
lastStopCauseTxt = t_sensorErrString;
fl_printSS_lastStopCauseTxt = 1;
}
//PrintSS(t_sensorErrString);
}
if ( errorcode == ERR_OK ) { //auto-clean counter just in case
sequential_errors = 0;
}
//disable pump by pressure error, immediately
if ( heatpump_state == 1 && ( errorcode == ERR_P_HI || errorcode == ERR_P_LO ) ) {
millis_last_heatpump_on = millis_now;
heatpump_state = 0;
if (errorcode == ERR_P_HI) {
lastStopCauseTxt = F("E.PressHot");
} else if (errorcode == ERR_P_LO) {
lastStopCauseTxt = F("E.PressCold");
}
LSCint = LSCint_error;
fl_printSS_lastStopCauseTxt = 1;
//PrintSS(lastStopCauseTxt);
}
//!!! self-test
///heatpump_state = 1;
halifise();
if (errorcode == ERR_T_SENSOR) {
PrintSS(t_sensorErrString);
}
if (fl_printSS_lastStartMsgTxt == 1){
PrintSS(lastStartMsgTxt);
fl_printSS_lastStartMsgTxt = 0;
}
if (fl_printSS_lastStopCauseTxt == 1){
PrintSS(lastStopCauseTxt);
fl_printSS_lastStopCauseTxt = 0;
}
#endif
//process errors
//beep N times error
if ( errorcode != ERR_OK ) {
LED_OK_state = 0;
LED_ERR_state = 1;
if ( ((unsigned long)(millis_now - millis_notification) > millis_notification_interval) || millis_notification == 0 ) {
millis_notification = millis_now;
outString = F("Err: ");
PrintSS_SaI(errorcode);
for ( i = 0; i < errorcode; i++) {
LED_ERR_state = 0;
halifise();
analogWrite(speakerOut, 10); delay (500);
LED_ERR_state = 1;
halifise();
analogWrite(speakerOut, 0); delay (500);
}
}
} else {
LED_OK_state = 1;
LED_ERR_state = 0;
halifise();
}
}
if (Serial.available() > 0) {
inChar = Serial.read();
if ( inChar == 0x1B ) {
skipchars_local += 3;
inChar = 0x00;
millis_escinput_local = millis();
}
if ( skipchars_local != 0 ) {
millis_charinput_local = millis();
if ((unsigned long)(millis_charinput_local - millis_escinput_local) < 16*2 ) { //2 chars for 2400
if (inChar != 0x7e) {
skipchars_local -= 1;
}
if (inChar == 0x7e) {
skipchars_local = 0;
}
if (inChar >= 0x30 && inChar <= 0x35) {
skipchars_local += 1;
}
inChar = 0x00;
} else {
skipchars_local = 0;
}
}
_ProcessInChar();
}
if (RS485Serial.available() > 0) {
//PrintSS("some on 485.."); //!!!debug
#ifdef RS485_HUMAN
if (RS485Serial.available()) {
inChar = RS485Serial.read();
//RS485Serial.print(inChar); //!!!debug
if ( inChar == 0x1B ) {
skipchars_485 += 3;
inChar = 0x00;
millis_escinput_485 = millis();
}
if ( skipchars_485 != 0 ) {
millis_charinput_485 = millis();
//if (millis_escinput_485 + 2 > millis_charinput_485)
if ((unsigned long)(millis_charinput_485 - millis_escinput_485) < 16*2 ) { //2 chars for 2400
if (inChar != 0x7e) {
skipchars_485 -= 1;
}
if (inChar == 0x7e) {
skipchars_485 = 0;
}
if (inChar >= 0x30 && inChar <= 0x35) {
skipchars_485 += 1;
}
inChar = 0x00;
} else {
skipchars_485 = 0;
}
}
_ProcessInChar();
}
#endif
#ifdef RS485_JSON
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
dataBuf[index] = inChar; // Store it
index++; // Increment where to write next
dataBuf[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
/*
if (dataBuf[0] != 0x00) {
PrintSS("-");
PrintSS(dataBuf);
PrintSS("-");
}
*/
//or this debug
/*
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(10);
RS485Serial.println(dataBuf);
RS485Serial.flush();
RS485Serial.println(index);
*/
//ALL lines must be terminated with \n!
if ( (dataBuf[0] == hostID) && (dataBuf[1] == devID) ) {
// COMMANDS:
// G (0x47): (G)et main data
// TNN.NN (0x54): set aim (T)emperature
// ENN.NN (0x45): set (E)EV difference aim
digitalWrite(SerialTxControl, RS485Transmit);
halifise();
delay(1);
//PrintSS(freeMemory());
outString = "";
outString = devID;
outString += hostID;
outString += "A "; //where A is Answer, space after header
char *outChar=&outString[0];
if ( (dataBuf[2] == 0x47 ) ) {
//PrintSS("G");
//WARNING: this procedure can cause "NO answer" effect if no or few T sensors connected
//outString = "";
//if (TsgE) { outString += ",\"TSG\":" + String(Tsg); }
//if (TslE) { outString += ",\"TSL\":" + String(Tsl); }
//if (TbvE) { outString += ",\"TBV\":" + String(Tbv); }
//if (TsucE) { outString += ",\"TSUC\":" + String(Tsuc);}
//RS485Serial.write(outChar); //dirty hack to transfer long string
//RS485Serial.flush();
//delay (1); //lot of errors without delay
outString += "{";
outString += "\"E1\":" + String(errorcode);
if (TciE) { outString += ",\"TCI\":"; ApToOut_D(Tci); }
if (TcoE) { outString += ",\"TCO\":"; ApToOut_D(Tco); }
if (TbeE) { outString += ",\"TBE\":"; ApToOut_D(Tbe); }
if (TaeE) { outString += ",\"TAE\":"; ApToOut_D(Tae); }
if (Ts1E) { outString += ",\"TS1\":"; ApToOut_D(Ts1); }
if (Ts2E) { outString += ",\"TS2\":"; ApToOut_D(Ts2); }
if (TcrcE) { outString += ",\"TCRC\":"; ApToOut_D(Tcrc);}
if (TregE) { outString += ",\"TR\":"; ApToOut_D(Treg);}
RS485Serial.write(outChar); //dirty hack to transfer long string
RS485Serial.flush();
delay (1); //lot of errors without delay
outString = "";
if (TacE) { outString += ",\"TAC\":"; ApToOut_D(Tac); }
if (TbcE) { outString += ",\"TBC\":"; ApToOut_D(Tbc); }
if (ThoE) { outString += ",\"THO\":"; ApToOut_D(Tho); }
if (ThiE) { outString += ",\"THI\":"; ApToOut_D(Thi);}
outString += ",\"W1\":"; ApToOut_D(async_wattage);
outString += ",\"EEVP\":" + String(EEV_cur_pos);
outString += ",\"EEVA\":"; ApToOut_D(T_EEV_setpoint);
#ifndef EEV_ONLY
outString += ",\"A1\":"; ApToOut_D(T_setpoint); //(A)im (target)
outString += ",\"RP\":" + String(heatpump_state*RELAY_HEATPUMP);
outString += ",\"RH\":" + String(hotside_circle_state*RELAY_HOTSIDE_CIRCLE);
outString += ",\"RC\":" + String(coldside_circle_state*1);
outString += ",\"RCRCH\":" + String(crc_heater_state*3);
//if (TregE) { outString += ",\"RRH\":" + String(reg_heater_state*4);}
//RS485Serial.write(outChar); //dirty hack to transfer long string
//RS485Serial.flush();
//delay (1); //lot of errors without delay
#endif
RS485Serial.write(outChar); //dirty hack to transfer long string
RS485Serial.flush();
delay (1); //lot of errors without delay
outString = "";
outString = ",\"LSC\":\"";
outString += lastStopCauseTxt;
outString += ("\"");
//RS485Serial.write(outChar); //dirty hack to transfer long string
//RS485Serial.flush();
//delay (1); //lot of errors without delay
outString += ",\"LSM\":\"";
outString += lastStartMsgTxt;
outString += ("\"");
outString += "}";
} else if ( (dataBuf[2] == 0x54 ) || (dataBuf[2] == 0x45 )) { //(T)arget or (E)EV target format NN.NN, text
if ( isDigit(dataBuf[ 3 ]) && isDigit(dataBuf[ 4 ]) && (dataBuf[ 5 ] == 0x2e) && isDigit(dataBuf[ 6 ]) && isDigit(dataBuf[ 7 ]) && ( ! isDigit(dataBuf[ 8 ])) ) {
analogWrite(speakerOut, 10);
delay (100);
analogWrite(speakerOut, 0);
char * carray = &dataBuf[ 3 ];
tempdouble = atof(carray);
if (dataBuf[2] == 0x54 ){
if (tempdouble > cT_setpoint_max) {
outString += "{\"r\":\"too hot!\"}";
} else if (tempdouble < cT_setpoint_min) {
outString += "{\"r\":\"too cold!\"}";
} else {
T_setpoint = tempdouble;
_HotWPon_by_Setpoint_update();
outString += "{\"r\":\"ok, new value: ";
ApToOut_D(T_setpoint);
outString += "\"}";
}
}
if (dataBuf[2] == 0x45 ) {
if (tempdouble > 10.0) { //!!!!!!! hardcode !!!
outString += "{\"r\":\"too hot!\"}";
} else if (tempdouble < 0.1) { //!!!!!!! hardcode !!!
outString += "{\"r\":\"too cold!\"}";
} else {
T_EEV_setpoint = tempdouble;
outString += "{\"r\":\"ok, new EEV value: ";
ApToOut_D(T_EEV_setpoint);
outString += "\"}";
}
}
} else {
outString += "{\"r\":\"NaN, format: NN.NN\"}";
}
} else {
//default, just for example
outString += "{\"r\":\"no_command\"}";
}
//crc.integer = CRC16.xmodem((uint8_t& *) outString, outString.length());
//outString += (crc, HEX);
outString += "\n";
RS485Serial.write(outChar);
}
index = 0;
for (i=0;i < (BUFSIZE);i++) { //clear buffer
dataBuf[i]=0;
}
RS485Serial.flush();
digitalWrite(SerialTxControl, RS485Receive);
delay(1);
#endif
#ifdef RS485_MODBUS
index = 0;
z = 0; //error flag
while ( 1 == 1 ) {//9600
//read
//!!!!!!!
//Serial.println("-");
if (RS485Serial.available()) {
if(index < BUFSIZE) {
inChar = RS485Serial.read();
//Serial.print(inChar, HEX);
//Serial.print(" ");
dataBuf[index] = inChar;
index++;
dataBuf[index] = '\0';
delayMicroseconds(80); //yep, 80, HERE
} else {
z = 1;
while (RS485Serial.available()) {
inChar = RS485Serial.read();
delayMicroseconds(1800);
}
break;
}
} else {
//Serial.print(".");
tmic1 = micros();
for (i = 0; i < 10; i++) {
delayMicroseconds(180);
if (RS485Serial.available()){
//Serial.print("babaika");
//Serial.println(i);
tmic2 = micros();
break;
}
tmic2 = micros();
if ( (unsigned long)(tmic2 - tmic1) > 1800 ){
i = 10;
break;
}
}
if (i == 10 && RS485Serial.available()) {
z = 2;
i = 0;
while (RS485Serial.available()) {
if (i > 200){
break;
}
inChar = RS485Serial.read();
delayMicroseconds(1800);
i++;
}
break;
} else if (!RS485Serial.available()) {
break;
} else if (RS485Serial.available()) {
continue;
} else {
//PrintSS(F("e2245"));
}
}
}
//check CRC
if (index < 3) {
z+= 10;
}
if ( dataBuf[1] == 0x03 && ( (index % 8 ) == 0) && index > 8 ) { //automatic "duplicated message" detector, can be found if lot of T sensors absent and requests are too fast
index = 8;
}
crc16 = SEED;
for (x = 0; x < (index-2); x++) {
Calc_CRC(dataBuf[x]);
}
x = dataBuf[index - 2];
y = dataBuf[index - 1];
if (( x != (crc16 & 0xFF )) || ( y != (crc16 >> 8))) {
z += 100;
}
//PrintSS(F("-----"));
if ( z != 0 ) {
//probably another proto
//PrintSS(F("MmsgERR: "));
/*Serial.println(z);
for (x =0; x MODBUS_MR) { //0x03
z = 3;
}
if (dataBuf[1] == 0x03 && dataBuf[5] > MODBUS_MR) { //0x05
z = 5;
}
i = 0;
//dataBuf[i] = devID;
//unchanged! can be devID or 0x00
i++;
if (z == 0) {
//PrintSS(F("ModParse"));
x = dataBuf[3]; //addr
y = dataBuf[5]; //num
if (dataBuf[1] == 0x03) {
//PrintSS(F("F03"));
dataBuf[i] = 0x03;
i++;
//the most significant byte is sent first
dataBuf[i] = y*2;
i++; // data
for (u = x; u < (x+y); u++) {
if (u > MODBUS_MR){
z = 2;
break;
}
switch (u) {
case 0x00:
Add_Double_To_Buf_IntFract(Tci); //uses dataBuf, i
break;
case 0x01:
Add_Double_To_Buf_IntFract(Tco); //uses dataBuf, i
break;
case 0x02:
Add_Double_To_Buf_IntFract(Tbe); //uses dataBuf, i
break;
case 0x03:
Add_Double_To_Buf_IntFract(Tae); //uses dataBuf, i
break;
case 0x04:
//Add_Double_To_Buf_IntFract(Tsg); //uses dataBuf, i
dataBuf[i] = 0;
i++;
dataBuf[i] = 0;
i++;
break;
case 0x05:
//Add_Double_To_Buf_IntFract(Tsl); //uses dataBuf, i
dataBuf[i] = 0;
i++;
dataBuf[i] = 0;
i++;
break;
case 0x06:
//Add_Double_To_Buf_IntFract(Tbv); //uses dataBuf, i
dataBuf[i] = 0;
i++;
dataBuf[i] = 0;
i++;
break;
case 0x07:
//Add_Double_To_Buf_IntFract(Tsuc); //uses dataBuf, i
dataBuf[i] = 0;
i++;
dataBuf[i] = 0;
i++;
break;
case 0x08:
Add_Double_To_Buf_IntFract(Ts1); //uses dataBuf, i
break;
case 0x09:
Add_Double_To_Buf_IntFract(Ts2); //uses dataBuf, i
break;
case 0x0A:
Add_Double_To_Buf_IntFract(Tcrc); //uses dataBuf, i
break;
case 0x0B:
Add_Double_To_Buf_IntFract(Treg); //uses dataBuf, i
break;
case 0x0C:
Add_Double_To_Buf_IntFract(Tac); //uses dataBuf, i
break;
case 0x0D:
Add_Double_To_Buf_IntFract(Tbc); //uses dataBuf, i
break;
case 0x0E:
Add_Double_To_Buf_IntFract(Tho); //uses dataBuf, i
break;
case 0x0F:
Add_Double_To_Buf_IntFract(Thi); //uses dataBuf, i
break;
case 0x10:
dataBuf[i] = 0;
i++;
dataBuf[i] = errorcode;
i++;
break;
case 0x11:
dataBuf[i] = (int)async_wattage >> 8;
i++;
dataBuf[i] = (int)async_wattage & 0xFF;
i++;
break;
case 0x12:
dataBuf[i] = 0;
i++;
dataBuf[i] = 0;
bitWrite(dataBuf[i], 0, heatpump_state);
bitWrite(dataBuf[i], 1, hotside_circle_state);
bitWrite(dataBuf[i], 2, coldside_circle_state);
bitWrite(dataBuf[i], 3, crc_heater_state);
//bitWrite(dataBuf[i], 4, reg_heater_state);
i++;
break;
case 0x13:
Add_Double_To_Buf_IntFract(T_EEV_setpoint); //uses dataBuf, i
break;
case 0x14:
Add_Double_To_Buf_IntFract(T_setpoint); //uses dataBuf, i
break;
case 0x15:
dataBuf[i] = (int)EEV_cur_pos >> 8;
i++;
dataBuf[i] = (int)EEV_cur_pos & 0xFF;
i++;
break;
case 0x16:
dataBuf[i] = lastStopCauseTxt.charAt(0);
i++;
dataBuf[i] = lastStopCauseTxt.charAt(1);
i++;
break;
case 0x17:
dataBuf[i] = lastStopCauseTxt.charAt(2);
i++;
dataBuf[i] = lastStopCauseTxt.charAt(3);
i++;
break;
case 0x18:
dataBuf[i] = lastStopCauseTxt.charAt(4);
i++;
dataBuf[i] = lastStopCauseTxt.charAt(5);
i++;
break;
case 0x19:
dataBuf[i] = lastStopCauseTxt.charAt(6);
i++;
dataBuf[i] = lastStopCauseTxt.charAt(7);
i++;
break;
case 0x1A:
dataBuf[i] = lastStopCauseTxt.charAt(8);
i++;
dataBuf[i] = lastStopCauseTxt.charAt(9);
i++;
break;
case 0x1B:
dataBuf[i] = lastStopCauseTxt.charAt(10);
i++;
dataBuf[i] = lastStopCauseTxt.charAt(11);
i++;
break;
case 0x1C:
dataBuf[i] = lastStartMsgTxt.charAt(0);
i++;
dataBuf[i] = lastStartMsgTxt.charAt(1);
i++;
break;
case 0x1D:
dataBuf[i] = lastStartMsgTxt.charAt(2);
i++;
dataBuf[i] = lastStartMsgTxt.charAt(3);
i++;
break;
case 0x1E:
dataBuf[i] = lastStartMsgTxt.charAt(4);
i++;
dataBuf[i] = lastStartMsgTxt.charAt(5);
i++;
break;
case 0x1F:
dataBuf[i] = lastStartMsgTxt.charAt(6);
i++;
dataBuf[i] = lastStartMsgTxt.charAt(7);
i++;
break;
case 0x20:
dataBuf[i] = lastStartMsgTxt.charAt(8);
i++;
dataBuf[i] = lastStartMsgTxt.charAt(9);
i++;
break;
case 0x21:
dataBuf[i] = lastStartMsgTxt.charAt(10);
i++;
dataBuf[i] = lastStartMsgTxt.charAt(11);
i++;
break;
default:
dataBuf[i] = 0x00;
i++;
dataBuf[i] = 0x00;
i++;
break;
}
}
} else if (dataBuf[1] == 0x06) { //de-facto echo
//PrintSS(F("F06"));
dataBuf[i] = 0x06;
i++;
dataBuf[i] = 0x00;
i++;
dataBuf[i] = x;
i++;
switch (x) {
case 0x13:
//PrintSS(F("06F_EEV_setpoint"));
IntFract_to_tempdouble(dataBuf[4], dataBuf[5]);
//Serial.println(tempdouble);
if (tempdouble > 15.0 || tempdouble < -15.0) { //incorrectest values filter
z = 3;
break;
}
T_EEV_setpoint = tempdouble;
//Serial.println(T_EEV_setpoint);
Add_Double_To_Buf_IntFract(T_EEV_setpoint); //uses dataBuf, i
break;
case 0x14:
//PrintSS(F("06F_T_setpoint"));
IntFract_to_tempdouble(dataBuf[4], dataBuf[5]);
//Serial.println(tempdouble);
if (tempdouble > cT_setpoint_max || tempdouble < cT_setpoint_min) { //incorrectest values filter
z = 3;
break;
}
T_setpoint = tempdouble;
_HotWPon_by_Setpoint_update();
//Serial.println(T_setpoint);
Add_Double_To_Buf_IntFract(T_setpoint); //uses dataBuf, i
break;
//case 0x15:
// //EEV_cur_pos
// break;
default:
z = 3;
break;
}
} else {
PrintSSch(IDX_UNKNF);
z = 1;
}
if (z != 0) {
i = 1;
bitWrite(dataBuf[i], 7, 1);
i++;
dataBuf[i] = z;
i++;
}
crc16 = SEED;
for (x = 0; x < (i); x++) {
Calc_CRC(dataBuf[x]);
}
dataBuf[i] = crc16 & 0xFF;
i++;
dataBuf[i] = crc16 >> 8;
i++;
RS485Serial.write(dataBuf, i);
RS485Serial.flush();
delay (1);
//!!! debug
/*
for (x = 0; x