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