mirror of
https://github.com/gonzho000/chpc.git
synced 2025-01-14 20:44:04 +00:00
2438 lines
75 KiB
C++
2438 lines
75 KiB
C++
/*
|
|
Cheap Heat Pump Controller (CHPC) firmware.
|
|
Copyright (C) 2018-2019 Gonzho (gonzho@web.de)
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
See https://github.com/gonzho000/chpc/ for more details
|
|
*/
|
|
|
|
|
|
|
|
//-----------------------USER OPTIONS-----------------------
|
|
#define BOARD_TYPE_G //Type "G", PCB from github.com/gonzho000/chpc/
|
|
//#define BOARD_TYPE_F //Type "F"
|
|
//#define BOARD_TYPE_G9 //Type "G9" or "G-MAX", current testing
|
|
|
|
//#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
|
|
|
|
//#define RS485_PYTHON 1
|
|
#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
|
|
|
|
#define WATCHDOG //only if u know what to do
|
|
|
|
//-----------------------TEMPERATURES-----------------------
|
|
#define T_SETPOINT_MAX 45.0; //defines max temperature that ordinary user can set
|
|
#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_SUMP_MIN 9.0; //HP will not start if T lower
|
|
#define T_SUMP_MAX 110.0; //HP will stop if T higher
|
|
#define T_SUMP_HEAT_THRESHOLD 16.0; //sump heater will be powered on if T lower
|
|
#define T_BEFORE_CONDENSER_MAX 108.0; //discharge MAX, system stops if discharge higher
|
|
#define T_AFTER_EVAPORATOR_MIN -7.0; //suction MIN, HP stops if lower, anti-freeze and anti-liquid at suction protection
|
|
#define T_COLD_MIN -8.0; //cold loop anti-freeze: stop if inlet or outlet temperature lower
|
|
#define T_HOTOUT_MAX 50.0; //hot loop: stop if outlet temperature higher than this
|
|
#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
|
|
|
|
//-----------------------TUNING OPTIONS -----------------------
|
|
#define MAX_WATTS 1170.0 //user for power protection
|
|
|
|
#define DEFFERED_STOP_HOTCIRCLE 3000000 //50 mins
|
|
|
|
#define POWERON_PAUSE 300000 //5 mins
|
|
#define MINCYCLE_POWEROFF 300000 //5 mins
|
|
#define MINCYCLE_POWERON 3600000 //60 mins
|
|
#define POWERON_HIGHTIME 10000 //10 sec, defines time after start when power consumption can be 2 times greater than normal
|
|
|
|
//EEV
|
|
#define EEV_MAXPULSES 480
|
|
|
|
#define EEV_PULSE_FCLOSE_MILLIS 20 //fast close, set waiting pos., close on danger
|
|
#define EEV_PULSE_CLOSE_MILLIS 50000 //precise close
|
|
#define EEV_PULSE_WOPEN_MILLIS 20 //waiting pos. set
|
|
#define EEV_PULSE_FOPEN_MILLIS 1300 //fast open, fast search
|
|
#define EEV_PULSE_OPEN_MILLIS 60000 //precise open
|
|
|
|
#define EEV_STOP_HOLD 500 //0.1..1sec for Sanhua
|
|
#define EEV_CLOSE_ADD_PULSES 8 //read below, close algo
|
|
#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)
|
|
//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
|
|
|
|
#define MAGIC 0x55 //change if u want to reinit T sensors
|
|
//-----------------------USER OPTIONS END -----------------------
|
|
|
|
//#define INPUTS_AS_INPUTS 2 //
|
|
//#define RS485_MACHINE 3 //?? or part of Python?
|
|
|
|
//-----------------------changelog-----------------------
|
|
/*
|
|
v1.0:
|
|
- Displays support
|
|
- define TYPE F/G and rearrange ports
|
|
- multi-DS18b20 support on lane
|
|
- skip non-important DS18B20 during init
|
|
- rewrite Main Cycle to unification: some sensors can be absent, ex: T_hot_out can be absent because i'ts used as target
|
|
- 2 on-board buttons support: +/- aim
|
|
- DISPLAY: indication: real and aim
|
|
- RS485_HUMAN: remote commands +,-,G,0x20/?/Enter
|
|
- buttons: < > increase_decrease t
|
|
- simpliest thermostat scheme: only T target
|
|
- rename all procs
|
|
- RS485_PYTHON: print to console inspite of mode diring init proc
|
|
- faster wattage overload processing
|
|
- write aim value to EE if needed, period: 15 mins (eq. 1041 days)
|
|
- deferred stop of hot side circle
|
|
- 80 microseconds at 9600
|
|
|
|
v1.1, 15 Apr 2019:
|
|
- HUMAN_AUTOINFO time
|
|
- EEV_ONLY mode
|
|
- EEV_Support
|
|
- EEV auto poweron/poweroff every 10 sec
|
|
- EEV_recalibration_time to stop HP and recalibrate EEV from zero level ex: every 24 hours
|
|
|
|
v1.2, 16 Apr 2019:
|
|
- "Type F" support
|
|
|
|
v1.3, 30 Apr 2019:
|
|
- EEV changed "overheating" to "delta T"
|
|
- EEV algo v1.1
|
|
|
|
v1.4, 02 Jun 2019:
|
|
- minor fixes
|
|
- EEV more asyncy
|
|
- T options to header
|
|
|
|
v1.5, 01 Jul 2019:
|
|
- prototyping 9
|
|
|
|
v1.6, 30 Apr 2021:
|
|
- sensors init issue fix
|
|
|
|
//TODO:
|
|
- 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)
|
|
- Dev and Host ID to header
|
|
- add speaker and err code for ""ERR: no Tae or Tbe for EEV!""
|
|
- min_user_t/max_user_t to header
|
|
- rs485_modbus
|
|
- full relays halification
|
|
? wclose and fclose to EEV
|
|
- liquid ref. protection: start cold circle and sump heater if tsump =< tco/tci+1, add option to header
|
|
- periodical start of hot side circle
|
|
- valve_4way
|
|
- 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-----------------------
|
|
|
|
// DS18B20 pins: GND DATA VDD
|
|
|
|
//Connections:
|
|
//DS18B20 Pinout (Left to Right, pins down, flat side toward you)
|
|
//- Left = Ground
|
|
//- Center = Signal (Pin N of arduino): (with 3.3K to 4.7K resistor to +5 or 3.3 )
|
|
//- Right = +5 or +3.3 V
|
|
//
|
|
|
|
|
|
//
|
|
// high volume scheme: +---- +5V (12V not tested)
|
|
// |
|
|
// +----+
|
|
// 1MOhm piezo
|
|
// +----+
|
|
// |(C)
|
|
// pin -> 1.6 kOhms -> (B) 2n2222 < front here
|
|
// |(E)
|
|
// +--- GND
|
|
//
|
|
|
|
/*
|
|
scheme SCT-013-000:
|
|
|
|
2 pins used: tip and sleeve, center (ring) not used http://cms.35g.tw/coding/wp-content/uploads/2014/09/SCT-013-000_UNO-1.jpg
|
|
pins are interchangeable due to AC
|
|
|
|
32 Ohms (22+10) between sensor pins (35 == ideal)
|
|
|
|
Pin1:
|
|
- via elect. cap. to GND
|
|
- via ~10K..470K resistor to GND
|
|
- via ~10K..470K resistor to +5 (same as prev.)
|
|
if 10K+10K used: current is 25mA
|
|
use 100K+100K for 3 phases
|
|
|
|
Pin2:
|
|
- to analog pin
|
|
- via 32..35 Ohms resistor to Pin1
|
|
|
|
+5 -------------------------+
|
|
|
|
|
|
|
|
# R1 10K+
|
|
|
|
|
|
|
|
|~2.5 at this point
|
|
+---------------+--------------------------------------+----+
|
|
| | | |
|
|
#_ elect. cap. # R2 10K+ (same as R1) SCT-013-000 $ # R3 = 35 Ohms (ideal case), 32 used
|
|
| | | |
|
|
GND --------+---------------+ +----+--------> to Analog pin
|
|
|
|
|
|
WARNING: calibrate 3 sensors together, from different sellers, due to case of incorrectly worked 1 of 3 sensor
|
|
|
|
P(watts)=220*220/R(Ohms)
|
|
*/
|
|
|
|
//
|
|
//MAX 485 voltage - 5V
|
|
//
|
|
// use resistor at RS-485 GND
|
|
// 1st test: 10k result lot of issues
|
|
// 2nd test: 1k, issues
|
|
// 3rd test: 100, see discussions
|
|
|
|
|
|
//16-ch Multiplexer EN pin: active LOW, connect to GND
|
|
|
|
//used pins:
|
|
//!!! ACTUALISE
|
|
//2: Z
|
|
//3: S3
|
|
//4: S2
|
|
//5: S1
|
|
//6: S0
|
|
//7: relay 2
|
|
//8: relay 3
|
|
//9: speaker
|
|
//10: relay 4
|
|
//11-13: rs485
|
|
//A0: relay 1
|
|
//A1: power monitor
|
|
|
|
/*
|
|
relay 1: heat pump
|
|
relay 2: hot side pump
|
|
relay 3: cold side pump
|
|
relay 4: (future) heatpump sump heater
|
|
|
|
t0: room
|
|
t1: heatpump sump
|
|
t2: cold in
|
|
t3: cold out
|
|
t4: hot in
|
|
t5: hot out
|
|
t6: before condenser
|
|
t7: condenser-evaporator
|
|
t8: after evaporator
|
|
t9: outer
|
|
tA: warm floor
|
|
|
|
wattage1
|
|
|
|
*/
|
|
|
|
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
|
|
#define OW_BUS_ALLTSENSORS 12
|
|
#define SerialTxControl 13 //RS485 Direction control DE and RE to this pin
|
|
#define speakerOut 6
|
|
#define em_pin1 A6
|
|
#define EMERGENCY_PIN A7
|
|
|
|
|
|
|
|
#ifdef BOARD_TYPE_G
|
|
String hw_version = "Type G v1.x";
|
|
#define RELAY_HEATPUMP 8
|
|
#define RELAY_HOTSIDE_CIRCLE 9
|
|
#define RELAY_COLDSIDE_CIRCLE 7
|
|
#define RELAY_SUMP_HEATER 10
|
|
#define RELAY_4WAY_VALVE 11
|
|
#ifdef INPUTS_AS_BUTTONS
|
|
#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
|
|
#ifdef __arm__
|
|
// should use uinstd.h to define sbrk but Due causes a conflict
|
|
extern "C" char* sbrk(int incr);
|
|
#else // __ARM__
|
|
extern char *__brkval;
|
|
#endif // __arm__
|
|
|
|
int freeMemory() {
|
|
char top;
|
|
#ifdef __arm__
|
|
return &top - reinterpret_cast<char*>(sbrk(0));
|
|
#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
|
|
return &top - __brkval;
|
|
#else // __arm__
|
|
return __brkval ? &top - __brkval : &top - __malloc_heap_start;
|
|
#endif // __arm__
|
|
}
|
|
//---------------------------memory debug END
|
|
|
|
|
|
#include <avr/wdt.h>
|
|
#include <EEPROM.h>
|
|
//#include <FastCRC.h>
|
|
/*FastCRC16 CRC16;
|
|
union _crc {
|
|
unsigned int integer;
|
|
char bytes[2];
|
|
} crc;
|
|
*/
|
|
|
|
#include <SoftwareSerial.h>
|
|
|
|
#define SerialRX 0 //RX connected to RO - Receiver Output
|
|
#define SerialTX 1 //TX connected to DI - Driver Output Pin
|
|
#define RS485Transmit HIGH
|
|
#define RS485Receive LOW
|
|
|
|
const char devID = 0x41;
|
|
const char hostID = 0x30;
|
|
|
|
SoftwareSerial RS485Serial(SerialRX, SerialTX); // RX, TX
|
|
|
|
#include <OneWire.h>
|
|
#include <DallasTemperature.h>
|
|
//library's DEVICE_DISCONNECTED_C -127.0
|
|
|
|
OneWire ow_ALLTSENSORS(OW_BUS_ALLTSENSORS);
|
|
DallasTemperature s_allTsensors(&ow_ALLTSENSORS);
|
|
|
|
typedef struct {
|
|
DeviceAddress addr;
|
|
bool e; //enabled
|
|
double T;
|
|
} st_tsens;
|
|
|
|
DeviceAddress dev_addr; //temp
|
|
|
|
st_tsens Tae ;
|
|
st_tsens Tbe ;
|
|
st_tsens Ttarget;
|
|
st_tsens Tsump ;
|
|
st_tsens Tci ;
|
|
st_tsens Tco ;
|
|
st_tsens Thi ;
|
|
st_tsens Tho ;
|
|
st_tsens Tbc ;
|
|
st_tsens Tac ;
|
|
st_tsens Touter ;
|
|
st_tsens Ts1 ;
|
|
st_tsens Ts2 ;
|
|
|
|
#define BIT_Tae 0
|
|
#define BIT_Tbe 1
|
|
#define BIT_Ttarget 2
|
|
#define BIT_Tsump 3
|
|
#define BIT_Tci 4
|
|
#define BIT_Tco 5
|
|
#define BIT_Thi 6
|
|
#define BIT_Tho 7
|
|
#define BIT_Tbc 8
|
|
#define BIT_Tac 9
|
|
#define BIT_Touter 10
|
|
#define BIT_Ts1 11
|
|
#define BIT_Ts2 12
|
|
|
|
unsigned int used_sensors = 0 ; //bit array
|
|
|
|
double T_setpoint = 26.5;
|
|
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_hotcircle_delta_min = T_HOTCIRCLE_DELTA_MIN;
|
|
const double cT_sump_min = T_SUMP_MIN;
|
|
const double cT_sump_max = T_SUMP_MAX;
|
|
const double cT_sump_heat_threshold = T_SUMP_HEAT_THRESHOLD;
|
|
//const double cT_sump_outerT_threshold = 18.0; //?? seems to be not useful
|
|
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_cold_min = T_COLD_MIN;
|
|
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_hot_delta_min = 0.5;
|
|
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
|
|
//PH165X1CY : 920 Watts, 4.2 A
|
|
const double c_workingOK_wattage_min = c_wattage_max/2.5; //
|
|
|
|
bool heatpump_state = 0;
|
|
bool hotside_circle_state = 0;
|
|
bool coldside_circle_state = 0;
|
|
bool sump_heater_state = 0;
|
|
bool valve4w_state = 0;
|
|
|
|
bool relay6_state = 0;
|
|
bool relay7_state = 0;
|
|
bool relay8_state = 0;
|
|
bool relay9_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_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;
|
|
|
|
//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_notification = 0;
|
|
unsigned long millis_notification_interval = 33000;
|
|
|
|
unsigned long millis_displ_update = 0;
|
|
unsigned long millis_displ_update_interval = 10000;
|
|
|
|
unsigned long millis_escinput = 0;
|
|
unsigned long millis_charinput = 0;
|
|
|
|
unsigned long millis_lasteesave = 0;
|
|
|
|
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
|
|
}
|
|
|
|
|
|
|
|
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: " ; print_Serial_SaD(Tci.T); }
|
|
if (Tco.e == 1) {outString = "Tco: " ; print_Serial_SaD(Tco.T); }
|
|
if (Thi.e == 1) {outString = "Thi: " ; print_Serial_SaD(Thi.T); }
|
|
if (Tho.e == 1) {outString = "Tho: " ; print_Serial_SaD(Tho.T); }
|
|
if (Tbc.e == 1) {outString = "Tbc: " ; print_Serial_SaD(Tbc.T); }
|
|
if (Tac.e == 1) {outString = "Tac: " ; print_Serial_SaD(Tac.T); }
|
|
if (Touter.e == 1) {outString = "Touter: " ; print_Serial_SaD(Touter.T); }
|
|
if (Ts1.e == 1) {outString = "Ts1: " ; print_Serial_SaD(Ts1.T); }
|
|
if (Ts2.e == 1) {outString = "Ts2: " ; print_Serial_SaD(Ts2.T); }
|
|
outString = "Err: " + String(errorcode) + "\n\rWatts:" + String(async_wattage) + "\n\rAim: "; print_Serial_SaD(T_setpoint);
|
|
#ifdef EEV_SUPPORT
|
|
outString = "EEV_pos:" + String (EEV_cur_pos);
|
|
RS485Serial.print(outString);
|
|
#endif
|
|
RS485Serial.println();
|
|
RS485Serial.flush();
|
|
digitalWrite(SerialTxControl, RS485Receive);
|
|
#endif
|
|
}
|
|
|
|
void ReadEECheckAddr(unsigned char *to_addr) {
|
|
for (i=0 ; i<8 ; i++) {
|
|
to_addr[i] = EEPROM.read(eeprom_addr);
|
|
eeprom_addr++;
|
|
}
|
|
i = 0;
|
|
CheckIsInvalidCRCAddr(to_addr);
|
|
if (i != 0) {
|
|
while (1) {
|
|
//PrintAddr(to_addr);
|
|
PrintS_and_D(F("Err:EEPROM, reinit!"));
|
|
delay(5000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckIsInvalidCRCAddr(unsigned char *addr) {
|
|
if (OneWire::crc8( addr, 7) != addr[7] ) {
|
|
i+= 1;
|
|
}
|
|
}
|
|
|
|
void CopyAddrStoreEE(unsigned char *addr_to, int bit_offset) { //get result from dev_addr, autoincrement eeprom_addr
|
|
//dev_addr and z from globals used
|
|
for (i=0 ; i<8 ; i++) { //no matter
|
|
if (z == 0) {
|
|
dev_addr[i] = 0x00;
|
|
}
|
|
addr_to[i] = dev_addr[i];
|
|
EEPROM.write(eeprom_addr, dev_addr[i]);
|
|
eeprom_addr++;
|
|
}
|
|
bitWrite(used_sensors, bit_offset, z);
|
|
}
|
|
|
|
void WriteFloatEEPROM(int addr, float val) {
|
|
byte *x = (byte *)&val;
|
|
for(byte u = 0; u < 4; u++) EEPROM.write(u+addr, x[u]);
|
|
}
|
|
|
|
float ReadFloatEEPROM(int addr) {
|
|
byte x[4];
|
|
for(byte u = 0; u < 4; u++) x[u] = EEPROM.read(u+addr);
|
|
float *y = (float *)&x;
|
|
return y[0];
|
|
}
|
|
|
|
void SaveSetpointEE(void) {
|
|
if( (T_setpoint_lastsaved != T_setpoint) &&
|
|
( ((unsigned long)(millis_now - millis_lasteesave) > 15*60*1000 ) || (millis_lasteesave == 0) ) ) {
|
|
eeprom_addr = 1;
|
|
WriteFloatEEPROM(eeprom_addr, T_setpoint);
|
|
millis_lasteesave = millis_now;
|
|
T_setpoint_lastsaved = T_setpoint;
|
|
}
|
|
}
|
|
|
|
void PrintAddr(unsigned char *str) {
|
|
outString = "";
|
|
for (i = 0; i < 8; i++) {
|
|
if (str[i] < 0x10) outString += "0";
|
|
outString += String(str[i], HEX);
|
|
}
|
|
PrintS_and_D(outString);
|
|
}
|
|
|
|
unsigned char FindAddr(String what, int required = 0) {
|
|
i = 1;
|
|
while (RS485Serial.available() > 0) {
|
|
inChar = RS485Serial.read();
|
|
delay(1);
|
|
}
|
|
inChar = 0x00;
|
|
while (1) {
|
|
while (!s_allTsensors.getAddress(dev_addr, 0)) {
|
|
if (required == 0) {
|
|
PrintS_and_D(F("Press > to skip"));
|
|
delay(500);
|
|
while (RS485Serial.available() > 0) {
|
|
inChar = RS485Serial.read();
|
|
if (inChar == 0x3E) {
|
|
PrintS_and_D("Skipped: " + what);
|
|
return 0;
|
|
}
|
|
}
|
|
#ifdef INPUTS_AS_BUTTONS
|
|
i = digitalRead(BUT_RIGHT);
|
|
if (i == 1) {
|
|
PrintS_and_D("Skipped: " + what);
|
|
delay(4000);
|
|
return 0;
|
|
}
|
|
#endif
|
|
}
|
|
PrintS_and_D("Insert " + what);
|
|
delay(1000);
|
|
}
|
|
if ( OneWire::crc8( dev_addr, 7) != dev_addr[7]) {
|
|
PrintS_and_D(F("Invalid CRC! Remove and insert same sensor!\n"));
|
|
delay(200);
|
|
continue;
|
|
} else if (CheckAddrExists() == 1) {
|
|
PrintS_and_D(F("USED! Remove!"));
|
|
delay(1000);
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
while (1) {
|
|
PrintAddr(dev_addr);
|
|
delay(1000);
|
|
if (s_allTsensors.getAddress(dev_addr, 0)) {
|
|
PrintS_and_D("OK! Remove " + what);
|
|
delay(1000);
|
|
} else {
|
|
delay(100);
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
double GetT (unsigned char *str) {
|
|
tempdouble = -127.0;
|
|
for ( i = 0; i < 8; i++) {
|
|
#ifdef WATCHDOG
|
|
wdt_reset();
|
|
#endif
|
|
#ifdef EEV_SUPPORT
|
|
eevise();
|
|
#endif
|
|
tempdouble = s_allTsensors.getTempC(str);
|
|
if ( (tempdouble == 85.0) || (tempdouble == -127.0) ) {
|
|
if ( tempdouble == 85.0 ) { //initial value in dallas register after poweron
|
|
delay (375); //375 actual for 11 bits resolution, 2-3 retries OK for 12-bits resolution
|
|
} else {
|
|
delay (37);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return tempdouble;
|
|
}
|
|
|
|
void Get_Temperatures(void) {
|
|
if (Tae.e) Tae.T = GetT(Tae.addr);
|
|
if (Tbe.e) Tbe.T = GetT(Tbe.addr);
|
|
if (Ttarget.e) Ttarget.T = GetT(Ttarget.addr);
|
|
if (Tsump.e) Tsump.T = GetT(Tsump.addr);
|
|
if (Tci.e) Tci.T = GetT(Tci.addr);
|
|
if (Tco.e) Tco.T = GetT(Tco.addr);
|
|
if (Thi.e) Thi.T = GetT(Thi.addr);
|
|
if (Tho.e) Tho.T = GetT(Tho.addr);
|
|
if (Tbc.e) Tbc.T = GetT(Tbc.addr);
|
|
if (Tac.e) Tac.T = GetT(Tac.addr);
|
|
if (Touter.e) Touter.T = GetT(Touter.addr);
|
|
if (Ts1.e) Ts1.T = GetT(Ts1.addr);
|
|
if (Ts2.e) Ts2.T = GetT(Ts2.addr);
|
|
s_allTsensors.requestTemperatures(); //global request
|
|
//---------DEBUG and self-test !!!--------
|
|
/*PrintS_and_D("");
|
|
PrintS_and_D_double(Tae.T);
|
|
PrintS_and_D_double(Tbe.T);
|
|
PrintS_and_D_double(Ttarget.T);
|
|
PrintS_and_D_double(Tsump.T);
|
|
PrintS_and_D("");*/
|
|
/*
|
|
PrintS_and_D("Sensor 1 ");
|
|
PrintS_and_D_double(tr_sens_1);
|
|
PrintS_and_D(",\tci ");
|
|
PrintS_and_D_double(tr_cold_in);
|
|
PrintS_and_D(",\tcout ");
|
|
PrintS_and_D_double(tr_cold_out);
|
|
PrintS_and_D(",\thin ");
|
|
PrintS_and_D_double(tr_hot_in);
|
|
PrintS_and_D(",\tho ");
|
|
PrintS_and_D_double(tr_hot_out);
|
|
PrintS_and_D(",\tbcond ");
|
|
PrintS_and_D_double(tr_before_condenser);
|
|
PrintS_and_D(",\t outer ");
|
|
PrintS_and_D_double(tr_outer);
|
|
PrintS_and_D(",\t Sensor 2 ");
|
|
PrintS_and_D_double(tr_sens_2);
|
|
*/
|
|
|
|
//---------DEBUG END--------
|
|
}
|
|
|
|
#ifdef EEV_SUPPORT
|
|
void on_EEV(){ //1 = do not take care of position
|
|
x = EEV_steps[EEV_cur_step];
|
|
digitalWrite (EEV_1, bitRead(x, 0));
|
|
digitalWrite (EEV_2, bitRead(x, 1));
|
|
digitalWrite (EEV_3, bitRead(x, 2));
|
|
digitalWrite (EEV_4, bitRead(x, 3));
|
|
}
|
|
|
|
void off_EEV(){ //1 = do not take care of position
|
|
digitalWrite (EEV_1, 0);
|
|
digitalWrite (EEV_2, 0);
|
|
digitalWrite (EEV_3, 0);
|
|
digitalWrite (EEV_4, 0);
|
|
//PrintS_and_D("off_EEV");
|
|
}
|
|
|
|
#endif
|
|
|
|
void halifise(void){
|
|
#ifdef BOARD_TYPE_F
|
|
/*#define LATCH_595 = 10;
|
|
#define CLK_595 = 11;
|
|
#DEFINE DATA_595 = 9;
|
|
//595.0: relay 3 RELAY_HOTSIDE_CIRCLE, 595.1: relay 4 RELAY_SUMP_HEATER, 595.2: relay 5 RELAY_4WAY_VALVE, 595.3: uln 6, 595.4: uln 7, 595.5: uln 8, 595.6: uln 9, 595.7: uln 10
|
|
*/
|
|
digitalWrite(LATCH_595, 0);
|
|
//7
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//6
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//5
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//4
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//3
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//2
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0); //4way valve here
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//1
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, sump_heater_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//0
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, hotside_circle_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(CLK_595, 0);
|
|
//
|
|
digitalWrite(LATCH_595, 1);
|
|
|
|
digitalWrite (RELAY_HEATPUMP, heatpump_state);
|
|
digitalWrite (RELAY_COLDSIDE_CIRCLE, coldside_circle_state);
|
|
#endif
|
|
#ifdef BOARD_TYPE_G
|
|
digitalWrite (RELAY_SUMP_HEATER, sump_heater_state);
|
|
digitalWrite (RELAY_HOTSIDE_CIRCLE, hotside_circle_state);
|
|
digitalWrite (RELAY_HEATPUMP, heatpump_state);
|
|
digitalWrite (RELAY_COLDSIDE_CIRCLE, coldside_circle_state);
|
|
#endif
|
|
#ifdef BOARD_TYPE_G9
|
|
//#define RELAY_4WAY_VALVE 8
|
|
//#define RELAY_SUMP_HEATER 7
|
|
/*
|
|
595.0: relay 10(not used)
|
|
595.1: relay 8 //use for 1st test of DAC
|
|
595.2: relay 9 //use for 1st test of DAC
|
|
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
|
|
*/
|
|
|
|
digitalWrite(LATCH_595, 0);
|
|
//7
|
|
digitalWrite(CLK_595, 0);
|
|
digitalWrite(DATA_595, relay7_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//6
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, relay6_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//5
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, hotside_circle_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//4
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, coldside_circle_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//3
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, heatpump_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//2
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, relay9_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//1
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, relay8_state);
|
|
digitalWrite(CLK_595, 1);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
//0
|
|
digitalWrite(CLK_595, 0);
|
|
__asm__ __volatile__ ("nop\n\t");
|
|
digitalWrite(DATA_595, 0);
|
|
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_SUMP_HEATER, sump_heater_state);
|
|
digitalWrite (RELAY_4WAY_VALVE, valve4w_state);
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
//PrintS_and_D("EEmax!");
|
|
}
|
|
}
|
|
if ( EEV_apulses < 0 ) {
|
|
if ( (EEV_cur_pos - 1 >= EEV_MINWORKPOS) || (EEV_adonotcare == 1) ) {
|
|
EEV_cur_pos -= 1;
|
|
EEV_cur_step -= 1;
|
|
EEV_apulses += 1;
|
|
} else {
|
|
EEV_apulses = 0;
|
|
//PrintS_and_D("EEmin!");
|
|
}
|
|
}
|
|
if (EEV_cur_step > 3) EEV_cur_step = 0;
|
|
if (EEV_cur_step < 0) EEV_cur_step = 3;
|
|
x = EEV_steps[EEV_cur_step];
|
|
digitalWrite (EEV_1, bitRead(x, 0));
|
|
digitalWrite (EEV_2, bitRead(x, 1));
|
|
digitalWrite (EEV_3, bitRead(x, 2));
|
|
digitalWrite (EEV_4, bitRead(x, 3));
|
|
}
|
|
if (EEV_cur_pos < 0) {
|
|
EEV_cur_pos = 0;
|
|
}
|
|
millis_eev_last_step = millis_now;
|
|
#ifdef EEV_DEBUG
|
|
PrintS(String(EEV_cur_pos));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//--------------------------- functions END
|
|
|
|
void setup(void) {
|
|
|
|
#ifdef BOARD_TYPE_G
|
|
pinMode (RELAY_HEATPUMP, OUTPUT);
|
|
pinMode (RELAY_COLDSIDE_CIRCLE, OUTPUT);
|
|
digitalWrite (RELAY_HEATPUMP, LOW);
|
|
digitalWrite (RELAY_COLDSIDE_CIRCLE, LOW);
|
|
//
|
|
pinMode (RELAY_SUMP_HEATER, OUTPUT);
|
|
pinMode (RELAY_HOTSIDE_CIRCLE, OUTPUT);
|
|
digitalWrite (RELAY_SUMP_HEATER, LOW);
|
|
digitalWrite (RELAY_HOTSIDE_CIRCLE, LOW);
|
|
halifise();
|
|
#endif
|
|
#ifdef BOARD_TYPE_F
|
|
pinMode (RELAY_HEATPUMP, OUTPUT);
|
|
pinMode (RELAY_COLDSIDE_CIRCLE, OUTPUT);
|
|
digitalWrite (RELAY_HEATPUMP, LOW);
|
|
digitalWrite (RELAY_COLDSIDE_CIRCLE, LOW);
|
|
//
|
|
pinMode (LATCH_595, OUTPUT);
|
|
pinMode (CLK_595, OUTPUT);
|
|
pinMode (DATA_595, OUTPUT);
|
|
digitalWrite (LATCH_595, LOW);
|
|
digitalWrite (CLK_595, LOW);
|
|
digitalWrite (DATA_595, LOW);
|
|
halifise();
|
|
#endif
|
|
#ifdef BOARD_TYPE_G9
|
|
pinMode (LATCH_595, OUTPUT);
|
|
pinMode (CLK_595, OUTPUT);
|
|
pinMode (DATA_595, OUTPUT);
|
|
pinMode (RELAY_SUMP_HEATER, OUTPUT);
|
|
pinMode (RELAY_4WAY_VALVE, OUTPUT);
|
|
pinMode (OE_595, OUTPUT);
|
|
digitalWrite (LATCH_595, LOW);
|
|
digitalWrite (CLK_595, LOW);
|
|
digitalWrite (DATA_595, LOW);
|
|
digitalWrite (RELAY_SUMP_HEATER, LOW);
|
|
digitalWrite (RELAY_4WAY_VALVE, LOW);
|
|
halifise();
|
|
digitalWrite (OE_595, LOW);
|
|
#endif
|
|
|
|
#ifdef WATCHDOG
|
|
wdt_disable();
|
|
delay(2000);
|
|
#endif
|
|
InitS_and_D();
|
|
pinMode(SerialTxControl, OUTPUT);
|
|
digitalWrite(SerialTxControl, RS485Receive);
|
|
//digitalWrite(SerialTxControl, RS485Transmit);
|
|
//RS485Serial.println("starting..."); //!!!debug
|
|
delay(100);
|
|
PrintS_and_D("ID: 0x" + String(devID, HEX));
|
|
//Print_Lomem(C_ID);
|
|
delay(200);
|
|
#ifdef EEV_SUPPORT
|
|
pinMode (EEV_1, OUTPUT);
|
|
pinMode (EEV_2, OUTPUT);
|
|
pinMode (EEV_3, OUTPUT);
|
|
pinMode (EEV_4, OUTPUT);
|
|
off_EEV();
|
|
#endif
|
|
|
|
pinMode (em_pin1, INPUT);
|
|
|
|
//PrintS_and_D("setpoint (C):");
|
|
//PrintS_and_D(setpoint);
|
|
|
|
//PrintS_and_D(String(freeMemory())); //!!! debug
|
|
|
|
s_allTsensors.begin();
|
|
s_allTsensors.setWaitForConversion(false); //ASYNC mode, request before get, see Dallas library for details
|
|
|
|
|
|
//----------------------------- self-tests !!!----------------------------- ----------------------------- -----------------------------
|
|
/*
|
|
digitalWrite(RELAY_HEATPUMP,HIGH);
|
|
delay(300);
|
|
digitalWrite(RELAY_HOTSIDE_CIRCLE,HIGH);
|
|
delay(300);
|
|
digitalWrite(RELAY_COLDSIDE_CIRCLE,HIGH);
|
|
delay(300);
|
|
digitalWrite(RELAY_SUMP_HEATER,HIGH);
|
|
delay(2000);
|
|
digitalWrite(RELAY_HEATPUMP,LOW);
|
|
delay(300);
|
|
digitalWrite(RELAY_HOTSIDE_CIRCLE,LOW);
|
|
delay(300);
|
|
digitalWrite(RELAY_COLDSIDE_CIRCLE,LOW);
|
|
delay(300);
|
|
digitalWrite(RELAY_SUMP_HEATER,LOW);
|
|
*/
|
|
/*
|
|
tone(speakerOut, 2250);
|
|
delay (500); // like ups power on
|
|
noTone(speakerOut);
|
|
|
|
|
|
|
|
while ( 1 == 1) {
|
|
|
|
heatpump_state = 1; halifise(); delay(1000);
|
|
coldside_circle_state = 1; halifise(); delay(1000);
|
|
hotside_circle_state = 1; halifise(); delay(1000);
|
|
sump_heater_state = 1; halifise(); delay(1000);
|
|
valve4w_state = 1; halifise(); delay(1000);
|
|
#ifdef BOARD_TYPE_G9
|
|
relay6_state = 1; halifise(); delay(1000);
|
|
relay7_state = 1; halifise(); delay(1000);
|
|
relay8_state = 1; halifise(); delay(1000);
|
|
relay9_state = 1; halifise(); delay(1000);
|
|
#endif
|
|
break;
|
|
|
|
delay(3000);
|
|
heatpump_state = 0; halifise(); delay(1000);
|
|
coldside_circle_state = 0; halifise(); delay(1000);
|
|
hotside_circle_state = 0; halifise(); delay(1000);
|
|
sump_heater_state = 0; halifise(); delay(1000);
|
|
valve4w_state = 0; halifise(); delay(1000);
|
|
#ifdef BOARD_TYPE_G9
|
|
relay6_state = 0; halifise(); delay(1000);
|
|
relay7_state = 0; halifise(); delay(1000);
|
|
relay8_state = 0; halifise(); delay(1000);
|
|
relay9_state = 0; halifise(); delay(1000);
|
|
#endif
|
|
delay(3000);
|
|
|
|
}
|
|
//EEV self-test
|
|
while ( 1 == 1 ) {
|
|
EEV_apulses = -(EEV_MAXPULSES + EEV_CLOSE_ADD_PULSES);
|
|
EEV_adonotcare = 1;
|
|
EEV_fast = 1;
|
|
while (EEV_apulses < 0){
|
|
millis_now = millis();
|
|
eevise();
|
|
}
|
|
//delay(1000);
|
|
EEV_apulses = EEV_MAXPULSES;
|
|
EEV_fast = 1;
|
|
while (EEV_apulses > 0){
|
|
millis_now = millis();
|
|
eevise();
|
|
}
|
|
//delay(1000);
|
|
}
|
|
*/
|
|
//----------------------------- self-test END----------------------------- ----------------------------- -----------------------------
|
|
|
|
|
|
eeprom_magic_read = EEPROM.read(eeprom_addr);
|
|
#ifdef INPUTS_AS_BUTTONS
|
|
pinMode (BUT_RIGHT, INPUT);
|
|
//digitalWrite (BUT_RIGHT, LOW);
|
|
pinMode (BUT_LEFT, INPUT);
|
|
//digitalWrite (BUT_LEFT, LOW);
|
|
#endif
|
|
//EEPROM content:
|
|
//0x00 - magic,
|
|
//0x01 .. 0x04 Target value,
|
|
//0x05 and 0x06 if sensor enabled or not, used_sensors HI and LO
|
|
//0x07 .. 0x0e 1st addr, etc..
|
|
|
|
// tr_after_evaporator(0); tr_before_evaporator(1); tr_target(2); tr_sump(3);
|
|
// tr_cold_in(4); tr_cold_out(5); tr_hot_in(6); tr_hot_out(7);
|
|
// tr_before_condenser(8); tr_after_condenser(9); tr_outer(10); tr_sens_1(11);
|
|
// tr_sens_2(12);
|
|
|
|
eeprom_addr = 0x00;
|
|
if (eeprom_magic_read == eeprom_magic) {
|
|
eeprom_addr += 1;
|
|
T_setpoint = ReadFloatEEPROM(eeprom_addr);
|
|
eeprom_addr += 4;
|
|
//PrintS_and_D("EEPROM->T " + String(T_setpoint));
|
|
|
|
z = EEPROM.read(eeprom_addr); //high
|
|
eeprom_addr += 1;
|
|
i = EEPROM.read(eeprom_addr); //lo
|
|
eeprom_addr += 1;
|
|
used_sensors= word (z,i);
|
|
|
|
Tae.e = bitRead(used_sensors, BIT_Tae );
|
|
Tbe.e = bitRead(used_sensors, BIT_Tbe );
|
|
Ttarget.e = bitRead(used_sensors, BIT_Ttarget );
|
|
Tsump.e = bitRead(used_sensors, BIT_Tsump );
|
|
Tci.e = bitRead(used_sensors, BIT_Tci );
|
|
Tco.e = bitRead(used_sensors, BIT_Tco );
|
|
Thi.e = bitRead(used_sensors, BIT_Thi );
|
|
Tho.e = bitRead(used_sensors, BIT_Tho );
|
|
Tbc.e = bitRead(used_sensors, BIT_Tbc );
|
|
Tac.e = bitRead(used_sensors, BIT_Tac );
|
|
Touter.e = bitRead(used_sensors, BIT_Touter );
|
|
Ts1.e = bitRead(used_sensors, BIT_Ts1 );
|
|
Ts2.e = bitRead(used_sensors, BIT_Ts2 );
|
|
#ifdef EEV_SUPPORT
|
|
if (Tae.e != 1 || Tbe.e != 1) {
|
|
while (1) {
|
|
PrintS_and_D("ERR: no Tae or Tbe for EEV!");
|
|
delay (1000);
|
|
}
|
|
}
|
|
#endif
|
|
ReadEECheckAddr(Tae.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tae");
|
|
ReadEECheckAddr(Tbe.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tbe");
|
|
ReadEECheckAddr(Ttarget.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Ttarget");
|
|
ReadEECheckAddr(Tsump.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tsump");
|
|
ReadEECheckAddr(Tci.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tci");
|
|
ReadEECheckAddr(Tco.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tco");
|
|
ReadEECheckAddr(Thi.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Thi");
|
|
ReadEECheckAddr(Tho.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tho");
|
|
ReadEECheckAddr(Tbc.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tbc");
|
|
ReadEECheckAddr(Tac.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Tac");
|
|
ReadEECheckAddr(Touter.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Touter");
|
|
ReadEECheckAddr(Ts1.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Ts1");
|
|
ReadEECheckAddr(Ts2.addr); //eeprom_addr incremeneted here
|
|
//PrintS_and_D("k:Ts2");
|
|
|
|
/*
|
|
//?code duplicated, see ReadEECheckAddr
|
|
i = 0;
|
|
if (Tae.e == 1) CheckIsInvalidCRCAddr(Tae.addr );
|
|
if (Tbe.e == 1) CheckIsInvalidCRCAddr(Tbe.addr );
|
|
if (Ttarget.e == 1) CheckIsInvalidCRCAddr(Ttarget.addr);
|
|
if (Tsump.e == 1) CheckIsInvalidCRCAddr(Tsump.addr);
|
|
if (Tci.e == 1) CheckIsInvalidCRCAddr(Tci.addr );
|
|
if (Tco.e == 1) CheckIsInvalidCRCAddr(Tco.addr );
|
|
if (Thi.e == 1) CheckIsInvalidCRCAddr(Thi.addr );
|
|
if (Tho.e == 1) CheckIsInvalidCRCAddr(Tho.addr );
|
|
if (Tbc.e == 1) CheckIsInvalidCRCAddr(Tbc.addr );
|
|
if (Tac.e == 1) CheckIsInvalidCRCAddr(Tac.addr );
|
|
if (Touter.e == 1) CheckIsInvalidCRCAddr(Touter.addr);
|
|
if (Ts1.e == 1) CheckIsInvalidCRCAddr(Ts1.addr );
|
|
if (Ts2.e == 1) CheckIsInvalidCRCAddr(Ts2.addr );
|
|
if (i != 0) {
|
|
while ( 1 ) { PrintS_and_D(F("EEPROM err1!")); delay (1000); }
|
|
}
|
|
*/
|
|
} else {
|
|
eeprom_addr += 1;
|
|
ishuman += 1;
|
|
WriteFloatEEPROM(eeprom_addr, T_setpoint);
|
|
//PrintS_and_D(F("init EEPROM"));
|
|
eeprom_addr += 4;
|
|
eeprom_addr += 2; //used sensors, skip
|
|
//Ttarget -needed, other - optional
|
|
#ifdef EEV_SUPPORT
|
|
z = FindAddr("Tae", 1); //holds result in dev_addr, returns "is used"
|
|
#else
|
|
z = FindAddr("Tae"); //holds result in dev_addr, returns "is used"
|
|
#endif
|
|
Tae.e = z;
|
|
CopyAddrStoreEE (Tae.addr, BIT_Tae); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
#ifdef EEV_SUPPORT
|
|
z = FindAddr("Tbe", 1);
|
|
#else
|
|
z = FindAddr("Tbe");
|
|
#endif
|
|
Tbe.e = z;
|
|
CopyAddrStoreEE (Tbe.addr, BIT_Tbe); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
#ifdef EEV_ONLY
|
|
//z = FindAddr("Ttarget");
|
|
z = 0;
|
|
#else
|
|
z = FindAddr("Ttarget", 1);
|
|
#endif
|
|
Ttarget.e = z;
|
|
CopyAddrStoreEE (Ttarget.addr, BIT_Ttarget); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Tsump");
|
|
Tsump.e = z;
|
|
CopyAddrStoreEE (Tsump.addr, BIT_Tsump); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Tci");
|
|
Tci.e = z;
|
|
CopyAddrStoreEE (Tci.addr, BIT_Tci); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Tco");
|
|
Tco.e = z;
|
|
CopyAddrStoreEE (Tco.addr, BIT_Tco); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Thi");
|
|
Thi.e = z;
|
|
CopyAddrStoreEE (Thi.addr, BIT_Thi); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Tho");
|
|
Tho.e = z;
|
|
CopyAddrStoreEE (Tho.addr, BIT_Tho); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Tbc");
|
|
Tbc.e = z;
|
|
CopyAddrStoreEE (Tbc.addr, BIT_Tbc); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Tac");
|
|
Tac.e = z;
|
|
CopyAddrStoreEE (Tac.addr, BIT_Tac); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Touter");
|
|
Touter.e = z;
|
|
CopyAddrStoreEE (Touter.addr, BIT_Touter); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Ts1");
|
|
Ts1.e = z;
|
|
CopyAddrStoreEE (Ts1.addr, BIT_Ts1); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
z = FindAddr("Ts2");
|
|
Ts2.e = z;
|
|
CopyAddrStoreEE (Ts2.addr, BIT_Ts2); //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
|
|
|
|
//
|
|
//final, off-the-sequence
|
|
EEPROM.write(0+1+4+0,highByte(used_sensors));
|
|
EEPROM.write(0+1+4+1,lowByte(used_sensors));
|
|
EEPROM.write(0x00, eeprom_magic);
|
|
ishuman -= 1;
|
|
}
|
|
T_setpoint_lastsaved = T_setpoint;
|
|
//s_allTsensors.setResolution(ad_Tae, 12);
|
|
/*PrintAddr(Tae.addr);
|
|
PrintAddr(Tbe.addr);
|
|
PrintAddr(Ttarget.addr);
|
|
PrintAddr(Tsump.addr);
|
|
PrintAddr(Tci.addr);
|
|
PrintAddr(Tco.addr);
|
|
PrintAddr(Thi.addr);
|
|
PrintAddr(Tho.addr);
|
|
PrintAddr(Tbc.addr);
|
|
PrintAddr(Tac.addr);
|
|
PrintAddr(Touter.addr);
|
|
PrintAddr(Ts1.addr);
|
|
PrintAddr(Ts2.addr);*/
|
|
|
|
#ifdef WATCHDOG
|
|
wdt_enable (WDTO_8S);
|
|
#endif
|
|
|
|
Get_Temperatures();
|
|
|
|
outString.reserve(200);
|
|
//PrintS_and_D(String(freeMemory())); //!!! debug
|
|
//!!!
|
|
//analogWrite(speakerOut, 10);
|
|
//delay (1500);
|
|
//analogWrite(speakerOut, 0);
|
|
//delay (1500);
|
|
//!!!
|
|
tone(speakerOut, 2250);
|
|
delay (1500); // like ups power on
|
|
noTone(speakerOut);
|
|
}
|
|
|
|
|
|
void loop(void) {
|
|
digitalWrite(SerialTxControl, RS485Receive);
|
|
millis_now = millis();
|
|
|
|
#ifdef RS485_HUMAN
|
|
if (((unsigned long)(millis_now - millis_last_printstats) > HUMAN_AUTOINFO) || (millis_last_printstats == 0) ) {
|
|
PrintStats_Serial();
|
|
millis_last_printstats = millis_now;
|
|
}
|
|
#endif
|
|
//--------------------async fuctions start
|
|
if (em_i == 0) {
|
|
supply_voltage = ReadVcc();
|
|
}
|
|
if (em_i < em_samplesnum) {
|
|
sampleI_1 = analogRead(em_pin1);
|
|
// Digital low pass filter extracts the 2.5 V or 1.65 V dc offset, then subtract this - signal is now centered on 0 counts.
|
|
offsetI_1 = (offsetI_1 + (sampleI_1-offsetI_1)/1024);
|
|
filteredI_1 = sampleI_1 - offsetI_1;
|
|
|
|
// Root-mean-square method current
|
|
// 1) square current values
|
|
sqI_1 = filteredI_1 * filteredI_1;
|
|
// 2) sum
|
|
sumI_1 += sqI_1;
|
|
|
|
em_i += 1;
|
|
} else {
|
|
em_i = 0;
|
|
double I_RATIO = em_calibration *((supply_voltage/1000.0) / (ADC_COUNTS));
|
|
async_Irms_1 = I_RATIO * sqrt(sumI_1 / em_samplesnum);
|
|
async_wattage = async_Irms_1*220.0;
|
|
|
|
//Reset accumulators
|
|
sumI_1 = 0;
|
|
|
|
//----------------------------- self-test !!!
|
|
/*
|
|
PrintS_and_D("Async impl. results 1: ");
|
|
PrintS_and_D(String(async_wattage)); // Apparent power
|
|
PrintS_and_D(" ");
|
|
PrintS_and_D(String(async_Irms_1)); // Irms
|
|
PrintS_and_D(" voltage: ");
|
|
PrintS_and_D(String(supply_voltage));
|
|
*/
|
|
//----------------------------- self-test END
|
|
|
|
}
|
|
#ifdef EEV_SUPPORT
|
|
eevise();
|
|
#endif
|
|
//--------------------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*2)) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(("Overload." + String(async_wattage)));
|
|
#endif
|
|
millis_last_heatpump_on = millis_now;
|
|
heatpump_state = 0;
|
|
halifise();
|
|
//digitalWrite(RELAY_HEATPUMP, heatpump_state); //old, now halifised
|
|
}
|
|
}
|
|
|
|
//-------------------buttons processing
|
|
#ifdef INPUTS_AS_BUTTONS
|
|
|
|
z = digitalRead(BUT_LEFT);
|
|
i = digitalRead(BUT_RIGHT);
|
|
if ( (z == 1) && ( i == 1) ) {
|
|
//
|
|
} else if ( (z == 1) || ( i == 1) ) {
|
|
#ifndef EEV_ONLY
|
|
if ( z == 1 ) {
|
|
x = Dec_T();
|
|
}
|
|
if ( i == 1 ) {
|
|
x = Inc_T();
|
|
}
|
|
if (x == 1) {
|
|
PrintS_and_D("New aim: " + String(T_setpoint));
|
|
delay(300);
|
|
}
|
|
#else
|
|
if ( z == 1 ) {
|
|
T_EEV_setpoint -= 0.25;
|
|
}
|
|
if ( i == 1 ) {
|
|
T_EEV_setpoint += 0.25;
|
|
}
|
|
PrintS_and_D("New EEV Td: " + String(T_EEV_setpoint));
|
|
delay(300);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
//-------------------buttons processing END
|
|
|
|
//-------------------display
|
|
#if (DISPLAY == 2) || (DISPLAY == 1)
|
|
if( ((unsigned long)(millis_now - millis_displ_update) > millis_displ_update_interval ) || (millis_displ_update == 0) ) {
|
|
//!!!EEV_ONLY SUPPORT???
|
|
#ifndef EEV_ONLY
|
|
outString = "A:" + String(T_setpoint, 1) + " Real:";
|
|
if (Ttarget.e == 1) {
|
|
outString += String(Ttarget.T, 1);
|
|
} else {
|
|
outString += "ERR";
|
|
}
|
|
PrintS_and_D(outString, 1); //do not print serial
|
|
|
|
//2
|
|
//#ifdef EEV_SUPPORT
|
|
// outString = "Tbe:" + String(Tbe.T, 1) + "Tae:" + String(Tbe.T, 1);
|
|
// Print_D2();
|
|
//#endif
|
|
if (Touter.e == 1){
|
|
outString = "Outer:" + String(Touter.T, 1);
|
|
Print_D2();
|
|
}
|
|
#else
|
|
outString = "be:";
|
|
if (Tbe.e == 1){
|
|
outString += String(Tbe.T, 1);
|
|
}
|
|
outString += " ae:";
|
|
if (Tae.e == 1){
|
|
outString += String(Tae.T, 1);
|
|
}
|
|
PrintS_and_D(outString, 1); //do not print serial
|
|
|
|
#endif
|
|
millis_displ_update = millis_now;
|
|
}
|
|
#endif
|
|
//-------------------display END
|
|
|
|
//-------------------check cycle
|
|
if( ((unsigned long)(millis_now - millis_prev) > millis_cycle ) || (millis_prev == 0) ) {
|
|
millis_prev = millis_now;
|
|
Get_Temperatures(); // wdt_reset here due to 85.0'C filtration
|
|
SaveSetpointEE();
|
|
|
|
//--------------------important logic
|
|
//check T sensors
|
|
if ( ( errorcode == ERR_OK ) && ( (Tae.e == 1 && Tae.T == -127 ) ||
|
|
(Tbe.e == 1 && Tbe.T == -127 ) ||
|
|
(Ttarget.e == 1 && Ttarget.T == -127 ) ||
|
|
(Tsump.e == 1 && Tsump.T == -127 ) ||
|
|
(Tci.e == 1 && Tci.T == -127 ) ||
|
|
(Tco.e == 1 && Tco.T == -127 ) ||
|
|
(Thi.e == 1 && Thi.T == -127 ) ||
|
|
(Tho.e == 1 && Tho.T == -127 ) ||
|
|
(Tbc.e == 1 && Tbc.T == -127 ) ||
|
|
(Tac.e == 1 && Tac.T == -127 ) ||
|
|
(Touter.e == 1 && Touter.T == -127 ) ||
|
|
(Ts1.e == 1 && Ts1.T == -127 ) ||
|
|
(Ts2.e == 1 && Ts2.T == -127 ) ) ) {
|
|
errorcode = ERR_T_SENSOR;
|
|
#ifdef RS485_HUMAN
|
|
PrintS_and_D("ERR:T.sens." + String(errorcode));
|
|
#endif
|
|
}
|
|
//auto-clean sensor error on sensor appear
|
|
// add 1xor enable here!
|
|
if ( ( errorcode == ERR_T_SENSOR ) && ( ( (Tae.e == 1 && Tae.T != -127 ) ||(Tae.e ^1) ) &&
|
|
( (Tbe.e == 1 && Tbe.T != -127 ) ||(Tbe.e ^1) ) &&
|
|
( (Ttarget.e == 1 && Ttarget.T != -127) ||(Ttarget.e ^1) ) &&
|
|
( (Tsump.e == 1 && Tsump.T != -127 ) ||(Tsump.e ^1) ) &&
|
|
( (Tci.e == 1 && Tci.T != -127 ) ||(Tci.e ^1) ) &&
|
|
( (Tco.e == 1 && Tco.T != -127 ) ||(Tco.e ^1) ) &&
|
|
( (Thi.e == 1 && Thi.T != -127 ) ||(Thi.e ^1) ) &&
|
|
( (Tho.e == 1 && Tho.T != -127 ) ||(Tho.e ^1) ) &&
|
|
( (Tbc.e == 1 && Tbc.T != -127 ) ||(Tbc.e ^1) ) &&
|
|
( (Tac.e == 1 && Tac.T != -127 ) ||(Tac.e ^1) ) &&
|
|
( (Touter.e == 1 && Touter.T != -127 ) ||(Touter.e ^1) ) &&
|
|
( (Ts1.e == 1 && Ts1.T != -127 ) ||(Ts1.e ^1) ) &&
|
|
( (Ts2.e == 1 && Ts2.T != -127 ) ||(Ts2.e ^1) ) ) ) {
|
|
errorcode = ERR_OK;
|
|
}
|
|
|
|
//process errors
|
|
//beep N times error
|
|
if ( errorcode != ERR_OK ) {
|
|
if ( ((unsigned long)(millis_now - millis_notification) > millis_notification_interval) || millis_notification == 0 ) {
|
|
millis_notification = millis_now;
|
|
#ifdef RS485_HUMAN
|
|
PrintS_and_D("Error:" + String(errorcode));
|
|
#endif
|
|
for ( i = 0; i < errorcode; i++) {
|
|
tone(speakerOut, ERR_HZ); delay (500);
|
|
noTone(speakerOut); delay (500);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-------------- EEV cycle
|
|
#ifdef EEV_SUPPORT
|
|
/*
|
|
//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 ){
|
|
PrintS_and_D("EEV: FULL closing");//!!!
|
|
if ( millis_eev_last_close != 0 ) {
|
|
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
|
|
} else {
|
|
EEV_apulses = -(EEV_MAXPULSES + EEV_CLOSE_ADD_PULSES);
|
|
}
|
|
EEV_adonotcare = 1;
|
|
EEV_fast = 1;
|
|
//delay(EEV_STOP_HOLD);
|
|
millis_eev_last_close = millis_now;
|
|
} else if (errorcode != 0 || async_wattage <= c_workingOK_wattage_min) { //err or sleep
|
|
PrintS_and_D("EEV: err or sleep");//!!!
|
|
if (EEV_cur_pos <= 0 && EEV_OPEN_AFTER_CLOSE != 0) { //set waiting pos
|
|
EEV_apulses = +EEV_OPEN_AFTER_CLOSE;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
}
|
|
if (EEV_cur_pos > 0 && EEV_cur_pos != EEV_OPEN_AFTER_CLOSE && EEV_cur_pos <= EEV_MAXPULSES) { //waiting pos. set
|
|
PrintS_and_D("EEV: close");//!!!
|
|
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
|
|
EEV_adonotcare = 1;
|
|
EEV_fast = 1;
|
|
}
|
|
} else if (errorcode == 0 && async_wattage > c_workingOK_wattage_min) {
|
|
T_EEV_dt = Tae.T - Tbe.T;
|
|
PrintS_and_D("EEV: driving " + String(T_EEV_dt));//!!!
|
|
if (EEV_cur_pos <= 0){
|
|
PrintS_and_D("EEV: full close protection");
|
|
if (EEV_OPEN_AFTER_CLOSE != 0) { //full close protection
|
|
EEV_apulses = +EEV_OPEN_AFTER_CLOSE;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
}
|
|
} else if (EEV_cur_pos > 0) {
|
|
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
|
|
PrintS_and_D("EEV: emergency closing!");//!!!
|
|
EEV_apulses = -EEV_EMERG_STEPS;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
} else if (T_EEV_dt < T_EEV_setpoint) { //too
|
|
PrintS_and_D("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
|
|
PrintS_and_D("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
|
|
PrintS_and_D("EEV: opening");//!!!
|
|
EEV_apulses = +1;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 0;
|
|
} else if (T_EEV_dt > T_EEV_setpoint) { //ok
|
|
PrintS_and_D("EEV: OK");//!!!
|
|
//
|
|
}
|
|
}
|
|
off_EEV();
|
|
}
|
|
|
|
}
|
|
*/
|
|
//v1.1 algo
|
|
if ( errorcode == 0 && async_wattage > c_workingOK_wattage_min && EEV_cur_pos > 0 ) {
|
|
T_EEV_dt = Tae.T - Tbe.T;
|
|
#ifdef EEV_DEBUG
|
|
PrintS("EEV Td: " + String(T_EEV_dt));
|
|
#endif
|
|
if ( EEV_apulses >= 0 && EEV_cur_pos >= EEV_MINWORKPOS) {
|
|
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 1 emergency closing!"));
|
|
#endif
|
|
EEV_apulses = -1;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
} else if (T_EEV_dt < T_EEV_setpoint) { //too
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 2 closing"));
|
|
#endif
|
|
//EEV_apulses = -EEV_NONPRECISE_STEPS;
|
|
EEV_apulses = -1;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 0;
|
|
}
|
|
//faster open when needed, condition copypasted (see EEV_apulses <= 0)
|
|
if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 3 enforce faster opening"));
|
|
#endif
|
|
//EEV_apulses = +EEV_NONPRECISE_STEPS;
|
|
//EEV_apulses = +1;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
}
|
|
}
|
|
if ( EEV_apulses <= 0 ) {
|
|
if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START) { //very
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 4 fast opening"));
|
|
#endif
|
|
//EEV_apulses = +EEV_NONPRECISE_STEPS;
|
|
EEV_apulses = +1;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
} else if (T_EEV_dt > T_EEV_setpoint + EEV_HYSTERESIS) { //too
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 5 opening"));
|
|
#endif
|
|
EEV_apulses = +1;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 0;
|
|
} else if (T_EEV_dt > T_EEV_setpoint) { //ok
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 6 OK"));
|
|
#endif
|
|
//
|
|
}
|
|
//faster closing when needed, condition copypasted (see EEV_apulses >= 0)
|
|
if (T_EEV_dt < (T_EEV_setpoint - EEV_EMERG_DIFF) ) { //emerg!
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 7 enforce faster closing!"));
|
|
#endif
|
|
//EEV_apulses = -EEV_EMERG_STEPS;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
}
|
|
}
|
|
off_EEV();
|
|
}
|
|
if ( EEV_apulses == 0 ) {
|
|
if ( ((async_wattage < c_workingOK_wattage_min) && ((unsigned long)(millis_now - millis_eev_last_close) > EEV_CLOSEEVERY)) || millis_eev_last_close == 0 ){ //close every 24h by default
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 10 FULL closing"));
|
|
#endif
|
|
if ( millis_eev_last_close != 0 ) {
|
|
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
|
|
} else {
|
|
EEV_apulses = -(EEV_MAXPULSES + EEV_CLOSE_ADD_PULSES);
|
|
}
|
|
EEV_adonotcare = 1;
|
|
EEV_fast = 1;
|
|
//delay(EEV_STOP_HOLD);
|
|
millis_eev_last_close = millis_now;
|
|
} else if (errorcode != 0 || async_wattage < c_workingOK_wattage_min) { //err or sleep
|
|
if (EEV_cur_pos > 0 && EEV_cur_pos > EEV_OPEN_AFTER_CLOSE) { //waiting pos. set
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 11 close before open"));
|
|
#endif
|
|
EEV_apulses = -(EEV_cur_pos + EEV_CLOSE_ADD_PULSES);
|
|
EEV_adonotcare = 1;
|
|
EEV_fast = 1;
|
|
}
|
|
}
|
|
off_EEV();
|
|
}
|
|
if ( EEV_apulses == 0 && async_wattage < c_workingOK_wattage_min && EEV_cur_pos < EEV_OPEN_AFTER_CLOSE) {
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 12 full close protection"));
|
|
#endif
|
|
if (EEV_OPEN_AFTER_CLOSE != 0) { //full close protection
|
|
EEV_apulses = EEV_OPEN_AFTER_CLOSE - EEV_cur_pos;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
}
|
|
off_EEV();
|
|
}
|
|
if ( EEV_apulses == 0 && async_wattage >= c_workingOK_wattage_min && EEV_cur_pos < EEV_MINWORKPOS) {
|
|
#ifdef EEV_DEBUG
|
|
PrintS(F("EEV: 13 open to work"));
|
|
#endif
|
|
if (EEV_MINWORKPOS != 0) { //full close protection
|
|
EEV_apulses = EEV_MINWORKPOS - EEV_cur_pos;
|
|
EEV_adonotcare = 0;
|
|
EEV_fast = 1;
|
|
}
|
|
off_EEV();
|
|
}
|
|
if ( ((unsigned long)(millis_now - millis_eev_last_on) > 10000) || millis_eev_last_on == 0 ) {
|
|
//PrintS_and_D("EEV: ON/OFF");
|
|
on_EEV();
|
|
//delay(30);
|
|
//off_EEV(); //off_EEV called everywhere takes care of it
|
|
millis_eev_last_on = millis_now;
|
|
}
|
|
#endif
|
|
//-------------- EEV cycle END
|
|
|
|
#ifndef EEV_ONLY
|
|
//process heatpump sump heater
|
|
if (Tsump.e == 1) {
|
|
if ( Tsump.T < cT_sump_heat_threshold && sump_heater_state == 0 && Tsump.T != -127) {
|
|
sump_heater_state = 1;
|
|
} else if (Tsump.T >= cT_sump_heat_threshold && sump_heater_state == 1) {
|
|
sump_heater_state = 0;
|
|
} else if (Tsump.T == -127) {
|
|
sump_heater_state = 0;
|
|
}
|
|
halifise();
|
|
}
|
|
|
|
|
|
|
|
//main logic
|
|
if (_1st_start_sleeped == 0) {
|
|
//PrintS_and_D("!!!!sleep disabled!!!!");
|
|
//_1st_start_sleeped = 1;
|
|
if ( (millis_now < poweron_pause) && (_1st_start_sleeped == 0) ) {
|
|
PrintS_and_D("Wait: " + String(((poweron_pause-millis_now))/1000) + " s.");
|
|
return;
|
|
} else {
|
|
_1st_start_sleeped = 1;
|
|
}
|
|
}
|
|
|
|
//process_heatpump:
|
|
//start if
|
|
// (last_on > N or not_started_yet)
|
|
// and (no errors)
|
|
// and (t hot out < t target + heat_delta_min)
|
|
// and (sump t > min'C)
|
|
// and (sump t < max'C)
|
|
// and (t watertank < target)
|
|
// and (t after evaporator > after evaporator min)
|
|
// and (t cold in > cold min)
|
|
// and (t cold out > cold min)
|
|
if ( heatpump_state == 0 &&
|
|
(((unsigned long)(millis_now - millis_last_heatpump_on) > mincycle_poweroff) || (millis_last_heatpump_on == 0) ) &&
|
|
//( tr_hot_out < (tr_sens_1 + cT_hotcircle_delta_min) ) &&
|
|
errorcode == 0 &&
|
|
( (Tsump.e == 1 && Tsump.T > cT_sump_min) || (Tsump.e^1)) &&
|
|
( (Tsump.e == 1 && Tsump.T < cT_sump_max) || (Tsump.e^1)) &&
|
|
//t1_sump > t2_cold_in && ???
|
|
Ttarget.T < T_setpoint && //was room here, change to advanced algo with room temperature
|
|
( (Tae.e == 1 && Tae.T > cT_after_evaporator_min) || (Tae.e^1)) &&
|
|
( (Tbc.e == 1 && Tbc.T < cT_before_condenser_max) || (Tbc.e^1)) &&
|
|
( (Tci.e == 1 && Tci.T > cT_cold_min) || (Tci.e^1)) &&
|
|
( (Tco.e == 1 && Tco.T > cT_cold_min) || (Tco.e^1)) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Start"));
|
|
#endif
|
|
millis_last_heatpump_off = millis_now;
|
|
heatpump_state = 1;
|
|
}
|
|
|
|
//stop if
|
|
// ( (last_off > N) and (t watertank > target) )
|
|
if ( heatpump_state == 1 && ((unsigned long)(millis_now - millis_last_heatpump_off) > mincycle_poweron) && (Ttarget.T > T_setpoint)) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Normal stop"));
|
|
#endif
|
|
millis_last_heatpump_on = millis_now;
|
|
heatpump_state = 0;
|
|
}
|
|
|
|
//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) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Hot WP ON"));
|
|
#endif
|
|
hotside_circle_state = 1;
|
|
}
|
|
|
|
if ( (heatpump_state == 0) && (hotside_circle_state == 1) ) {
|
|
if ( (deffered_stop_hotcircle != 0 && ((unsigned long)(millis_now - millis_last_heatpump_on) > deffered_stop_hotcircle) ) ) {
|
|
if ( (Tho.e == 1 && Tho.T < (Ttarget.T + cT_hotcircle_delta_min)) ||
|
|
(Thi.e == 1 && Thi.T < (Ttarget.T + cT_hotcircle_delta_min)) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Hot WP OFF 1"));
|
|
#endif
|
|
hotside_circle_state = 0;
|
|
} else {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Hot WP OFF 2"));
|
|
#endif
|
|
hotside_circle_state = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//heat if we can, just in case, ex. if lost power
|
|
if ( (hotside_circle_state == 0) &&
|
|
( Tho.e == 1 && Tho.T > (Ttarget.T + cT_hotcircle_delta_min) ) ||
|
|
( Thi.e == 1 && Thi.T > (Ttarget.T + cT_hotcircle_delta_min) ) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Hot WP ON"));
|
|
#endif
|
|
hotside_circle_state = 1;
|
|
}
|
|
|
|
//process_cold_side_pump:
|
|
//start if (heatpump_enabled)
|
|
//stop if (heatpump_disbled)
|
|
if ( (heatpump_state == 1) && (coldside_circle_state == 0) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Cold WP ON"));
|
|
#endif
|
|
coldside_circle_state = 1;
|
|
}
|
|
|
|
if ( (heatpump_state == 0) && (coldside_circle_state == 1) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Cold WP OFF"));
|
|
#endif
|
|
coldside_circle_state = 0;
|
|
}
|
|
|
|
|
|
//protective_cycle:
|
|
//stop if
|
|
// (error)
|
|
// (t hot out > hot out max)
|
|
// (sump t > max'C)
|
|
// or (t after evaporator < after evaporator min)
|
|
// or (t cold in < cold min)
|
|
// or (t cold out < cold min)
|
|
//
|
|
if ( heatpump_state == 1 &&
|
|
( errorcode != 0 ||
|
|
(Tho.e == 1 && Tho.T > cT_hotout_max) ||
|
|
(Tsump.e == 1 && Tsump.T > cT_sump_max) ||
|
|
(Tae.e == 1 && Tae.T < cT_after_evaporator_min) ||
|
|
(Tbc.e == 1 && Tbc.T > cT_before_condenser_max) ||
|
|
(Tci.e == 1 && Tci.T < cT_cold_min ) ||
|
|
(Tco.e == 1 && Tco.T < cT_cold_min) ) ) {
|
|
#ifdef RS485_HUMAN
|
|
PrintS(F("Protective stop"));
|
|
#endif
|
|
millis_last_heatpump_on = millis_now;
|
|
heatpump_state = 0;
|
|
//digitalWrite(RELAY_HEATPUMP, heatpump_state); // old, now halifised
|
|
}
|
|
|
|
//alive_check_cycle_after_5_mins:
|
|
//error if
|
|
//v1.3: not error, just poweroff all
|
|
// or (t cold in - t cold out < t workingok min)
|
|
// or (t hot out - t hot in < t workingok min)
|
|
// or (sump t < 25'C)
|
|
// or wattage too low
|
|
|
|
if ( heatpump_state == 1 && ((unsigned long)(millis_now - millis_last_heatpump_off) > 300000) ) {
|
|
//cold side processing simetimes works incorrectly, after long period of inactivity, due to T inertia on cold tube sensor, commented out
|
|
//if ( ( errorcode == ERR_OK ) && ( tr_cold_in - tr_cold_out < cT_workingOK_cold_delta_min ) ) {
|
|
// errorcode = ERR_COLD_PUMP;
|
|
//}
|
|
//if ( ( errorcode == ERR_OK ) && ( Tho.e == 1 && Thi.e == 1 && (Tho.T - Thi.T < cT_workingOK_hot_delta_min )) ) {
|
|
// errorcode = ERR_HOT_PUMP;
|
|
//}
|
|
if ( ( errorcode == ERR_OK ) && ( Tsump.e == 1 && Tsump.T < cT_workingOK_sump_min ) ) {
|
|
//errorcode = ERR_HEATPUMP;
|
|
millis_last_heatpump_on = millis_now;
|
|
heatpump_state = 0;
|
|
}
|
|
if ( ( errorcode == ERR_OK ) && ( async_wattage < c_workingOK_wattage_min ) ) {
|
|
//errorcode = ERR_WATTAGE;
|
|
millis_last_heatpump_on = millis_now;
|
|
heatpump_state = 0;
|
|
}
|
|
//digitalWrite(RELAY_HEATPUMP, heatpump_state); // old, now halifised
|
|
}
|
|
|
|
//disable pump by error
|
|
if ( errorcode != ERR_OK ) {
|
|
millis_last_heatpump_on = millis_now;
|
|
heatpump_state = 0;
|
|
//digitalWrite(RELAY_HEATPUMP, heatpump_state); // old, now halifised
|
|
#ifdef RS485_HUMAN
|
|
PrintS("Error stop: " + String(errorcode, HEX));
|
|
#endif
|
|
}
|
|
|
|
//!!! self-test
|
|
///heatpump_state = 1;
|
|
|
|
//!!! write states to relays, old, now halifised
|
|
//digitalWrite (RELAY_HEATPUMP, heatpump_state);
|
|
//digitalWrite (RELAY_COLDSIDE_CIRCLE, coldside_circle_state);
|
|
halifise();
|
|
#endif
|
|
}
|
|
|
|
if (RS485Serial.available() > 0) {
|
|
//RS485Serial.println("some on serial.."); //!!!debug
|
|
#ifdef RS485_HUMAN
|
|
if (RS485Serial.available()) {
|
|
inChar = RS485Serial.read();
|
|
//RS485Serial.print(inChar); //!!!debug
|
|
if ( inChar == 0x1B ) {
|
|
skipchars += 3;
|
|
inChar = 0x00;
|
|
millis_escinput = millis();
|
|
}
|
|
if ( skipchars != 0 ) {
|
|
millis_charinput = millis();
|
|
//if (millis_escinput + 2 > millis_charinput)
|
|
if ((unsigned long)(millis_charinput - millis_escinput) < 16*2 ) { //2 chars for 2400
|
|
if (inChar != 0x7e) {
|
|
skipchars -= 1;
|
|
}
|
|
if (inChar == 0x7e) {
|
|
skipchars = 0;
|
|
}
|
|
if (inChar >= 0x30 && inChar <= 0x35) {
|
|
skipchars += 1;
|
|
}
|
|
inChar = 0x00;
|
|
} else {
|
|
skipchars = 0;
|
|
}
|
|
}
|
|
|
|
//- RS485_HUMAN: remote commands +,-,G,0x20/?/Enter
|
|
switch (inChar) {
|
|
case 0x00:
|
|
break;
|
|
case 0x20:
|
|
_PrintHelp();
|
|
break;
|
|
case 0x3F:
|
|
_PrintHelp();
|
|
break;
|
|
case 0x0D:
|
|
_PrintHelp();
|
|
break;
|
|
case 0x2B:
|
|
Inc_T();
|
|
break;
|
|
case 0x2D:
|
|
Dec_T();
|
|
break;
|
|
case 0x3C:
|
|
Dec_E();
|
|
break;
|
|
case 0x3E:
|
|
Inc_E();
|
|
break;
|
|
case 0x47:
|
|
PrintStats_Serial();
|
|
break;
|
|
case 0x67:
|
|
PrintStats_Serial();
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef RS485_PYTHON
|
|
index = 0;
|
|
while (RS485Serial.available() > 0) { // Don't read unless you know there is data
|
|
if(index < 49) { // size of the array minus 1
|
|
inChar = RS485Serial.read(); // Read a character
|
|
inData[index] = inChar; // Store it
|
|
index++; // Increment where to write next
|
|
inData[index] = '\0'; // clear next symbol, null terminate the string
|
|
delayMicroseconds(80); //80 microseconds - the best choice at 9600, "no answer"disappeared
|
|
//40(20??) microseconds seems to be good, 9600, 49 symbols
|
|
//
|
|
} else { //too long message! read it to nowhere
|
|
inChar = RS485Serial.read();
|
|
delayMicroseconds(80);
|
|
//break; //do not break if symbols!!
|
|
}
|
|
}
|
|
|
|
//!!!debug, be carefull, can cause strange results
|
|
/*
|
|
if (inData[0] != 0x00) {
|
|
RS485Serial.println("-");
|
|
RS485Serial.println(inData);
|
|
RS485Serial.println("-");
|
|
}
|
|
*/
|
|
//or this debug
|
|
/*
|
|
digitalWrite(SerialTxControl, RS485Transmit);
|
|
delay(10);
|
|
RS485Serial.println(inData);
|
|
RS485Serial.flush();
|
|
RS485Serial.println(index);
|
|
*/
|
|
|
|
//ALL lines must be terminated with \n!
|
|
if ( (inData[0] == hostID) && (inData[1] == devID) ) {
|
|
// COMMANDS:
|
|
// G (0x47): (G)et main data
|
|
// TNN.NN (0x54): set aim (T)emperature
|
|
digitalWrite(SerialTxControl, RS485Transmit);
|
|
delay(1);
|
|
//PrintS_and_D(freeMemory());
|
|
outString = "";
|
|
outString += devID;
|
|
outString += hostID;
|
|
outString += "A "; //where A is Answer, space after header
|
|
|
|
if ( (inData[2] == 0x47 ) ) {
|
|
//PrintS_and_D("G");
|
|
//WARNING: this procedure can cause "NO answer" effect if no or few T sensors connected
|
|
outString += "{";
|
|
outString += "\"E1\":" + String(errorcode);
|
|
if (Ts1.e == 1) {
|
|
outString += ",\"TS1\":" + String(Ts1.T);
|
|
}
|
|
if (Tsump.e == 1) {
|
|
outString += ",\"TS\":" + String(Tsump.T);
|
|
}
|
|
if (Tho.e == 1) {
|
|
outString += ",\"THO\":" + String(Tho.T);
|
|
}
|
|
if (Tae.e == 1) {
|
|
outString += ",\"TAE\":" + String(Tae.T);
|
|
}
|
|
char *outChar=&outString[0];
|
|
RS485Serial.write(outChar); //dirty hack to transfer long string
|
|
RS485Serial.flush();
|
|
delay (1); //lot of errors without delay
|
|
outString = "";
|
|
if (Tbe.e == 1) {
|
|
outString += ",\"TBE\":" + String(Tbe.T);
|
|
}
|
|
if (Touter.e == 1) {
|
|
outString += ",\"TO\":" + String(Touter.T);
|
|
}
|
|
if (Tco.e == 1) {
|
|
outString += ",\"TCO\":" + String(Tco.T);
|
|
}
|
|
outString += ",\"W1\":" + String(async_wattage);
|
|
#ifndef EEV_ONLY
|
|
outString += ",\"A1\":" + String(T_setpoint); //(A)im (target)
|
|
//!!!!! must be changed for G9 v1.4 - personal pin !!!!!!!
|
|
#ifndef BOARD_TYPE_G9
|
|
outString += ",\"RP\":" + String(heatpump_state*RELAY_HEATPUMP);
|
|
#endif
|
|
#ifdef BOARD_TYPE_G9
|
|
outString += ",\"RP\":" + String(heatpump_state*20);
|
|
#endif
|
|
//!!!!!
|
|
#endif
|
|
if (Tci.e == 1) {
|
|
outString += ",\"TCI\":" + String(Tci.T);
|
|
}
|
|
RS485Serial.write(outChar); //dirty hack to transfer long string
|
|
RS485Serial.flush();
|
|
delay (1); //lot of errors without delay
|
|
outString = "";
|
|
if (Thi.e == 1) {
|
|
outString += ",\"THI\":" + String(Thi.T);
|
|
}
|
|
#ifndef EEV_ONLY
|
|
outString += ",\"RSH\":" + String(sump_heater_state*3);
|
|
outString += ",\"RH\":" + String(hotside_circle_state*2);
|
|
outString += ",\"RC\":" + String(coldside_circle_state*1);
|
|
#endif
|
|
if (Tbc.e == 1) {
|
|
outString += ",\"TBC\":" + String(Tbc.T);
|
|
}
|
|
RS485Serial.write(outChar); //dirty hack to transfer long string
|
|
RS485Serial.flush();
|
|
delay (1); //lot of errors without delay
|
|
outString = "";
|
|
if (Ts2.e == 1) {
|
|
outString += ",\"TS2\":" + String(Ts2.T);
|
|
}
|
|
if (Tac.e == 1) {
|
|
outString += ",\"TAC\":" + String(Tac.T);
|
|
}
|
|
if (Ttarget.e == 1) {
|
|
outString += ",\"TT\":" + String(Ttarget.T);
|
|
}
|
|
#ifdef EEV_SUPPORT
|
|
outString += ",\"EEVP\":" + String(EEV_cur_pos);
|
|
outString += ",\"EEVA\":" + String(T_EEV_setpoint);
|
|
#endif
|
|
outString += "}";
|
|
} else if ( (inData[2] == 0x54 ) || (inData[2] == 0x45 )) { //(T)arget or (E)EV target format NN.NN, text
|
|
if ( isDigit(inData[ 3 ]) && isDigit(inData[ 4 ]) && (inData[ 5 ] == 0x2e) && isDigit(inData[ 6 ]) && isDigit(inData[ 7 ]) && ( ! isDigit(inData[ 8 ])) ) {
|
|
|
|
tone(speakerOut, 2250);
|
|
delay (100); // like ups power on
|
|
noTone(speakerOut);
|
|
|
|
char * carray = &inData[ 3 ];
|
|
tempdouble = atof(carray);
|
|
if (inData[2] == 0x54 ){
|
|
if (tempdouble > cT_setpoint_max) {
|
|
outString += "{\"err\":\"too hot!\"}";
|
|
} else if (tempdouble < 1.0) {
|
|
outString += "{\"err\":\"too cold!\"}";
|
|
} else {
|
|
T_setpoint = tempdouble;
|
|
outString += "{\"result\":\"ok, new value is: ";
|
|
outString += String(T_setpoint);
|
|
outString += "\"}";
|
|
}
|
|
}
|
|
if (inData[2] == 0x45 ) {
|
|
if (tempdouble > 10.0) { //!!!!!!! hardcode !!!
|
|
outString += "{\"err\":\"too hot!\"}";
|
|
} else if (tempdouble < 0.1) { //!!!!!!! hardcode !!!
|
|
outString += "{\"err\":\"too cold!\"}";
|
|
} else {
|
|
T_EEV_setpoint = tempdouble;
|
|
outString += "{\"result\":\"ok, new EEV value is: ";
|
|
outString += String(T_EEV_setpoint);
|
|
outString += "\"}";
|
|
}
|
|
}
|
|
} else {
|
|
outString += "{\"err\":\"NaN, format: NN.NN\"}";
|
|
}
|
|
} else {
|
|
//default, just for example
|
|
outString += "{\"err\":\"no_command\"}";
|
|
}
|
|
//crc.integer = CRC16.xmodem((uint8_t& *) outString, outString.length());
|
|
//outString += (crc, HEX);
|
|
outString += "\n";
|
|
char *outChar=&outString[0];
|
|
RS485Serial.write(outChar);
|
|
}
|
|
|
|
index = 0;
|
|
for (i=0;i<49;i++) { //clear buffer
|
|
inData[i]=0;
|
|
}
|
|
RS485Serial.flush();
|
|
digitalWrite(SerialTxControl, RS485Receive);
|
|
delay(1);
|
|
#endif
|
|
}
|
|
|
|
}
|