2019-04-01 03:02:55 +00:00
/*
Cheap Heat Pump Controller ( CHPC ) firmware .
Copyright ( C ) 2018 - 2019 Gonzho ( gonzho @ web . de )
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
See https : //github.com/gonzho000/chpc/ for more details
*/
//-----------------------USER OPTIONS-----------------------
2019-04-16 16:35:20 +00:00
//#define BOARD_TYPE_G //Type "G"
# define BOARD_TYPE_F //Type "F"
2019-04-01 03:02:55 +00:00
//#define DISPLAY_096 1
2019-04-16 15:53:34 +00:00
//#define DISPLAY_1602 2 // patch "inline size_t LiquidCrystal_I2C::write(uint8_t value)" if only 1st character appears: "return 1" instead of "return 0"
2019-04-01 03:02:55 +00:00
//#define DISPLAY_NONE -1
2019-04-16 16:35:20 +00:00
//#define INPUTS_AS_BUTTONS 1 //pulldown resistors required!
//#define RS485_PYTHON 1
# define RS485_HUMAN 2
2019-04-16 15:53:34 +00:00
# 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
2019-04-01 03:02:55 +00:00
//#define WATCHDOG //only if u know what to do
//-----------------------TUNING OPTIONS -----------------------
# define MAX_WATTS 1170.0 //user for power protection
# define DEFFERED_STOP_HOTCIRCLE 3000000 //5 mins
2019-04-16 15:53:34 +00:00
# define POWERON_PAUSE 300000 //5 mins
# define MINCYCLE_POWEROFF 300000 //5 mins
# define MINCYCLE_POWERON 3600000 //60 mins
//EEV
# define EEV_MAXPULSES 480
# define EEV_PULSE_FCLOSE_MILLIS 20 //fast close, set waiting pos., close on danger
# define EEV_PULSE_CLOSE_MILLIS 20000 //precise close
# define EEV_PULSE_WOPEN_MILLIS 20 //waiting pos. set
# define EEV_PULSE_FOPEN_MILLIS 2000 //fast open, fast search
# define EEV_PULSE_OPEN_MILLIS 55000 //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 40 //0 - close to zero position, than close on EEV_CLOSE_ADD_PULSES
//N - close to zero position, than close on EEV_CLOSE_ADD_PULSES, than open on N pulses
# define EEV_MINWORKPOS 45 //position will be not less during normal work
//#define EEV_NONPRECISE_STEPS 3 //pulses per fast step
# define EEV_PRECISE_START 8.5 //T difference, threshold: make slower pulses if less
# define EEV_EMERG_DIFF 3.5 //see below
# define EEV_EMERG_STEPS 3 //pulses per emergency close step, if dangerous condition: diff =< (desired_overheat - EEV_EMERG_DIFF) occured
# define EEV_HYSTERESIS 0.6 //must be less than EEV_PRECISE_START, ex: overheating = 4.0, hysteresis = 0.1, if overheating in range 4.0..4.1 no EEV pulses will be done;
# define EEV_CLOSEEVERY 86400000 //86400000: every 24 hours, when HP is NOT working
# define EEV_OVERHEATING 4.0
2019-04-01 03:02:55 +00:00
# define MAGIC 0x39 //change if u want to reinit T sensors
//-----------------------USER OPTIONS END -----------------------
//#define EEV_SUPPORT
//#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
2019-04-16 15:53:34 +00:00
v1 .1 , 15 Apr 2019 :
2019-04-01 03:02:55 +00:00
- HUMAN_AUTOINFO time
2019-04-16 15:53:34 +00:00
- EEV_ONLY mode
2019-04-01 03:02:55 +00:00
- EEV_Support
2019-04-16 15:53:34 +00:00
- EEV auto poweron / poweroff every 10 sec
2019-04-16 16:35:20 +00:00
v1 .2 , 16 Apr 2019 :
- minor " Type F " support
2019-04-16 15:53:34 +00:00
//TODO:
- EEV to EEPROM
- few devices at same lane for RS485_HUMAN
2019-04-01 03:02:55 +00:00
- EEV_recalibration_time to stop HP and recalibrate EEV from zero level ex : every 24 hours
- valve_4way
- rewite re - init proc from MAGIC to emergency jumper removal at board start
- emergency jumper support
2019-04-16 15:53:34 +00:00
? Liquid ref . T protection
? current sensor optional
2019-04-01 03:02:55 +00:00
? periodical start of hot side circle
*/
//-----------------------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 ~ 10 K . .470 K resistor to GND
- via ~ 10 K . .470 K resistor to + 5 ( same as prev . )
if 10 K + 10 K used : current is 25 mA
use 100 K + 100 K 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.0 " ;
# 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
//hardware resources
# define OW_BUS_ALLTSENSORS 12
# define SerialTxControl 13 //RS485 Direction control DE and RE to this pin
# define speakerOut 6
# define em_pin1 A6
# define EMERGENCY_PIN A7
2019-04-16 17:38:55 +00:00
2019-04-16 16:35:20 +00:00
2019-04-01 03:02:55 +00:00
# ifdef BOARD_TYPE_G
2019-04-16 15:53:34 +00:00
String hw_version = " Type G v1.x " ;
2019-04-01 03:02:55 +00:00
# define RELAY_HEATPUMP 8
# define RELAY_HOTSIDE_CIRCLE 9
# define RELAY_COLDSIDE_CIRCLE 7
# define RELAY_SUMP_HEATER 10
# define RELAY_4WAY_VALVE 11
# ifdef INPUTS_AS_BUTTONS
2019-04-16 15:53:34 +00:00
# define BUT_RIGHT A3
# define BUT_LEFT A2
# endif
2019-04-16 17:38:55 +00:00
# ifdef EEV_SUPPORT
# define EEV_1 2
# define EEV_2 4
# define EEV_3 3
# define EEV_4 5
2019-04-01 03:02:55 +00:00
# endif
2019-04-16 16:35:20 +00:00
# endif
# ifdef BOARD_TYPE_F
String hw_version = " Type F v1.x " ;
# define RELAY_HEATPUMP 7
# define RELAY_COLDSIDE_CIRCLE 8
2019-04-16 17:38:55 +00:00
//incorrect below, see ralays on register
2019-04-16 16:35:20 +00:00
# define RELAY_HOTSIDE_CIRCLE 10
# define RELAY_SUMP_HEATER 11
# define RELAY_4WAY_VALVE 9
//latchPin = 10; clockPin = 11; dataPin = 9;
//595.0: relay 1, 595.1: relay 2, 595.2: relay 3, 595.3: uln 6, 595.4: uln 7, 595.5: uln 8, 595.6: uln 9, 595.7: uln 10
2019-04-16 17:38:55 +00:00
# ifdef EEV_SUPPORT
# define EEV_1 5
# define EEV_2 3
# define EEV_3 4
# define EEV_4 2
# endif
2019-04-16 16:35:20 +00:00
2019-04-01 03:02:55 +00:00
# endif
//---------------------------memory debug
# ifdef __arm__
// should use uinstd.h to define sbrk but Due causes a conflict
extern " C " char * sbrk ( int incr ) ;
# else // __ARM__
extern char * __brkval ;
# endif // __arm__
int freeMemory ( ) {
char top ;
# ifdef __arm__
return & top - reinterpret_cast < char * > ( sbrk ( 0 ) ) ;
# elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151)
return & top - __brkval ;
# else // __arm__
return __brkval ? & top - __brkval : & top - __malloc_heap_start ;
# endif // __arm__
}
//---------------------------memory debug END
# include <avr/wdt.h>
# include <EEPROM.h>
//#include <FastCRC.h>
/*FastCRC16 CRC16;
union _crc {
unsigned int integer ;
char bytes [ 2 ] ;
} crc ;
*/
# include <SoftwareSerial.h>
# define SerialRX 0 //RX connected to RO - Receiver Output
# define SerialTX 1 //TX connected to DI - Driver Output Pin
# define RS485Transmit HIGH
# define RS485Receive LOW
2019-04-16 15:53:34 +00:00
const char devID = 0x44 ;
2019-04-01 03:02:55 +00:00
const char hostID = 0x30 ;
SoftwareSerial RS485Serial ( SerialRX , SerialTX ) ; // RX, TX
# include <OneWire.h>
# include <DallasTemperature.h>
//library's DEVICE_DISCONNECTED_C -127.0
OneWire ow_ALLTSENSORS ( OW_BUS_ALLTSENSORS ) ;
DallasTemperature s_allTsensors ( & ow_ALLTSENSORS ) ;
typedef struct {
DeviceAddress addr ;
bool e ; //enabled
double T ;
} st_tsens ;
DeviceAddress dev_addr ; //temp
st_tsens Tae ;
st_tsens Tbe ;
st_tsens Ttarget ;
st_tsens Tsump ;
st_tsens Tci ;
st_tsens Tco ;
st_tsens Thi ;
st_tsens Tho ;
st_tsens Tbc ;
st_tsens Tac ;
st_tsens Touter ;
st_tsens Ts1 ;
st_tsens Ts2 ;
# define BIT_Tae 0
# define BIT_Tbe 1
# define BIT_Ttarget 2
# define BIT_Tsump 3
# define BIT_Tci 4
# define BIT_Tco 5
# define BIT_Thi 6
# define BIT_Tho 7
# define BIT_Tbc 8
# define BIT_Tac 9
# define BIT_Touter 10
# define BIT_Ts1 11
# define BIT_Ts2 12
unsigned int used_sensors = 0 ; //bit array
double T_setpoint = 26.5 ;
double T_setpoint_lastsaved = T_setpoint ;
2019-04-16 15:53:34 +00:00
double T_EEV_setpoint = EEV_OVERHEATING ;
double T_EEV_overheating = 0.0 ; //real, used during run
2019-04-01 03:02:55 +00:00
const double cT_setpoint_max = 45.0 ;
const double cT_heat_delta_min = 2.0 ;
2019-04-16 15:53:34 +00:00
const double cT_sump_min = 8.0 ;
2019-04-01 03:02:55 +00:00
const double cT_sump_max = 101.0 ;
const double cT_sump_heat_threshold = 16.0 ;
//const double cT_sump_outerT_threshold = 18.0; //?? seems to be not useful
const double cT_before_condenser_max = 99.0 ;
const double cT_after_evaporator_min = - 7.0 ; // working evaporation presure ~= -10, it is constant due to large evaporator volume // waterhouse v1: -12 is too high
const double cT_cold_min = - 8.0 ;
const double cT_hotout_max = 50.0 ;
//const double cT_workingOK_cold_delta_min = 0.5; // 0.7 - 1st try, 2nd try 0.5
const double cT_workingOK_hot_delta_min = 0.5 ;
2019-04-16 15:53:34 +00:00
const double cT_workingOK_sump_min = 30.0 ; //need to be not very high to normal start after deep freeze
2019-04-01 03:02:55 +00:00
const double c_wattage_max = MAX_WATTS ; //FUNAI: 1000W seems to be normal working wattage INCLUDING 1(one) CR25/4 at 3rd speed
//PH165X1CY : 920 Watts, 4.2 A
const double c_workingOK_wattage_min = c_wattage_max / 2.5 ; //
int heatpump_state = 0 ;
int hotside_circle_state = 0 ;
int coldside_circle_state = 0 ;
int sump_heater_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
int _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 ;
2019-04-16 15:53:34 +00:00
int EEV_cur_pos = 0 ;
int EEV_apulses = 0 ; //for async
bool EEV_adonotcare = 0 ;
const unsigned char EEV_steps [ 4 ] = { 0 b1010 , 0 b0110 , 0 b0101 , 0 b1001 } ;
char EEV_cur_step = 0 ;
bool EEV_fast = 0 ;
2019-04-01 03:02:55 +00:00
//main cycle vars
unsigned long millis_prev = 0 ;
unsigned long millis_now = 0 ;
2019-04-16 15:53:34 +00:00
unsigned long millis_cycle = 1000 ;
2019-04-01 03:02:55 +00:00
unsigned long millis_last_heatpump_on = 0 ;
unsigned long millis_last_heatpump_off = 0 ;
unsigned long millis_notification = 0 ;
unsigned long millis_notification_interval = 33000 ;
unsigned long millis_displ_update = 0 ;
unsigned long millis_displ_update_interval = 10000 ;
unsigned long millis_escinput = 0 ;
unsigned long millis_charinput = 0 ;
unsigned long millis_lasteesave = 0 ;
2019-04-16 15:53:34 +00:00
unsigned long millis_last_printstats = 0 ;
unsigned long millis_eev_last_close = 0 ;
unsigned long millis_eev_last_on = 0 ;
unsigned long millis_eev_last_step = 0 ;
2019-04-01 03:02:55 +00:00
int skipchars = 0 ;
# define ERR_HZ 2500
char inData [ 50 ] ; // Allocate some space for the string, do not change that size!
char inChar = - 1 ; // space to store the character read
byte index = 0 ; // Index into array; where to store the character
//-------------temporary variables
char temp [ 10 ] ;
int i = 0 ;
int z = 0 ;
int x = 0 ;
2019-04-16 15:53:34 +00:00
int y = 0 ;
2019-04-01 03:02:55 +00:00
double tempdouble = 0.0 ;
int tempint = 0 ;
String outString ;
//-------------EEPROM
int eeprom_magic_read = 0x00 ;
int eeprom_addr = 0x00 ;
//initial values, saved to EEPROM and can be modified later
//CHANGE eeprom_magic after correction!
const int eeprom_magic = MAGIC ;
//-------------ERROR states
# define ERR_OK 0
# define ERR_T_SENSOR 1
# define ERR_HOT_PUMP 2
# define ERR_COLD_PUMP 3
# define ERR_HEATPUMP 4
# define ERR_WATTAGE 5
int errorcode = 0 ;
//--------------------------- for wattage
# define ADC_BITS 10 //10 fo regular arduino
# define ADC_COUNTS (1<<ADC_BITS)
2019-04-16 15:53:34 +00:00
float em_calibration = 62.5 ;
int em_samplesnum = 2960 ; // Calculate Irms only 1480 == full 14 periods for 50Hz
//double Irms = 0; //for tests with original procedure
2019-04-01 03:02:55 +00:00
int supply_voltage = 0 ;
int em_i = 0 ;
//phase 1
int sampleI_1 = 0 ;
double filteredI_1 = 0 ;
2019-04-16 15:53:34 +00:00
double offsetI_1 = ADC_COUNTS > > 1 ; //Low-pass filter output
double sqI_1 , sumI_1 = 0 ; //sq = squared, sum = Sum, inst = instantaneous
2019-04-01 03:02:55 +00:00
double async_Irms_1 = 0 ;
double async_wattage = 0 ;
//--------------------------- for wattage END
//--------------------------- functions
long ReadVcc ( ) {
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
# if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
ADMUX = _BV ( REFS0 ) | _BV ( MUX4 ) | _BV ( MUX3 ) | _BV ( MUX2 ) | _BV ( MUX1 ) ;
# elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ADMUX = _BV ( MUX5 ) | _BV ( MUX0 ) ;
# elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
ADMUX = _BV ( MUX3 ) | _BV ( MUX2 ) ;
# else
ADMUX = _BV ( REFS0 ) | _BV ( MUX3 ) | _BV ( MUX2 ) | _BV ( MUX1 ) ;
# endif
delay ( 2 ) ; // Wait for Vref to settle
ADCSRA | = _BV ( ADSC ) ; // Start conversion
while ( bit_is_set ( ADCSRA , ADSC ) ) ; // measuring
uint8_t low = ADCL ; // must read ADCL first - it then locks ADCH
uint8_t high = ADCH ; // unlocks both
long result = ( high < < 8 ) | low ;
//constant NOT same as in battery controller!
result = 1126400L / result ; // Calculate Vcc (in mV); (me: !!) 1125300 (!!) = 1.1*1023*1000
return result ; // Vcc in millivolts
}
char CheckAddrExists ( void ) {
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 ;
}
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_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 _PrintHelp ( void ) {
//sprintf(tweetMsg, "first variable = %d, 2nd variable = %ul", var1, var2)
PrintS_and_D ( " CHPC, https://github.com/gonzho000/chpc/ fw: " + fw_version + " board: " + hw_version ) ;
PrintS_and_D ( F ( " Commands: " ) ) ;
PrintS_and_D ( F ( " (?) help " ) ) ;
PrintS_and_D ( F ( " (+) increase aim T " ) ) ;
PrintS_and_D ( F ( " (-) decrease aim T " ) ) ;
2019-04-16 15:53:34 +00:00
# ifdef EEV_SUPPORT
PrintS_and_D ( F ( " (<) increase EEV overheating UNIMPLEMENTED!!! " ) ) ;
PrintS_and_D ( F ( " (>) decrease EEV overheating UNIMPLEMENTED!!! " ) ) ;
# endif
2019-04-01 03:02:55 +00:00
PrintS_and_D ( 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 ( " ERR: Max T! " ) ) ;
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 ( " ERR: Min T! " ) ) ;
delay ( 200 ) ;
return 0 ;
}
T_setpoint - = 0.5 ;
PrintS_and_D_double ( T_setpoint ) ;
return 1 ;
}
2019-04-16 15:53:34 +00:00
int Inc_E ( void ) { ///!!!!!! unprotected
T_EEV_setpoint + = 0.25 ;
PrintS_and_D_double ( T_EEV_setpoint ) ;
return 1 ;
}
int Dec_E ( void ) { ///!!!!!! unprotected
T_EEV_setpoint - = 0.25 ;
PrintS_and_D_double ( T_EEV_setpoint ) ;
return 1 ;
}
2019-04-01 03:02:55 +00:00
void print_Serial_SaD ( double num ) { //global string + double
RS485Serial . print ( outString ) ;
RS485Serial . println ( num ) ;
}
void PrintStats_Serial ( void ) {
# ifdef RS485_HUMAN
digitalWrite ( SerialTxControl , RS485Transmit ) ;
delay ( 1 ) ;
if ( Tae . e = = 1 ) { outString = " Tae: " ; print_Serial_SaD ( Tae . T ) ; }
if ( Tbe . e = = 1 ) { outString = " Tbe: " ; print_Serial_SaD ( Tbe . T ) ; }
if ( Ttarget . e = = 1 ) { outString = " Ttarget: " ; print_Serial_SaD ( Ttarget . T ) ; }
if ( Tsump . e = = 1 ) { outString = " Tsump: " ; print_Serial_SaD ( Tsump . T ) ; }
if ( Tci . e = = 1 ) { outString = " Tci: " ; print_Serial_SaD ( Tci . T ) ; }
if ( Tco . e = = 1 ) { outString = " Tco: " ; print_Serial_SaD ( Tco . T ) ; }
if ( Thi . e = = 1 ) { outString = " Thi: " ; print_Serial_SaD ( Thi . T ) ; }
if ( Tho . e = = 1 ) { outString = " Tho: " ; print_Serial_SaD ( Tho . T ) ; }
if ( Tbc . e = = 1 ) { outString = " Tbc: " ; print_Serial_SaD ( Tbc . T ) ; }
if ( Tac . e = = 1 ) { outString = " Tac: " ; print_Serial_SaD ( Tac . T ) ; }
if ( Touter . e = = 1 ) { outString = " Touter: " ; print_Serial_SaD ( Touter . T ) ; }
if ( Ts1 . e = = 1 ) { outString = " Ts1: " ; print_Serial_SaD ( Ts1 . T ) ; }
if ( Ts2 . e = = 1 ) { outString = " Ts2: " ; print_Serial_SaD ( Ts2 . T ) ; }
outString = " Err: " + String ( errorcode ) + " \n \r Watts: " + String ( async_wattage ) + " \n \r Aim: " ; print_Serial_SaD ( T_setpoint ) ;
2019-04-16 15:53:34 +00:00
# ifdef EEV_SUPPORT
outString = " EEV_pos: " + String ( EEV_cur_pos ) ;
RS485Serial . print ( outString ) ;
# endif
2019-04-01 03:02:55 +00:00
RS485Serial . println ( ) ;
RS485Serial . flush ( ) ;
digitalWrite ( SerialTxControl , RS485Receive ) ;
# endif
}
void ReadEECheckAddr ( unsigned char * to_addr ) {
for ( i = 0 ; i < 8 ; i + + ) {
to_addr [ i ] = EEPROM . read ( eeprom_addr ) ;
eeprom_addr + + ;
}
i = 0 ;
CheckIsInvalidCRCAddr ( to_addr ) ;
if ( i ! = 0 ) {
while ( 1 ) {
PrintS_and_D ( F ( " Err:EEPROM, reinit! " ) ) ;
delay ( 1000 ) ;
}
}
}
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 ;
//PrintS_and_D("Deb: EEsave!"); //!!!
}
}
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 ] ) {
RS485Serial . print ( " Invalid CRC! \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 ( " Remove " + what ) ;
delay ( 1000 ) ;
} else {
delay ( 100 ) ;
break ;
}
}
return i ;
}
double GetT ( unsigned char * str ) {
tempdouble = - 127.0 ;
for ( i = 0 ; i < 8 ; i + + ) {
# ifdef WATCHDOG
wdt_reset ( ) ;
# endif
if ( ( tempdouble = = 85.0 ) | | ( tempdouble = = - 127.0 ) ) {
if ( tempdouble = = 85.0 ) { //initial value in dallas register after poweron
delay ( 375 ) ; //375 actual for 11 bits resolution, 2-3 retries OK for 12-bits resolution
} else {
delay ( 37 ) ;
}
tempdouble = s_allTsensors . getTempC ( str ) ;
} else {
break ;
}
}
return tempdouble ;
}
void Get_Temperatures ( void ) {
if ( Tae . e ) Tae . T = GetT ( Tae . addr ) ;
if ( Tbe . e ) Tbe . T = GetT ( Tbe . addr ) ;
2019-04-16 15:53:34 +00:00
if ( Ttarget . e ) Ttarget . T = GetT ( Ttarget . addr ) ;
2019-04-01 03:02:55 +00:00
if ( Tsump . e ) Tsump . T = GetT ( Tsump . addr ) ;
if ( Tci . e ) Tci . T = GetT ( Tci . addr ) ;
if ( Tco . e ) Tco . T = GetT ( Tco . addr ) ;
if ( Thi . e ) Thi . T = GetT ( Thi . addr ) ;
if ( Tho . e ) Tho . T = GetT ( Tho . addr ) ;
if ( Tbc . e ) Tbc . T = GetT ( Tbc . addr ) ;
if ( Tac . e ) Tac . T = GetT ( Tac . addr ) ;
if ( Touter . e ) Touter . T = GetT ( Touter . addr ) ;
if ( Ts1 . e ) Ts1 . T = GetT ( Ts1 . addr ) ;
if ( Ts2 . e ) Ts2 . T = GetT ( Ts2 . addr ) ;
s_allTsensors . requestTemperatures ( ) ; //global request
//---------DEBUG and self-test !!!--------
/*PrintS_and_D("");
PrintS_and_D_double ( Tae . T ) ;
PrintS_and_D_double ( Tbe . T ) ;
PrintS_and_D_double ( Ttarget . T ) ;
PrintS_and_D_double ( Tsump . T ) ;
PrintS_and_D ( " " ) ; */
/*
PrintS_and_D ( " Sensor 1 " ) ;
PrintS_and_D_double ( tr_sens_1 ) ;
PrintS_and_D ( " , \t ci " ) ;
PrintS_and_D_double ( tr_cold_in ) ;
PrintS_and_D ( " , \t cout " ) ;
PrintS_and_D_double ( tr_cold_out ) ;
PrintS_and_D ( " , \t hin " ) ;
PrintS_and_D_double ( tr_hot_in ) ;
PrintS_and_D ( " , \t ho " ) ;
PrintS_and_D_double ( tr_hot_out ) ;
PrintS_and_D ( " , \t bcond " ) ;
PrintS_and_D_double ( tr_before_condenser ) ;
PrintS_and_D ( " , \t outer " ) ;
PrintS_and_D_double ( tr_outer ) ;
PrintS_and_D ( " , \t Sensor 2 " ) ;
PrintS_and_D_double ( tr_sens_2 ) ;
*/
//---------DEBUG END--------
}
2019-04-16 15:53:34 +00:00
# ifdef EEV_SUPPORT
void on_EEV ( ) { //1 = do not take care of position
x = EEV_steps [ EEV_cur_step ] ;
digitalWrite ( EEV_1 , bitRead ( x , 0 ) ) ;
digitalWrite ( EEV_2 , bitRead ( x , 1 ) ) ;
digitalWrite ( EEV_3 , bitRead ( x , 2 ) ) ;
digitalWrite ( EEV_4 , bitRead ( x , 3 ) ) ;
}
2019-04-01 03:02:55 +00:00
2019-04-16 15:53:34 +00:00
void off_EEV ( ) { //1 = do not take care of position
digitalWrite ( EEV_1 , 0 ) ;
digitalWrite ( EEV_2 , 0 ) ;
digitalWrite ( EEV_3 , 0 ) ;
digitalWrite ( EEV_4 , 0 ) ;
}
# endif
//--------------------------- functions END
2019-04-01 03:02:55 +00:00
void setup ( void ) {
pinMode ( RELAY_HEATPUMP , OUTPUT ) ;
pinMode ( RELAY_HOTSIDE_CIRCLE , OUTPUT ) ;
pinMode ( RELAY_COLDSIDE_CIRCLE , OUTPUT ) ;
pinMode ( RELAY_SUMP_HEATER , OUTPUT ) ;
digitalWrite ( RELAY_HEATPUMP , LOW ) ;
digitalWrite ( RELAY_HOTSIDE_CIRCLE , LOW ) ;
digitalWrite ( RELAY_COLDSIDE_CIRCLE , LOW ) ;
digitalWrite ( RELAY_SUMP_HEATER , LOW ) ;
# ifdef WATCHDOG
wdt_disable ( ) ;
delay ( 2000 ) ;
wdt_enable ( WDTO_8S ) ;
# endif
2019-04-16 15:53:34 +00:00
2019-04-01 03:02:55 +00:00
InitS_and_D ( ) ;
pinMode ( SerialTxControl , OUTPUT ) ;
digitalWrite ( SerialTxControl , RS485Receive ) ;
//digitalWrite(SerialTxControl, RS485Transmit);
//RS485Serial.println("starting..."); //!!!debug
delay ( 100 ) ;
PrintS_and_D ( " ID: 0x " + String ( devID , HEX ) ) ;
//Print_Lomem(C_ID);
2019-04-16 15:53:34 +00:00
delay ( 200 ) ;
# ifdef EEV_SUPPORT
pinMode ( EEV_1 , OUTPUT ) ;
pinMode ( EEV_2 , OUTPUT ) ;
pinMode ( EEV_3 , OUTPUT ) ;
pinMode ( EEV_4 , OUTPUT ) ;
off_EEV ( ) ;
# endif
pinMode ( em_pin1 , INPUT ) ;
2019-04-01 03:02:55 +00:00
//PrintS_and_D("setpoint (C):");
//PrintS_and_D(setpoint);
//PrintS_and_D(String(freeMemory())); //!!! debug
s_allTsensors . begin ( ) ;
s_allTsensors . setWaitForConversion ( false ) ; //ASYNC mode, request before get, see Dallas library for details
eeprom_magic_read = EEPROM . read ( eeprom_addr ) ;
# ifdef INPUTS_AS_BUTTONS
pinMode ( BUT_RIGHT , INPUT ) ;
//digitalWrite (BUT_RIGHT, LOW);
pinMode ( BUT_LEFT , INPUT ) ;
//digitalWrite (BUT_LEFT, LOW);
# endif
//EEPROM content:
//0x00 - magic,
//0x01 .. 0x04 Target value,
//0x05 and 0x06 if sensor enabled or not, used_sensors HI and LO
//0x07 .. 0x0e 1st addr, etc..
// tr_after_evaporator(0); tr_before_evaporator(1); tr_target(2); tr_sump(3);
// tr_cold_in(4); tr_cold_out(5); tr_hot_in(6); tr_hot_out(7);
// tr_before_condenser(8); tr_after_condenser(9); tr_outer(10); tr_sens_1(11);
// tr_sens_2(12);
eeprom_addr = 0x00 ;
if ( eeprom_magic_read = = eeprom_magic ) {
eeprom_addr + = 1 ;
T_setpoint = ReadFloatEEPROM ( eeprom_addr ) ;
eeprom_addr + = 4 ;
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
ReadEECheckAddr ( Tbe . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Ttarget . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Tsump . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Tci . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Tco . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Thi . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Tho . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Tbc . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Tac . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Touter . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Ts1 . addr ) ; //eeprom_addr incremeneted here
ReadEECheckAddr ( Ts2 . addr ) ; //eeprom_addr incremeneted here
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
2019-04-16 15:53:34 +00:00
# ifdef EEV_ONLY
//z = FindAddr("Ttarget");
z = 0 ;
# else
z = FindAddr ( " Ttarget " , 1 ) ;
# endif
2019-04-01 03:02:55 +00:00
Ttarget . e = z ;
CopyAddrStoreEE ( Ttarget . addr , BIT_Ttarget ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Tsump " ) ;
Tsump . e = z ;
CopyAddrStoreEE ( Tsump . addr , BIT_Tsump ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Tci " ) ;
Tci . e = z ;
CopyAddrStoreEE ( Tci . addr , BIT_Tci ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Tco " ) ;
Tco . e = z ;
CopyAddrStoreEE ( Tco . addr , BIT_Tco ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Thi " ) ;
Thi . e = z ;
CopyAddrStoreEE ( Thi . addr , BIT_Thi ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Tho " ) ;
Tho . e = z ;
CopyAddrStoreEE ( Tho . addr , BIT_Tho ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Tbc " ) ;
Tbc . e = z ;
CopyAddrStoreEE ( Tbc . addr , BIT_Tbc ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Tac " ) ;
Tac . e = z ;
CopyAddrStoreEE ( Tac . addr , BIT_Tac ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Touter " ) ;
Touter . e = z ;
CopyAddrStoreEE ( Touter . addr , BIT_Touter ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Ts1 " ) ;
Ts1 . e = z ;
CopyAddrStoreEE ( Ts1 . addr , BIT_Ts1 ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
z = FindAddr ( " Ts2 " ) ;
Ts2 . e = z ;
CopyAddrStoreEE ( Ts2 . addr , BIT_Ts2 ) ; //dev_addr and z used by proc, autoincrement eeprom_addr, store bit
//
//final, off-the-sequence
EEPROM . write ( 0 + 1 + 4 + 0 , highByte ( used_sensors ) ) ;
EEPROM . write ( 0 + 1 + 4 + 1 , lowByte ( used_sensors ) ) ;
EEPROM . write ( 0x00 , eeprom_magic ) ;
ishuman - = 1 ;
}
T_setpoint_lastsaved = T_setpoint ;
//s_allTsensors.setResolution(ad_Tae, 12);
/*PrintAddr(Tae.addr);
PrintAddr ( Tbe . addr ) ;
PrintAddr ( Ttarget . addr ) ;
PrintAddr ( Tsump . addr ) ;
PrintAddr ( Tci . addr ) ;
PrintAddr ( Tco . addr ) ;
PrintAddr ( Thi . addr ) ;
PrintAddr ( Tho . addr ) ;
PrintAddr ( Tbc . addr ) ;
PrintAddr ( Tac . addr ) ;
PrintAddr ( Touter . addr ) ;
PrintAddr ( Ts1 . addr ) ;
PrintAddr ( Ts2 . addr ) ; */
Get_Temperatures ( ) ;
tone ( speakerOut , 2250 ) ;
delay ( 1500 ) ; // like ups power on
noTone ( speakerOut ) ;
2019-04-16 15:53:34 +00:00
2019-04-01 03:02:55 +00:00
outString . reserve ( 200 ) ;
//PrintS_and_D(String(freeMemory())); //!!! debug
}
void loop ( void ) {
digitalWrite ( SerialTxControl , RS485Receive ) ;
millis_now = millis ( ) ;
//----------------------------- self-test !!!
2019-04-16 15:53:34 +00:00
/*
2019-04-01 03:02:55 +00:00
digitalWrite ( RELAY_HEATPUMP , HIGH ) ;
//delay(300);
digitalWrite ( RELAY_HOTSIDE_CIRCLE , HIGH ) ;
//delay(300);
digitalWrite ( RELAY_COLDSIDE_CIRCLE , HIGH ) ;
//delay(300);
digitalWrite ( RELAY_SUMP_HEATER , HIGH ) ;
2019-04-16 15:53:34 +00:00
delay ( 2000 ) ;
2019-04-01 03:02:55 +00:00
digitalWrite ( RELAY_HEATPUMP , LOW ) ;
delay ( 300 ) ;
digitalWrite ( RELAY_HOTSIDE_CIRCLE , LOW ) ;
delay ( 300 ) ;
digitalWrite ( RELAY_COLDSIDE_CIRCLE , LOW ) ;
delay ( 300 ) ;
digitalWrite ( RELAY_SUMP_HEATER , LOW ) ;
*/
// step one revolution in one direction:
2019-04-16 15:53:34 +00:00
//!!! write self-test for EEV
2019-04-01 03:02:55 +00:00
//----------------------------- self-test END
2019-04-16 15:53:34 +00:00
# ifdef RS485_HUMAN
if ( ( ( unsigned long ) ( millis_now - millis_last_printstats ) > HUMAN_AUTOINFO ) | | ( millis_last_printstats = = 0 ) ) {
PrintStats_Serial ( ) ;
millis_last_printstats = millis_now ;
}
# endif
//--------------------async fuctions start
2019-04-01 03:02:55 +00:00
if ( em_i = = 0 ) {
supply_voltage = ReadVcc ( ) ;
}
if ( em_i < em_samplesnum ) {
sampleI_1 = analogRead ( em_pin1 ) ;
// Digital low pass filter extracts the 2.5 V or 1.65 V dc offset, then subtract this - signal is now centered on 0 counts.
offsetI_1 = ( offsetI_1 + ( sampleI_1 - offsetI_1 ) / 1024 ) ;
filteredI_1 = sampleI_1 - offsetI_1 ;
// Root-mean-square method current
// 1) square current values
sqI_1 = filteredI_1 * filteredI_1 ;
// 2) sum
sumI_1 + = sqI_1 ;
em_i + = 1 ;
} else {
em_i = 0 ;
double I_RATIO = em_calibration * ( ( supply_voltage / 1000.0 ) / ( ADC_COUNTS ) ) ;
async_Irms_1 = I_RATIO * sqrt ( sumI_1 / em_samplesnum ) ;
async_wattage = async_Irms_1 * 220.0 ;
//Reset accumulators
sumI_1 = 0 ;
//----------------------------- self-test !!!
/*
PrintS_and_D ( " Async impl. results 1: " ) ;
2019-04-16 15:53:34 +00:00
PrintS_and_D ( String ( async_wattage ) ) ; // Apparent power
2019-04-01 03:02:55 +00:00
PrintS_and_D ( " " ) ;
2019-04-16 15:53:34 +00:00
PrintS_and_D ( String ( async_Irms_1 ) ) ; // Irms
2019-04-01 03:02:55 +00:00
PrintS_and_D ( " voltage: " ) ;
2019-04-16 15:53:34 +00:00
PrintS_and_D ( String ( supply_voltage ) ) ;
2019-04-01 03:02:55 +00:00
*/
//----------------------------- self-test END
}
2019-04-16 15:53:34 +00:00
# ifdef EEV_SUPPORT
if ( ( ( ( EEV_apulses < 0 ) & & ( EEV_fast = = 1 ) ) & & ( ( unsigned long ) ( millis_now - millis_eev_last_step ) > ( EEV_PULSE_FCLOSE_MILLIS ) ) ) | |
( ( ( EEV_apulses < 0 ) & & ( EEV_fast = = 0 ) ) & & ( ( unsigned long ) ( millis_now - millis_eev_last_step ) > ( EEV_PULSE_CLOSE_MILLIS ) ) ) | |
( ( ( EEV_apulses > 0 ) & & ( EEV_cur_pos < EEV_MINWORKPOS ) ) & & ( ( unsigned long ) ( millis_now - millis_eev_last_step ) > ( EEV_PULSE_WOPEN_MILLIS ) ) ) | |
( ( ( EEV_apulses > 0 ) & & ( EEV_fast = = 1 ) & & ( EEV_cur_pos > = EEV_MINWORKPOS ) ) & & ( ( unsigned long ) ( millis_now - millis_eev_last_step ) > ( EEV_PULSE_FOPEN_MILLIS ) ) ) | |
( ( ( EEV_apulses > 0 ) & & ( EEV_fast = = 0 ) & & ( EEV_cur_pos > = EEV_MINWORKPOS ) ) & & ( ( unsigned long ) ( millis_now - millis_eev_last_step ) > ( EEV_PULSE_OPEN_MILLIS ) ) ) | |
( millis_eev_last_step = = 0 )
) {
if ( EEV_apulses ! = 0 ) {
if ( EEV_apulses > 0 ) {
if ( EEV_cur_pos + 1 < = EEV_MAXPULSES ) {
EEV_cur_pos + = 1 ;
EEV_cur_step + = 1 ;
EEV_apulses - = 1 ;
} else {
EEV_apulses = 0 ;
//PrintS_and_D("EEmax!");
}
}
if ( EEV_apulses < 0 ) {
if ( ( EEV_cur_pos - 1 > = EEV_MINWORKPOS ) | | ( EEV_adonotcare = = 1 ) ) {
EEV_cur_pos - = 1 ;
EEV_cur_step - = 1 ;
EEV_apulses + = 1 ;
} else {
EEV_apulses = 0 ;
//PrintS_and_D("EEmin!");
}
}
if ( EEV_cur_step > 3 ) EEV_cur_step = 0 ;
if ( EEV_cur_step < 0 ) EEV_cur_step = 3 ;
x = EEV_steps [ EEV_cur_step ] ;
digitalWrite ( EEV_1 , bitRead ( x , 0 ) ) ;
digitalWrite ( EEV_2 , bitRead ( x , 1 ) ) ;
digitalWrite ( EEV_3 , bitRead ( x , 2 ) ) ;
digitalWrite ( EEV_4 , bitRead ( x , 3 ) ) ;
}
if ( EEV_cur_pos < 0 ) {
EEV_cur_pos = 0 ;
}
millis_eev_last_step = millis_now ;
PrintS_and_D ( String ( EEV_cur_pos ) ) ; //!!!!
}
# endif
//--------------------async fuctions END
2019-04-01 03:02:55 +00:00
if ( heatpump_state = = 1 & & async_wattage > c_wattage_max ) {
PrintS_and_D ( F ( " Overload stop. " ) ) ;
millis_last_heatpump_on = millis_now ;
heatpump_state = 0 ;
digitalWrite ( RELAY_HEATPUMP , heatpump_state ) ;
}
//-------------------buttons processing
# ifdef INPUTS_AS_BUTTONS
2019-04-16 15:53:34 +00:00
z = digitalRead ( BUT_LEFT ) ;
i = digitalRead ( BUT_RIGHT ) ;
if ( ( z = = 1 ) & & ( i = = 1 ) ) {
//
} else if ( ( z = = 1 ) | | ( i = = 1 ) ) {
# ifndef EEV_ONLY
if ( z = = 1 ) {
x = Dec_T ( ) ;
}
if ( i = = 1 ) {
x = Inc_T ( ) ;
}
if ( x = = 1 ) {
PrintS_and_D ( " New aim: " + String ( T_setpoint ) ) ;
delay ( 300 ) ;
}
# else
if ( z = = 1 ) {
T_EEV_setpoint - = 0.25 ;
}
if ( i = = 1 ) {
T_EEV_setpoint + = 0.25 ;
}
PrintS_and_D ( " New EEV o.h.: " + String ( T_EEV_setpoint ) ) ;
delay ( 300 ) ;
# endif
2019-04-01 03:02:55 +00:00
}
2019-04-16 15:53:34 +00:00
2019-04-01 03:02:55 +00:00
# endif
//-------------------buttons processing END
//-------------------display
# if (DISPLAY == 2) || (DISPLAY == 1)
if ( ( ( unsigned long ) ( millis_now - millis_displ_update ) > millis_displ_update_interval ) | | ( millis_displ_update = = 0 ) ) {
2019-04-16 15:53:34 +00:00
//EEV_ONLY SUPPORT!!!!!!!
# ifndef EEV_ONLY
outString = " A: " + String ( T_setpoint , 1 ) + " Real: " ;
if ( Ttarget . e = = 1 ) {
outString + = String ( Ttarget . T , 1 ) ;
} else {
outString + = " ERR " ;
}
PrintS_and_D ( outString , 1 ) ; //do not print serial
# else
outString = " be: " ;
if ( Tbe . e = = 1 ) {
outString + = String ( Tbe . T , 1 ) ;
}
outString + = " ae: " ;
if ( Tae . e = = 1 ) {
outString + = String ( Tae . T , 1 ) ;
}
PrintS_and_D ( outString , 1 ) ; //do not print serial
# endif
2019-04-01 03:02:55 +00:00
millis_displ_update = millis_now ;
}
# endif
//-------------------display END
//-------------------check cycle
if ( ( ( unsigned long ) ( millis_now - millis_prev ) > millis_cycle ) | | ( millis_prev = = 0 ) ) {
millis_prev = millis_now ;
Get_Temperatures ( ) ; // wdt_reset here due to 85.0'C filtration
SaveSetpointEE ( ) ;
//--------------------important logic
//check T sensors
if ( ( errorcode = = ERR_OK ) & & ( ( Tae . e = = 1 & & Tae . T = = - 127 ) | |
( Tbe . e = = 1 & & Tbe . T = = - 127 ) | |
2019-04-16 15:53:34 +00:00
( Ttarget . e = = 1 & & Ttarget . T = = - 127 ) | |
2019-04-01 03:02:55 +00:00
( 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 ;
PrintS_and_D ( " ERR:T.sens. " + String ( errorcode ) ) ;
}
//auto-clean sensor error on sensor appear
// add 1xor enable here!
if ( ( errorcode = = ERR_T_SENSOR ) & & ( ( ( Tae . e = = 1 & & Tae . T ! = - 127 ) | | ( Tae . e ^ 1 ) ) & &
( ( Tbe . e = = 1 & & Tbe . T ! = - 127 ) | | ( Tbe . e ^ 1 ) ) & &
2019-04-16 15:53:34 +00:00
( ( Ttarget . e = = 1 & & Ttarget . T ! = - 127 ) | | ( Ttarget . e ^ 1 ) ) & &
2019-04-01 03:02:55 +00:00
( ( Tsump . e = = 1 & & Tsump . T ! = - 127 ) | | ( Tsump . e ^ 1 ) ) & &
( ( Tci . e = = 1 & & Tci . T ! = - 127 ) | | ( Tci . e ^ 1 ) ) & &
( ( Tco . e = = 1 & & Tco . T ! = - 127 ) | | ( Tco . e ^ 1 ) ) & &
( ( Thi . e = = 1 & & Thi . T ! = - 127 ) | | ( Thi . e ^ 1 ) ) & &
( ( Tho . e = = 1 & & Tho . T ! = - 127 ) | | ( Tho . e ^ 1 ) ) & &
( ( Tbc . e = = 1 & & Tbc . T ! = - 127 ) | | ( Tbc . e ^ 1 ) ) & &
( ( Tac . e = = 1 & & Tac . T ! = - 127 ) | | ( Tac . e ^ 1 ) ) & &
( ( Touter . e = = 1 & & Touter . T ! = - 127 ) | | ( Touter . e ^ 1 ) ) & &
( ( Ts1 . e = = 1 & & Ts1 . T ! = - 127 ) | | ( Ts1 . e ^ 1 ) ) & &
( ( 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 ;
PrintS_and_D ( " Error: " + String ( errorcode ) ) ;
for ( i = 0 ; i < errorcode ; i + + ) {
tone ( speakerOut , ERR_HZ ) ; delay ( 500 ) ;
noTone ( speakerOut ) ; delay ( 500 ) ;
}
}
}
2019-04-16 15:53:34 +00:00
//-------------- EEV cycle
# ifdef EEV_SUPPORT
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_overheating = Tae . T - Tbe . T ;
2019-04-16 17:38:55 +00:00
PrintS_and_D ( " EEV: driving " + String ( T_EEV_overheating ) ) ; //!!!
2019-04-16 15:53:34 +00:00
if ( EEV_cur_pos < = 0 ) {
PrintS_and_D ( " EEV: full close protection " ) ;
if ( EEV_OPEN_AFTER_CLOSE ! = 0 ) { //full close protection
EEV_apulses = + EEV_OPEN_AFTER_CLOSE ;
EEV_fast = 1 ;
}
EEV_adonotcare = 0 ;
} else if ( EEV_cur_pos > 0 ) {
if ( T_EEV_overheating < ( T_EEV_setpoint - EEV_EMERG_DIFF ) ) { //emerg!
2019-04-16 17:38:55 +00:00
PrintS_and_D ( " EEV: emergency closing! " ) ; //!!!
2019-04-16 15:53:34 +00:00
EEV_apulses = - EEV_EMERG_STEPS ;
EEV_adonotcare = 0 ;
EEV_fast = 1 ;
} else if ( T_EEV_overheating < T_EEV_setpoint ) { //too
2019-04-16 17:38:55 +00:00
PrintS_and_D ( " EEV: closing " ) ; //!!!
2019-04-16 15:53:34 +00:00
//EEV_apulses = -EEV_NONPRECISE_STEPS;
EEV_apulses = - 1 ;
EEV_adonotcare = 0 ;
EEV_fast = 0 ;
} else if ( T_EEV_overheating > T_EEV_setpoint + EEV_HYSTERESIS + EEV_PRECISE_START ) { //very
2019-04-16 17:38:55 +00:00
PrintS_and_D ( " EEV: fast opening " ) ; //!!!
2019-04-16 15:53:34 +00:00
//EEV_apulses = +EEV_NONPRECISE_STEPS;
EEV_apulses = + 1 ;
EEV_adonotcare = 0 ;
EEV_fast = 1 ;
} else if ( T_EEV_overheating > T_EEV_setpoint + EEV_HYSTERESIS ) { //too
2019-04-16 17:38:55 +00:00
PrintS_and_D ( " EEV: opening " ) ; //!!!
2019-04-16 15:53:34 +00:00
EEV_apulses = + 1 ;
EEV_adonotcare = 0 ;
EEV_fast = 0 ;
} else if ( T_EEV_overheating > T_EEV_setpoint ) { //ok
2019-04-16 17:38:55 +00:00
PrintS_and_D ( " EEV: OK " ) ; //!!!
2019-04-16 15:53:34 +00:00
//
}
}
off_EEV ( ) ;
}
}
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 ( ) ;
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 ;
}
digitalWrite ( RELAY_SUMP_HEATER , sump_heater_state ) ;
2019-04-01 03:02:55 +00:00
}
2019-04-16 15:53:34 +00:00
//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 ;
2019-04-01 03:02:55 +00:00
} else {
2019-04-16 15:53:34 +00:00
_1st_start_sleeped = 1 ;
2019-04-01 03:02:55 +00:00
}
}
2019-04-16 15:53:34 +00:00
//process_heatpump:
//start if
// (last_on > N or not_started_yet)
// and (no errors)
// and (t hot out < t target + heat_delta_min)
// and (sump t > min'C)
// and (sump t < max'C)
// and (t watertank < target)
// and (t after evaporator > after evaporator min)
// and (t cold in > cold min)
// and (t cold out > cold min)
if ( heatpump_state = = 0 & &
( ( ( unsigned long ) ( millis_now - millis_last_heatpump_on ) > mincycle_poweroff ) | | ( millis_last_heatpump_on = = 0 ) ) & &
//( tr_hot_out < (tr_sens_1 + cT_heat_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 ) ) ) {
PrintS_and_D ( F ( " Start " ) ) ;
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 ) ) {
PrintS_and_D ( F ( " Normal stop " ) ) ;
millis_last_heatpump_on = millis_now ;
heatpump_state = 0 ;
}
2019-04-01 03:02:55 +00:00
2019-04-16 15:53:34 +00:00
//process_hot_side_pump:
//start if (heatpump_enabled)
//stop if (heatpump_disabled and (t hot out or in < t target + heat delta min) )
if ( ( heatpump_state = = 1 ) & & ( hotside_circle_state = = 0 ) ) {
2019-04-01 03:02:55 +00:00
PrintS_and_D ( F ( " Hot WP ON " ) ) ;
hotside_circle_state = 1 ;
}
2019-04-16 15:53:34 +00:00
if ( ( heatpump_state = = 0 ) & & ( hotside_circle_state = = 1 ) ) {
if ( ( deffered_stop_hotcircle ! = 0 & & ( ( unsigned long ) ( millis_now - millis_last_heatpump_on ) > deffered_stop_hotcircle ) ) ) {
if ( ( Tho . e = = 1 & & Tho . T < ( Ttarget . T + cT_heat_delta_min ) ) | |
( Thi . e = = 1 & & Thi . T < ( Ttarget . T + cT_heat_delta_min ) ) ) {
PrintS_and_D ( F ( " Hot WP OFF 1 " ) ) ;
hotside_circle_state = 0 ;
} else {
PrintS_and_D ( F ( " Hot WP OFF 2 " ) ) ;
hotside_circle_state = 0 ;
}
}
2019-04-01 03:02:55 +00:00
}
2019-04-16 15:53:34 +00:00
//heat if we can, just in case, ex. if lost power
if ( ( hotside_circle_state = = 0 ) & &
( Tho . e = = 1 & & Tho . T > ( Ttarget . T + cT_heat_delta_min ) ) | |
( Thi . e = = 1 & & Thi . T > ( Ttarget . T + cT_heat_delta_min ) ) ) {
PrintS_and_D ( F ( " Hot WP ON " ) ) ;
hotside_circle_state = 1 ;
}
//process_cold_side_pump:
//start if (heatpump_enabled)
//stop if (heatpump_disbled)
if ( ( heatpump_state = = 1 ) & & ( coldside_circle_state = = 0 ) ) {
PrintS_and_D ( F ( " Cold WP ON " ) ) ;
coldside_circle_state = 1 ;
}
if ( ( heatpump_state = = 0 ) & & ( coldside_circle_state = = 1 ) ) {
PrintS_and_D ( F ( " Cold WP OFF " ) ) ;
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 ) ) ) {
PrintS_and_D ( F ( " Protective stop " ) ) ;
millis_last_heatpump_on = millis_now ;
heatpump_state = 0 ;
digitalWrite ( RELAY_HEATPUMP , heatpump_state ) ;
}
//alive_check_cycle_after_5_mins:
//error if
// 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 ;
}
if ( ( errorcode = = ERR_OK ) & & ( async_wattage < c_workingOK_wattage_min ) ) {
errorcode = ERR_WATTAGE ;
}
} */
//disable pump by error
if ( errorcode ! = ERR_OK ) {
millis_last_heatpump_on = millis_now ;
heatpump_state = 0 ;
digitalWrite ( RELAY_HEATPUMP , heatpump_state ) ;
//PrintS_and_D("Error stop: " + String(errorcode, HEX));
}
//!!! self-test
///heatpump_state = 1;
//write states to relays
digitalWrite ( RELAY_HEATPUMP , heatpump_state ) ;
digitalWrite ( RELAY_HOTSIDE_CIRCLE , hotside_circle_state ) ;
digitalWrite ( RELAY_COLDSIDE_CIRCLE , coldside_circle_state ) ;
digitalWrite ( RELAY_SUMP_HEATER , sump_heater_state ) ;
# endif
2019-04-01 03:02:55 +00:00
}
if ( RS485Serial . available ( ) > 0 ) {
//RS485Serial.println("some on serial.."); //!!!debug
# ifdef RS485_HUMAN
if ( RS485Serial . available ( ) ) {
inChar = RS485Serial . read ( ) ;
//RS485Serial.print(inChar); //!!!debug
if ( inChar = = 0x1B ) {
skipchars + = 3 ;
inChar = 0x00 ;
millis_escinput = millis ( ) ;
}
if ( skipchars ! = 0 ) {
millis_charinput = millis ( ) ;
//if (millis_escinput + 2 > millis_charinput)
if ( ( unsigned long ) ( millis_charinput - millis_escinput ) < 16 * 2 ) { //2 chars for 2400
if ( inChar ! = 0x7e ) {
skipchars - = 1 ;
}
if ( inChar = = 0x7e ) {
skipchars = 0 ;
}
if ( inChar > = 0x30 & & inChar < = 0x35 ) {
skipchars + = 1 ;
}
inChar = 0x00 ;
} else {
skipchars = 0 ;
}
}
//- RS485_HUMAN: remote commands +,-,G,0x20/?/Enter
switch ( inChar ) {
case 0x00 :
break ;
case 0x20 :
_PrintHelp ( ) ;
break ;
case 0x3F :
_PrintHelp ( ) ;
break ;
case 0x0D :
_PrintHelp ( ) ;
break ;
case 0x2B :
Inc_T ( ) ;
break ;
case 0x2D :
Dec_T ( ) ;
break ;
2019-04-16 15:53:34 +00:00
case 0x3C :
Dec_E ( ) ;
break ;
case 0x3E :
Inc_E ( ) ;
break ;
2019-04-01 03:02:55 +00:00
case 0x47 :
PrintStats_Serial ( ) ;
break ;
case 0x67 :
PrintStats_Serial ( ) ;
break ;
}
}
# endif
# ifdef RS485_PYTHON
index = 0 ;
while ( RS485Serial . available ( ) > 0 ) { // Don't read unless you know there is data
if ( index < 49 ) { // size of the array minus 1
inChar = RS485Serial . read ( ) ; // Read a character
inData [ index ] = inChar ; // Store it
index + + ; // Increment where to write next
inData [ index ] = ' \0 ' ; // clear next symbol, null terminate the string
delayMicroseconds ( 80 ) ; //80 microseconds - the best choice at 9600, "no answer"disappeared
//40(20??) microseconds seems to be good, 9600, 49 symbols
//
} else { //too long message! read it to nowhere
inChar = RS485Serial . read ( ) ;
delayMicroseconds ( 80 ) ;
//break; //do not break if symbols!!
}
}
//!!!debug, be carefull, can cause strange results
2019-04-16 15:53:34 +00:00
/*
if ( inData [ 0 ] ! = 0x00 ) {
2019-04-01 03:02:55 +00:00
RS485Serial . println ( " - " ) ;
RS485Serial . println ( inData ) ;
RS485Serial . println ( " - " ) ;
} */
//or this debug
/*
digitalWrite ( SerialTxControl , RS485Transmit ) ;
delay ( 10 ) ;
RS485Serial . println ( inData ) ;
RS485Serial . flush ( ) ;
RS485Serial . println ( index ) ;
*/
//ALL lines must be terminated with \n!
if ( ( inData [ 0 ] = = hostID ) & & ( inData [ 1 ] = = devID ) ) {
// COMMANDS:
// G (0x47): (G)et main data
// TNN.NN (0x54): set aim (T)emperature
digitalWrite ( SerialTxControl , RS485Transmit ) ;
delay ( 1 ) ;
//PrintS_and_D(freeMemory());
outString = " " ;
outString + = devID ;
outString + = hostID ;
outString + = " A " ; //where A is Answer, space after header
if ( ( inData [ 2 ] = = 0x47 ) ) {
//PrintS_and_D("G");
//WARNING: this procedure can cause "NO answer" effect if no or few T sensors connected
outString + = " { " ;
2019-04-16 15:53:34 +00:00
outString + = " \" E1 \" : " + String ( errorcode ) ;
2019-04-01 03:02:55 +00:00
if ( Ts1 . e = = 1 ) {
2019-04-16 15:53:34 +00:00
outString + = " , \" TS1 \" : " + String ( Ts1 . T ) ;
2019-04-01 03:02:55 +00:00
}
if ( Tsump . e = = 1 ) {
outString + = " , \" TS \" : " + String ( Tsump . T ) ;
}
if ( Tho . e = = 1 ) {
outString + = " , \" THO \" : " + String ( Tho . T ) ;
}
if ( Tae . e = = 1 ) {
outString + = " , \" TAE \" : " + String ( Tae . T ) ;
}
char * outChar = & outString [ 0 ] ;
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 ) ;
2019-04-16 15:53:34 +00:00
# ifndef EEV_ONLY
outString + = " \" A1 \" : " + String ( T_setpoint ) ; //(A)im (target)
outString + = " , \" RP \" : " + String ( heatpump_state * RELAY_HEATPUMP ) ;
# endif
2019-04-01 03:02:55 +00:00
if ( Tci . e = = 1 ) {
outString + = " , \" TCI \" : " + String ( Tci . T ) ;
}
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 ) ;
}
2019-04-16 15:53:34 +00:00
# ifndef EEV_ONLY
outString + = " , \" RSH \" : " + String ( sump_heater_state * RELAY_SUMP_HEATER ) ;
outString + = " , \" RH \" : " + String ( hotside_circle_state * RELAY_HOTSIDE_CIRCLE ) ;
outString + = " , \" RC \" : " + String ( coldside_circle_state * RELAY_COLDSIDE_CIRCLE ) ;
# endif
2019-04-01 03:02:55 +00:00
if ( Tbc . e = = 1 ) {
outString + = " , \" TBC \" : " + String ( Tbc . T ) ;
}
RS485Serial . write ( outChar ) ; //dirty hack to transfer long string
RS485Serial . flush ( ) ;
delay ( 1 ) ; //lot of errors without delay
outString = " " ;
if ( Ts2 . e = = 1 ) {
outString + = " , \" TS2 \" : " + String ( Ts2 . T ) ;
}
if ( Tac . e = = 1 ) {
outString + = " , \" TAC \" : " + String ( Tac . T ) ;
}
2019-04-16 15:53:34 +00:00
if ( Ttarget . e = = 1 ) {
outString + = " , \" TT \" : " + String ( Ttarget . T ) ;
}
# ifdef EEV_SUPPORT
outString + = " , \" EEVP \" : " + String ( EEV_cur_pos ) ;
outString + = " , \" EEVA \" : " + String ( T_EEV_setpoint ) ;
# endif
2019-04-01 03:02:55 +00:00
outString + = " } " ;
2019-04-16 15:53:34 +00:00
} else if ( ( inData [ 2 ] = = 0x54 ) | | ( inData [ 2 ] = = 0x45 ) ) { //(T)arget or (E)EV target format NN.NN, text
2019-04-01 03:02:55 +00:00
if ( isDigit ( inData [ 3 ] ) & & isDigit ( inData [ 4 ] ) & & ( inData [ 5 ] = = 0x2e ) & & isDigit ( inData [ 6 ] ) & & isDigit ( inData [ 7 ] ) & & ( ! isDigit ( inData [ 8 ] ) ) ) {
tone ( speakerOut , 2250 ) ;
delay ( 100 ) ; // like ups power on
noTone ( speakerOut ) ;
char * carray = & inData [ 3 ] ;
tempdouble = atof ( carray ) ;
2019-04-16 15:53:34 +00:00
if ( inData [ 2 ] = = 0x54 ) {
if ( tempdouble > cT_setpoint_max ) {
outString + = " { \" err \" : \" too hot! \" } " ;
} else if ( tempdouble < 1.0 ) {
outString + = " { \" err \" : \" too cold! \" } " ;
} else {
T_setpoint = tempdouble ;
outString + = " { \" result \" : \" ok, new value is: " ;
outString + = String ( T_setpoint ) ;
outString + = " \" } " ;
}
}
if ( inData [ 2 ] = = 0x45 ) {
if ( tempdouble > 10.0 ) { //!!!!!!! hardcode !!!
outString + = " { \" err \" : \" too hot! \" } " ;
} else if ( tempdouble < 0.1 ) { //!!!!!!! hardcode !!!
outString + = " { \" err \" : \" too cold! \" } " ;
} else {
T_EEV_setpoint = tempdouble ;
outString + = " { \" result \" : \" ok, new EEV value is: " ;
outString + = String ( T_EEV_setpoint ) ;
outString + = " \" } " ;
}
2019-04-01 03:02:55 +00:00
}
} else {
outString + = " { \" err \" : \" NaN, format: NN.NN \" } " ;
}
} else {
//default, just for example
outString + = " { \" result \" : \" 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
}
}