Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

heatpump

Created Diff never expires
424 removals
621 lines
613 additions
811 lines
/*
/*
Cheap Heat Pump Controller (CHPC) firmware.
Valden Heat Pump.
Copyright (C) 2018-2019 Gonzho (gonzho@web.de)
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
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
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
(at your option) any later version.


This program is distributed in the hope that it will be useful,
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
GNU General Public License for more details.


You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
along with this program. If not, see <http://www.gnu.org/licenses/>.
See https://github.com/gonzho000/chpc/ for more details
*/
*/





//-----------------------USER OPTIONS-----------------------
//-----------------------USER OPTIONS-----------------------
//#define SELFTEST_RELAYS_LEDS_SPEAKER //speaker and relays QA test, uncomment to enable
#define BOARD_TYPE_G //Type "G", PCB from github.com/gonzho000/chpc/
//#define SELFTEST_EEV //EEV QA test, uncomment to enable
//#define BOARD_TYPE_F //Type "F"
//#define SELFTEST_T_SENSORS //temperature sensors QA test, uncomment to enable
//#define BOARD_TYPE_G9 //Type "G9" or "G-MAX", current testing

//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-----------------------


//#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"
//#define DISPLAY_NONE -1


#define INPUTS_AS_BUTTONS 1 //pulldown resistors required


//-----------------------Fine Tuning OPTIONS-----------------------
//#define RS485_PYTHON 1
//next sections: advanced options
#define RS485_HUMAN 2
//#define RS485_NONE 3


#define EEV_SUPPORT
//#define EEV_ONLY //NO target, no relays. Oly EEV, Tae, Tbe, current sensor and may be additional T sensors


#define HUMAN_AUTOINFO 10000 //print stats to console


//-----------------------T Sensors -----------------------
#define WATCHDOG //only if u know what to do
//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-----------------------
//-----------------------TEMPERATURES-----------------------
#define MAGIC 0x66; //change this value if you want to rewrite the T setpoint in EEPROM
#define T_SETPOINT_MAX 45.0; //defines max temperature that ordinary user can set
#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_HOTCIRCLE_DELTA_MIN 2.0; //useful for "water heater vith intermediate heat exchanger" scheme, Target == sensor in water, hot side CP will be switched on if "target - hot_out > T_HOTCIRCLE_DELTA_MIN"
#define T_SETPOINT_MAX 48.0; //maximum "setpoint" temperature that an ordinary user can set
#define T_SUMP_MIN 9.0; //HP will not start if T lower
#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_SUMP_MAX 110.0; //HP will stop if T higher
#define T_CRANKCASE_MIN 8.0; //compressor (crankcase) min. temperature, HP will not start if T lower
#define T_SUMP_HEAT_THRESHOLD 16.0; //sump heater will be powered on 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_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_AFTER_EVAPORATOR_MIN -7.0; //suction MIN, HP stops if lower, anti-freeze and anti-liquid at suction protection
#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 -8.0; //cold loop anti-freeze: stop if inlet or outlet temperature lower
#define T_COLD_MIN -15.5; //cold side (glycol) loop freeze protection: HP stops if inlet or outlet temperature lower
#define T_HOTOUT_MAX 50.0; //hot loop: stop if outlet temperature higher than this
#define T_HOT_MAX 50.0; //hot loop: HP stops if hot side inlet or outlet temperature higher than this threshold
#define T_WORKINGOK_SUMP_MIN 30.0; //compressor MIN temperature, HP stops if it lower after 5 minutes of pumping, need to be not very high to normal start after deep freeze

//#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-----------------------
//-----------------------TUNING OPTIONS -----------------------
//If you are using a capillary tube or TXV: simply skip next section.
#define MAX_WATTS 1170.0 //user for power protection
//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
#define DEFFERED_STOP_HOTCIRCLE 3000000 //50 mins


//steps tuning: milliseconds per fast and slow (precise) steps
#define POWERON_PAUSE 300000 //5 mins
#define EEV_PULSE_FCLOSE_MILLIS 20 //(20 tube evaporator) fast closing, closing on danger (milliseconds per step)
#define MINCYCLE_POWEROFF 300000 //5 mins
#define EEV_PULSE_CLOSE_MILLIS 45000 //(50000 tube evaporator) accurate closing while the compressor works (milliseconds per step)
#define MINCYCLE_POWERON 3600000 //60 mins
#define EEV_PULSE_WOPEN_MILLIS 20 //(20 tube evaporator) standby (waiting) pos. set (milliseconds per step)
#define POWERON_HIGHTIME 10000 //10 sec, defines time after start when power consumption can be 2 times greater than normal
#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
//EEV
#define EEV_CLOSE_ADD_PULSES 8 //read below, additional steps after zero position while full closing
#define EEV_MAXPULSES 480
#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_PULSE_FCLOSE_MILLIS 20 //fast close, set waiting pos., close on danger
#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_PULSE_CLOSE_MILLIS 50000 //precise close
#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_PULSE_WOPEN_MILLIS 20 //waiting pos. set
#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_PULSE_FOPEN_MILLIS 1300 //fast open, fast search
#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
#define EEV_PULSE_OPEN_MILLIS 60000 //precise open


//additional options
#define EEV_STOP_HOLD 500 //0.1..1sec for Sanhua
#define EEV_REOPENLAST 1 ///1 = reopen to last position on compressor start, useful for ordinary schemes with everyday working cycles, 0 = not
#define EEV_CLOSE_ADD_PULSES 8 //read below, close algo
#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_OPEN_AFTER_CLOSE 47 //0 - close to zero position, than close on EEV_CLOSE_ADD_PULSES (close insurance, read EEV manuals for this value)
//#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
//N - close to zero position, than close on EEV_CLOSE_ADD_PULSES, than open on EEV_OPEN_AFTER_CLOSE pulses
//i.e. it is "waiting position" while HP not working
#define EEV_MINWORKPOS 52 //position will be not less during normal work, set after compressor start
#define EEV_PRECISE_START 8.6 //T difference, threshold: make slower pulses if (real_diff-target_diff) less than this value. Used for fine auto-tuning.
#define EEV_EMERG_DIFF 2.5 //if dangerous condition: real_diff =< (target_diff - EEV_EMERG_DIFF) occured 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) than 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: EEV will be closed (calibrated) every 24 hours, done while HP is NOT working
#define EEV_TARGET_TEMP_DIFF 4.0 //target difference between Before Evaporator and After Evaporator, the head of whole algo
//#define EEV_DEBUG //debug, usefull during system fine tuning, "RS485_HUMAN" only


//do not use next option if you're not sure what are you doing
#define MAGIC 0x55 //change if u want to reinit T sensors
//#define EEV_DEBUG //debug, useful during system fine-tuning, works both with local serial and RS485_HUMAN
//-----------------------USER OPTIONS END -----------------------
//-----------------------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 INPUTS_AS_INPUTS 2 //
#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)
//#define RS485_MACHINE 3 //?? or part of Python?
//-----------------------Fine Tuning OPTIONS END -----------------------


//-----------------------changelog-----------------------
//-----------------------changelog-----------------------
/*
/*
v1.0, 01 Sep 2019:
v1.0:
+ initial version, hardware and software branch ready
- Displays support

- define TYPE F/G and rearrange ports
v1.1: 21 Sep 2019:
- multi-DS18b20 support on lane
+ Dev and Host ID to header
- 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
v1.2: 20 Dec 2019:
- 2 on-board buttons support: +/- aim
+- ?seems to be fixed minor bug while HP stopped: wattage is 0, if tCrc < T_CRANKCASE_HEAT_THRESHOLD and may be few sensors absence
- DISPLAY: indication: real and aim
+ min_user_t/max_user_t to header
- RS485_HUMAN: remote commands +,-,G,0x20/?/Enter

- buttons: < > increase_decrease t
v1.3: 05 Jan 2020:
- simpliest thermostat scheme: only T target
+ manual EEV mode (high priority, ex: new system 1st starts and charge)
- rename all procs
+ rs485_modbus
- RS485_PYTHON: print to console inspite of mode diring init proc
+ reopen to last EEV value at startup
- faster wattage overload processing

- write aim value to EE if needed, period: 15 mins (eq. 1041 days)
v1.4: 22 Jan 2020
- deferred stop of hot side circle
+ crankcase naming
- 80 microseconds at 9600

v1.5: 05 Jun 2020
+ minor modbus updates


v1.6: 09 Dec 2020
v1.1, 15 Apr 2019:
+ NO_EEV option
- HUMAN_AUTOINFO time
+ some variables renames
- EEV_ONLY mode
+ Tho instead of Thi (stop conditions) bugfix
- EEV_Support
+ Last Start Message added
- EEV auto poweron/poweroff every 10 sec
- EEV_recalibration_time to stop HP and recalibrate EEV from zero level ex: every 24 hours


v1.7: 03 Feb 2021
v1.2, 16 Apr 2019:
+ 1.3 PCB revision support, previous revisions also supported
- "Type F" support
+ 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
v1.3, 30 Apr 2019:
+ very rare case: 0.0 readings, 2-3 attempts then pass 0.0
- EEV changed "overheating" to "delta T"
+ countdown for compressor relay after cold CP start (stab. cold loop T)
- EEV algo v1.1
+ self-test options to header


v1.9-1.11: 25-27 Feb 2021:
v1.4, 02 Jun 2019:
+ lot of small workflow logic and user terminal changes
- minor fixes
- EEV more asyncy
- T options to header


v1.12: 21 Mar 2021:
v1.5, 01 Jul 2019:
+ TS1/THO #define way fix
- prototyping 9
+ CWP and HWP prepare optimisation


v1.13: 26 Mar 2021:
v1.6, 30 Apr 2021:
+ rounding error via Modbus found and fixed
- sensors init issue fix


//TODO:
//TODO:
? lower bit resolution for all sensors, except Tbe, Tae, Ts1 ?
- 0.0 to -127 fix: only 2 attempts than pass 0.0
? poss. DoS: infinite read to nowhere, fix it, set finite counter (ex: 200)
- 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
- Dev and Host ID to header
? ref. migration protection for summer season with long waiting periods: start cold circle and crankcase heater if tCrc =< tci+1, add option to header
- add speaker and err code for ""ERR: no Tae or Tbe for EEV!""
? EEV manual mode and position by RS485 python or modbus command ?
- min_user_t/max_user_t to header
? add speaker and err code for ""ERR: no Tae or Tbe for EEV!""
- rs485_modbus
? deffered HWP stop: check HP stop cause, stop HWP if protective/error stop
- full relays halification
? wclose and fclose to EEV
? wclose and fclose to EEV
? valve_4way
- liquid ref. protection: start cold circle and sump heater if tsump =< tco/tci+1, add option to header
? rewite re-init proc from MAGIC to another way
- periodical start of hot side circle
? EEV: target to EEPROM (?? no need ?)
- valve_4way
? EEV: define maximum working position
- inputs support
- ? emergency jumper 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
- ? few devices at same lane for RS485_HUMAN
*/
*/
//-----------------------changelog END-----------------------
//-----------------------changelog END-----------------------


// DS18B20 pins: GND DATA VDD
// DS18B20 pins: GND DATA VDD


//Connections:
//Connections:
//DS18B20 Pinout (Left to Right, pins down, flat side toward you)
//DS18B20 Pinout (Left to Right, pins down, flat side toward you)
//- Left = Ground
//- Left = Ground
//- Center = Signal (Pin N of arduino): (with 3.3K to 4.7K resistor to +5 or 3.3 )
//- Center = Signal (Pin N of arduino): (with 3.3K to 4.7K resistor to +5 or 3.3 )
//- Right = +5 or +3.3 V
//- Right = +5 or +3.3 V
//
//


//Speaker

//
//
// high volume scheme: +---- +5V (12V not tested)
// high volume scheme: +---- +5V (12V not tested)
// |
// |
// +----+
// +----+
// 1MOhm piezo
// 1MOhm piezo
// +----+
// +----+
// |(C)
// |(C)
// pin -> 1.6 kOhms -> (B) 2n2222 < front here
// pin -> 1.6 kOhms -> (B) 2n2222 < front here
// |(E)
// |(E)
// +--- GND
// +--- GND
//
//


/*
/*
scheme SCT-013-000:
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
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
pins are interchangeable due to AC


32 Ohms (22+10) between sensor pins (35 == ideal)
32 Ohms (22+10) between sensor pins (35 == ideal)


Pin1:
Pin1:
- via elect. cap. to GND
- via elect. cap. to GND
- via ~10K..470K resistor to GND
- via ~10K..470K resistor to GND
- via ~10K..470K resistor to +5 (same as prev.)
- via ~10K..470K resistor to +5 (same as prev.)
if 10K+10K used: current is 25mA
if 10K+10K used: current is 25mA
use 100K+100K for 3 phases
use 100K+100K for 3 phases


Pin2:
Pin2:
- to analog pin
- to analog pin
- via 32..35 Ohms resistor to Pin1
- via 32..35 Ohms resistor to Pin1


+5 -------------------------+
+5 -------------------------+
|
|
|
|
# R1 10K+
# R1 10K+
|
|
|
|
|~2.5 at this point
|~2.5 at this point
+---------------+--------------------------------------+----+
+---------------+--------------------------------------+----+
| | | |
| | | |
#_ elect. cap. # R2 10K+ (same as R1) SCT-013-000 $ # R3 = 35 Ohms (ideal case), 32 used
#_ elect. cap. # R2 10K+ (same as R1) SCT-013-000 $ # R3 = 35 Ohms (ideal case), 32 used
| | | |
| | | |
GND --------+---------------+ +----+--------> to Analog pin
GND --------+---------------+ +----+--------> to Analog pin




WARNING: calibrate 3 sensors together, from different sellers, due to case of incorrectly worked 1 of 3 sensor
WARNING: calibrate 3 sensors together, from different sellers, due to case of incorrectly worked 1 of 3 sensor


P(watts)=220*220/R(Ohms)
P(watts)=220*220/R(Ohms)
*/
*/


//
//
//MAX 485 voltage - 5V
//MAX 485 voltage - 5V
//
//
// use resistor at RS-485 GND
// use resistor at RS-485 GND
// 1st test: 10k result lot of issues
// 1st test: 10k result lot of issues
// 2nd test: 1k, issues
// 2nd test: 1k, issues
// 3rd test: 100, see discussions
// 3rd test: 100, see discussions




//16-ch Multiplexer EN pin: active LOW, connect to GND
//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 1: heat pump
relay 2: hot side circulator pump
relay 2: hot side pump
relay 3: cold side circulator pump
relay 3: cold side pump
relay 4: crankcase heater
relay 4: (future) heatpump sump heater
relay 5: (1.3+: not used anymore)


relay 6: reserved
t0: room
relay 7: reserved
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


T sensors:
wattage1


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";
String fw_version = "1.6";

#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

#ifdef RS485_NONE
char ishuman = 0;
#endif


//hardware resources
//hardware resources
#define RELAY_HEATPUMP A2
#define OW_BUS_ALLTSENSORS 12
#define RELAY_HOTSIDE_CIRCLE A1
#define SerialTxControl 13 //RS485 Direction control DE and RE to this pin

#define PR_LOW A6
#define PR_HIGH A7

#define OW_BUS_ALLTSENSORS 9
#define speakerOut 6
#define speakerOut 6
#define em_pin1 A3
#define em_pin1 A6
#define EMERGENCY_PIN A7




String hw_version = "v1.1+";


#define LATCH_595 3
#ifdef BOARD_TYPE_G
#define CLK_595 2
String hw_version = "Type G v1.x";
#define DATA_595 7
#define RELAY_HEATPUMP 8
#define OE_595 4
#define RELAY_HOTSIDE_CIRCLE 9
#define RELAY_COLDSIDE_CIRCLE 7
#define RELAY_SUMP_HEATER 10
#define RELAY_4WAY_VALVE 11
#ifdef INPUTS_AS_BUTTONS
#define BUT_RIGHT A3
#define BUT_LEFT A2
#endif
#ifdef EEV_SUPPORT
#define EEV_1 2
#define EEV_2 4
#define EEV_3 3
#define EEV_4 5
#endif
#endif
#ifdef BOARD_TYPE_F
String hw_version = "Type F v1.x";
#define RELAY_HEATPUMP 7
#define RELAY_COLDSIDE_CIRCLE 8
#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
#ifdef EEV_SUPPORT
#define EEV_1 5
#define EEV_2 3
#define EEV_3 4
#define EEV_4 2
#endif
#ifdef INPUTS_AS_BUTTONS //not sure
#define BUT_RIGHT A3
#define BUT_LEFT A2
#endif
#endif
#ifdef BOARD_TYPE_G9
String hw_version = "Type G9 v1.x";
#define RELAY_4WAY_VALVE 8
#define RELAY_SUMP_HEATER 7


#define LATCH_595 10
#define CLK_595 9
#define DATA_595 11
#define OE_595 A1
/*
595.0: relay 10(not used)
595.1: relay 8
595.2: relay 9
595.3: relay 5 RELAY_HEATPUMP
595.4: relay 4 RELAY_COLDSIDE_CIRCLE
595.5: relay 3 RELAY_HOTSIDE_CIRCLE
595.6: relay 6
595.7: relay 7
*/
#ifdef EEV_SUPPORT
#define EEV_1 2
#define EEV_2 4
#define EEV_3 3
#define EEV_4 5
#endif
#endif
//---------------------------memory debug
//---------------------------memory debug
#ifdef __arm__
#ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
// should use uinstd.h to define sbrk but Due causes a conflict
extern "C" char* sbrk(int incr);
extern "C" char* sbrk(int incr);
#else // __ARM__
#else // __ARM__
extern char *__brkval;
extern char *__brkval;
#endif // __arm__
#endif // __arm__
int freeMemory() {
int freeMemory() {
char top;
char top;
#ifdef __arm__
#ifdef __arm__
return &top - reinterpret_cast<char*>(sbrk(0));
return &top - reinterpret_cast<char*>(sbrk(0));
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
return &top - __brkval;
return &top - __brkval;
#else // __arm__
#else // __arm__
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
#endif // __arm__
#endif // __arm__
}
}
//---------------------------memory debug END
//---------------------------memory debug END


#include <avr/wdt.h>
#include <avr/wdt.h>
#include <EEPROM.h>
#include <EEPROM.h>

//#include <FastCRC.h>
#define SEED 0xFFFF
/*FastCRC16 CRC16;
#define POLY 0xA001
union _crc {
unsigned int crc16;
unsigned int integer;
int cf;
char bytes[2];
#define MODBUS_MR 50 //50 ok now
} crc;
*/


#include <SoftwareSerial.h>
#include <SoftwareSerial.h>
#define SerialRX 12 //RX connected to RO - Receiver Output

#define SerialTX 11 //TX connected to DI - Driver Output Pin
#define SerialRX 0 //RX connected to RO - Receiver Output
#define SerialTxControl 13 //RS485 Direction control DE and RE to this pin
#define SerialTX 1 //TX connected to DI - Driver Output Pin
#define RS485Transmit HIGH
#define RS485Transmit HIGH
#define RS485Receive LOW
#define RS485Receive LOW


const char devID = 0x41;
const char hostID = 0x30;

SoftwareSerial RS485Serial(SerialRX, SerialTX); // RX, TX
SoftwareSerial RS485Serial(SerialRX, SerialTX); // RX, TX


#include <OneWire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DallasTemperature.h>
//library's DEVICE_DISCONNECTED_C -127.0
//library's DEVICE_DISCONNECTED_C -127.0


OneWire ow_ALLTSENSORS(OW_BUS_ALLTSENSORS);
OneWire ow_ALLTSENSORS(OW_BUS_ALLTSENSORS);
DallasTemperature s_allTsensors(&ow_ALLTSENSORS);
DallasTemperature s_allTsensors(&ow_ALLTSENSORS);


typedef struct {
DeviceAddress addr;
bool e; //enabled
double T;
} st_tsens;

DeviceAddress dev_addr; //temp
DeviceAddress dev_addr; //temp



st_tsens Tae ;
//short names used to prevent unreadeable source
st_tsens Tbe ;
#ifdef T_cold_in
st_tsens Ttarget;
bool TciE = 1;
st_tsens Tsump ;
#else
st_tsens Tci ;
bool TciE = 0;
st_tsens Tco ;
#endif
st_tsens Thi ;
double Tci = -127.0;
st_tsens Tho ;

st_tsens Tbc ;
#ifdef T_cold_out
st_tsens Tac ;
bool TcoE = 1;
st_tsens Touter ;
#else
st_tsens Ts1 ;
bool TcoE = 0;
st_tsens Ts2 ;
#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
#define BIT_Tae 0
bool ThoE = 1;
#define BIT_Tbe 1
#else
#define BIT_Ttarget 2
bool ThoE = 0;
#define BIT_Tsump 3
#endif
#define BIT_Tci 4
double Tho = -127.0;
#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


#ifdef T_hot_in
unsigned int used_sensors = 0 ; //bit array
bool ThiE = 1;
#else
bool ThiE = 0;
#endif
double Thi = -127.0;


double T_setpoint = T_SETPOINT;
double T_setpoint = 26.5;
double T_setpoint_lastsaved = T_setpoint;
double T_setpoint_lastsaved = T_setpoint;
double T_EEV_setpoint = EEV_TARGET_TEMP_DIFF;
double T_EEV_setpoint = EEV_TARGET_TEMP_DIFF;
double T_EEV_dt = 0.0; //real, used during run
double T_EEV_dt = 0.0; //real, used during run
const double cT_setpoint_max = T_SETPOINT_MAX;
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_hotcircle_delta_min = T_HOTCIRCLE_DELTA_MIN;
const double cT_sump_min = T_SUMP_MIN;
const double cT_crc_min = T_CRANKCASE_MIN;
const double cT_sump_max = T_SUMP_MAX;
const double cT_crc_max = T_CRANKCASE_MAX;
const double cT_sump_heat_threshold = T_SUMP_HEAT_THRESHOLD;
const double cT_crc_heat_threshold = T_CRANKCASE_HEAT_THRESHOLD;
//const double cT_sump_outerT_threshold = 18.0; //?? seems to be not useful
//const double cT_reg_heat_threshold = T_REG_HEAT_THRESHOLD;
const double cT_before_condenser_max = T_BEFORE_CONDENSER_MAX;
const double cT_before_condenser_max = T_BEFORE_CONDENSER_MAX;
const double cT_after_evaporator_min = T_AFTER_EVAPORATOR_MIN; // working evaporation presure ~= -10, it is constant due to large evaporator volume // waterhouse v1: -12 is too high
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_cold_min = T_COLD_MIN;
const double cT_hot_max = T_HOT_MAX;
const double cT_hotout_max = T_HOTOUT_MAX;
//const double cT_workingOK_cold_delta_min = 0.5; // 0.7 - 1st try, 2nd try 0.5
//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_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 cT_workingOK_sump_min = T_WORKINGOK_SUMP_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
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
//PH165X1CY : 920 Watts, 4.2 A
const double c_workingOK_wattage_min = c_wattage_max/5; //
const double c_workingOK_wattage_min = c_wattage_max/2.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 heatpump_state = 0;
bool hotside_circle_state = 0;
bool hotside_circle_state = 0;
bool coldside_circle_state = 0;
bool coldside_circle_state = 0;
bool crc_heater_state = 0;
bool sump_heater_state = 0;
//bool reg_heater_state = 0;
bool valve4w_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 relay6_state = 0;
bool EEV2_state = 0;
bool relay7_state = 0;
bool EEV3_state = 0;
bool relay8_state = 0;
bool EEV4_state = 0;
bool relay9_state = 0;


const long poweron_pause = POWERON_PAUSE ; //default 5 mins
const long poweron_pause = POWERON_PAUSE ; //default 5 mins
const long mincycle_poweroff = MINCYCLE_POWEROFF; //default 5 mins
const long mincycle_poweroff = MINCYCLE_POWEROFF; //default 5 mins
const long mincycle_poweron = MINCYCLE_POWERON ; //default 60 mins
const long mincycle_poweron = MINCYCLE_POWERON ; //default 60 mins
bool _1st_start_sleeped = 0;
bool _1st_start_sleeped = 0;
//??? TODO: periodical start ?
//??? TODO: periodical start ?
//const long floor_circle_maxhalted = 6000000; //circle NOT works max 100 minutes
//const long floor_circle_maxhalted = 6000000; //circle NOT works max 100 minutes
const long deffered_stop_hotcircle = DEFFERED_STOP_HOTCIRCLE;
const long deffered_stop_hotcircle = DEFFERED_STOP_HOTCIRCLE;


int EEV_cur_pos = 0;
int EEV_cur_pos = 0;
int EEV_reopen_pos = 0;
bool EEV_must_reopen_flag = 0;

int EEV_apulses = 0; //for async
int EEV_apulses = 0; //for async
bool EEV_adonotcare = 0;
bool EEV_adonotcare = 0;
const unsigned char EEV_steps[4] = {0b1010, 0b0110, 0b0101, 0b1001};
const unsigned char EEV_steps[4] = {0b1010, 0b0110, 0b0101, 0b1001};
char EEV_cur_step = 0;
char EEV_cur_step = 0;
bool EEV_fast = 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
//main cycle vars
unsigned long millis_prev = 0;
unsigned long millis_prev = 0;
unsigned long millis_now = 0;
unsigned long millis_now = 0;
unsigned long millis_cycle = 1000;
unsigned long millis_cycle = 1000;


unsigned long millis_last_heatpump_on = 0;
unsigned long millis_last_heatpump_on = 0;
unsigned long millis_last_heatpump_off = 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 = 0;
unsigned long millis_notification_interval = 33000;
unsigned long millis_notification_interval = 33000;


unsigned long millis_displ_update = 0;
unsigned long millis_displ_update = 0;
unsigned long millis_displ_update_interval = 10000;
unsigned long millis_displ_update_interval = 10000;


unsigned long millis_escinput_485 = 0;
unsigned long millis_escinput = 0;
unsigned long millis_charinput_485 = 0;
unsigned long millis_charinput = 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;

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;
int y = 0;
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)
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
int supply_voltage = 0;
int em_i = 0;
//phase 1
int sampleI_1 = 0;
double filteredI_1 = 0;
double offsetI_1 = ADC_COUNTS>>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

//--------------------------- 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) {
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;
/*
//incorrect way: 0.06 % chance for 13 sensors to false positive, calculated for true random.
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;
*/
}

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);
}

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
}

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
}

void Print_D2 () {
#ifdef DISPLAY_1602
lcd.setCursor(0, 1);
lcd.print(outString);
#endif
}




unsigned long millis_laste

void _PrintHelp(void) {
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"));
#ifdef EEV_SUPPORT
PrintS(F("(<) decrease EEV T diff \n(>) increase EEV T diff"));
#endif
PrintS(F("(G) get stats"));
}

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) {
PrintS_and_D(F("Max!"));
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) {
PrintS_and_D(F("Min!"));
delay (200);
return 0;
}
T_setpoint -= 0.5;
PrintS_and_D_double(T_setpoint);
return 1;
}

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;
}

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: " ;