Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
desbiensl
Published © GPL3+

HVAC Controller Retrofit

An electric furnace-air handler, arduino-based, controller.

AdvancedShowcase (no instructions)13,540
HVAC Controller Retrofit

Things used in this project

Hardware components

Arduino Leonardo
Arduino Leonardo
×1

Software apps and online services

UnoArduSim
Everycircuit
Fritzing

Story

Read more

Schematics

schematic

24VAC interfacing

how to interface 24VAC to Arduino

Code

HVAC Ctrl 1.0 by LD design (revision 2.5)

Arduino
You need a leonardo board and my shield for it to actually do something
// HVAC Ctrl 1.0, by LD design
// revision 2.5, 2019-02-10

// Copyright 2018, Louis Desbiens, All rights reserved.


#include <avr/pgmspace.h>




//
//
//
//
// beginning of global variables and constant declarations
//
//
//
//






// digital addresses

// outputs

const byte LED_ctrl = 13;      // LED control pin address
const byte H1_ctrl = 12;       // Heat strip #1, relay pin address
const byte H2_ctrl = 11;       // Heat strip #2, relay  pin address
const byte H3_ctrl = 10;       // Heat strip #3, relay pin address
const byte H4_ctrl = 9;        // Heat strip #4, relay pin address
const byte BLO_ctrl = 8;       // Blower low speed, relay pin address
const byte BMED_ctrl = 7;      // Blower medium speed, relay pin address
const byte BHI_ctrl = 6;       // Blower high speed, relay pin address
const byte BSSR_ctrl = 5;      // Blower circuit, solid-state relay pin address

// inputs

const byte G = 3;              // Thermostat G line status address (blower)
const byte W1 = 2;             // Thermostat W1 line status address (heat stage 1)
const byte W2 = 1;             // Thermostat W2 line status address (heat stage 2)
const byte W3 = 0;             // Thermostat W3 line status address (heat stage 3), connected to heat pump Y in the current implementation to override the fan speed algorithm


// analog input signal, addresses

const byte TH1 = 0;           // Thermistor 1 address
const byte TH2 = 6;           // Thermistor 2 address
const byte CSFAN = 1;         // Fan current sense address
const byte CSH1 = 5;          // Heat strip #1 current sense address
const byte CSH2 = 4;          // Heat strip #2 current sense address
const byte CSH3 = 3;          // Heat strip #3 current sense address
const byte CSH4 = 2;          // Heat strip #4 current sense address


// constant values

const byte nb_of_heating_elements = 4;                           // nb of heating elements
const byte CS_vector_size = 144;                                 // current sense vector size
const int delta_H = 1000;                                        // minimal delay (ms) between two subsequent activation / dactivation of heat strip relays
const int delta_G0 = 5000;                                       // minimal delay (ms), after a blower power-up at the lowest speed, before engaging the speed adjustment algorithm
const int delta_G = 1000;                                        // minimal delay (ms), between two subsequent blower speed change
const int delta_W1_level_1 = 5;                                  // minimal delay (s) before engaging the second heat strip on a W1 heat request, (2 x 5 kW capacity)
const int delta_W2_level_1 = 5;                                  // minimal delay (s) before engaging the second heat strip on a W2 heat request, (2 x 5 kW capacity)
const int delai_SSR = 10;                                        // minimal delay (ms) before adjusting the blower speed selection relays after opening the circuit with the SSR relay
const int delai_relay = 15;                                      // minimal delay (ms) after adjustment the blower speed selection relays before closing the circuit with the SSR relay
const int delta_T_LO_up = 1000;                                  // minimum temp for going from LO to MED in 1/100 of C (hysteresis)
const int delta_T_MED_up = 1500;                                 // minimum temp for going from MED to HI in 1/100 of C (hysteresis)
const int delta_T_HI_up = 2000;                                  // minimum temp for going from HI to VHI in 1/100 of C (hysteresis)
const int delta_T_MED_down = 900;                                // minimum temp for going from MED to LO in 1/100 of C (hysteresis)
const int delta_T_HI_down = 1400;                                // minimum temp for going from HI to MED in 1/100 of C (hysteresis)
const int delta_T_VHI_down = 1900;                               // minimum temp for going from VHIGH to HI in 1/100 of C (hysteresis)
const int H_current_conversion_1 = 35;                           // current sense conversion constant (30A model, heat strip)
const int B_current_conversion_1 = 210;                          // current sense conversion constant (5A model, blower)
const int HB_current_conversion_2 = 10000;                       // current sense conversion constant
const byte nb_pts_period = 4;                                    // nb of points per 60Hz cylce for the current sense evaluation
const int current_FAN_min[] = {0, 1300, 1400, 1600, 1900};       // blower current sense minimal threshold (mA) for OFF, LO, MED, HIGH, VHIGH states (PRELIMINARY)
const int current_FAN_max[] = {500, 2200, 2400, 2700, 3200};     // blower current sense maximal threshold (mA) for OFF, LO, MED, HIGH, VHIGH states (PRELIMINARY)
const int current_H_min[] = {0, 15000};                          // heat strip current sense minimal threshold (mA) for OFF, ON states (PRELIMINARY)
const int current_H_max[] = {1000, 25000};                       // heat strip current sense maximal threshold (mA) for OFF, ON states (PRELIMINARY)
const int USB_comm_period = 2000;                                // periodic delay (ms) between checks whether a USB connection is present


// constants stored in the flash (instead of SRAM)

const PROGMEM int16_t thermistor_conversion[] = {32767, 32767, 29765, 26554, 24485, 22984, 21820, 20876, 20087, 19411, 18822, 18301, 17836, 17415, 17032, 16681, 16358, 16058, 15779, 15518, 15273, 15042, 14824, 14618, 14422, 14235, 14058, 13888, 13726, 13570, 13421, 13277, 13139, 13006, 12877, 12753, 12633, 12517, 12404, 12295, 12189, 12086, 11987, 11889, 11795, 11703, 11613, 11525, 11440, 11357, 11275, 11196, 11118, 11042, 10967, 10895, 10823, 10753, 10685, 10618, 10552, 10487, 10424, 10362, 10300, 10240, 10181, 10123, 10066, 10010, 9955, 9901, 9847, 9795, 9743, 9692, 9642, 9592, 9543, 9495, 9448, 9401, 9355, 9309, 9265, 9220, 9177, 9133, 9091, 9049, 9007, 8966, 8926, 8885, 8846, 8807, 8768, 8730, 8692, 8655, 8618, 8581, 8545, 8509, 8474, 8439, 8404, 8370, 8336, 8302, 8269, 8236, 8203, 8171, 8139, 8107, 8076, 8044, 8014, 7983, 7953, 7923, 7893, 7863, 7834, 7805, 7776, 7748, 7720, 7691, 7664, 7636, 7609, 7582, 7555, 7528, 7501, 7475, 7449, 7423, 7397, 7372, 7346, 7321, 7296, 7272, 7247, 7223, 7198, 7174, 7150, 7126, 7103, 7079, 7056, 7033, 7010, 6987, 6965, 6942, 6920, 6897, 6875, 6853, 6832, 6810, 6788, 6767, 6746, 6724, 6703, 6682, 6662, 6641, 6620, 6600, 6580, 6560, 6539, 6519, 6500, 6480, 6460, 6441, 6421, 6402, 6383, 6364, 6345, 6326, 6307, 6288, 6269, 6251, 6232, 6214, 6196, 6178, 6160, 6142, 6124, 6106, 6088, 6071, 6053, 6036, 6018, 6001, 5984, 5966, 5949, 5932, 5915, 5899, 5882, 5865, 5849, 5832, 5815, 5799, 5783, 5766, 5750, 5734, 5718, 5702, 5686, 5670, 5655, 5639, 5623, 5608, 5592, 5577, 5561, 5546, 5530, 5515, 5500, 5485, 5470, 5455, 5440, 5425, 5410, 5395, 5381, 5366, 5351, 5337, 5322, 5308, 5293, 5279, 5265, 5250, 5236, 5222, 5208, 5194, 5180, 5166, 5152, 5138, 5124, 5111, 5097, 5083, 5069, 5056, 5042, 5029, 5015, 5002, 4989, 4975, 4962, 4949, 4935, 4922, 4909, 4896, 4883, 4870, 4857, 4844, 4831, 4818, 4805, 4793, 4780, 4767, 4754, 4742, 4729, 4717, 4704, 4692, 4679, 4667, 4654, 4642, 4630, 4617, 4605, 4593, 4581, 4568, 4556, 4544, 4532, 4520, 4508, 4496, 4484, 4472, 4460, 4448, 4437, 4425, 4413, 4401, 4389, 4378, 4366, 4354, 4343, 4331, 4320, 4308, 4297, 4285, 4274, 4262, 4251, 4240, 4228, 4217, 4206, 4194, 4183, 4172, 4161, 4150, 4138, 4127, 4116, 4105, 4094, 4083, 4072, 4061, 4050, 4039, 4028, 4017, 4006, 3996, 3985, 3974, 3963, 3952, 3942, 3931, 3920, 3910, 3899, 3888, 3878, 3867, 3857, 3846, 3835, 3825, 3814, 3804, 3793, 3783, 3773, 3762, 3752, 3741, 3731, 3721, 3710, 3700, 3690, 3680, 3669, 3659, 3649, 3639, 3628, 3618, 3608, 3598, 3588, 3578, 3568, 3558, 3548, 3538, 3527, 3517, 3507, 3498, 3488, 3478, 3468, 3458, 3448, 3438, 3428, 3418, 3408, 3399, 3389, 3379, 3369, 3359, 3350, 3340, 3330, 3320, 3311, 3301, 3291, 3282, 3272, 3262, 3253, 3243, 3233, 3224, 3214, 3205, 3195, 3186, 3176, 3166, 3157, 3147, 3138, 3128, 3119, 3109, 3100, 3091, 3081, 3072, 3062, 3053, 3044, 3034, 3025, 3015, 3006, 2997, 2987, 2978, 2969, 2959, 2950, 2941, 2932, 2922, 2913, 2904, 2895, 2885, 2876, 2867, 2858, 2849, 2839, 2830, 2821, 2812, 2803, 2793, 2784, 2775, 2766, 2757, 2748, 2739, 2730, 2721, 2712, 2702, 2693, 2684, 2675, 2666, 2657, 2648, 2639, 2630, 2621, 2612, 2603, 2594, 2585, 2576, 2567, 2558, 2549, 2540, 2531, 2522, 2513, 2504, 2496, 2487, 2478, 2469, 2460, 2451, 2442, 2433, 2424, 2415, 2407, 2398, 2389, 2380, 2371, 2362, 2353, 2344, 2336, 2327, 2318, 2309, 2300, 2291, 2283, 2274, 2265, 2256, 2247, 2239, 2230, 2221, 2212, 2203, 2195, 2186, 2177, 2168, 2159, 2151, 2142, 2133, 2124, 2116, 2107, 2098, 2089, 2081, 2072, 2063, 2054, 2046, 2037, 2028, 2019, 2011, 2002, 1993, 1984, 1976, 1967, 1958, 1949, 1941, 1932, 1923, 1914, 1906, 1897, 1888, 1880, 1871, 1862, 1853, 1845, 1836, 1827, 1818, 1810, 1801, 1792, 1784, 1775, 1766, 1757, 1749, 1740, 1731, 1722, 1714, 1705, 1696, 1687, 1679, 1670, 1661, 1653, 1644, 1635, 1626, 1618, 1609, 1600, 1591, 1583, 1574, 1565, 1556, 1548, 1539, 1530, 1521, 1512, 1504, 1495, 1486, 1477, 1469, 1460, 1451, 1442, 1433, 1425, 1416, 1407, 1398, 1389, 1381, 1372, 1363, 1354, 1345, 1336, 1328, 1319, 1310, 1301, 1292, 1283, 1274, 1266, 1257, 1248, 1239, 1230, 1221, 1212, 1203, 1194, 1185, 1176, 1168, 1159, 1150, 1141, 1132, 1123, 1114, 1105, 1096, 1087, 1078, 1069, 1060, 1051, 1042, 1033, 1024, 1015, 1006, 997, 988, 979, 969, 960, 951, 942, 933, 924, 915, 906, 897, 887, 878, 869, 860, 851, 842, 832, 823, 814, 805, 795, 786, 777, 768, 758, 749, 740, 730, 721, 712, 702, 693, 684, 674, 665, 655, 646, 637, 627, 618, 608, 599, 589, 580, 570, 561, 551, 542, 532, 523, 513, 503, 494, 484, 475, 465, 455, 445, 436, 426, 416, 407, 397, 387, 377, 367, 358, 348, 338, 328, 318, 308, 298, 288, 278, 268, 259, 249, 238, 228, 218, 208, 198, 188, 178, 168, 158, 147, 137, 127, 117, 106, 96, 86, 76, 65, 55, 44, 34, 24, 13, 3, -8, -18, -29, -40, -50, -61, -71, -82, -93, -104, -114, -125, -136, -147, -158, -168, -179, -190, -201, -212, -223, -234, -245, -257, -268, -279, -290, -301, -312, -324, -335, -346, -358, -369, -381, -392, -404, -415, -427, -438, -450, -462, -473, -485, -497, -509, -521, -532, -544, -556, -568, -580, -592, -605, -617, -629, -641, -654, -666, -678, -691, -703, -716, -728, -741, -753, -766, -779, -792, -804, -817, -830, -843, -856, -869, -883, -896, -909, -922, -936, -949, -963, -976, -990, -1003, -1017, -1031, -1045, -1058, -1072, -1086, -1100, -1115, -1129, -1143, -1157, -1172, -1186, -1201, -1216, -1230, -1245, -1260, -1275, -1290, -1305, -1320, -1335, -1351, -1366, -1381, -1397, -1413, -1428, -1444, -1460, -1476, -1492, -1509, -1525, -1541, -1558, -1575, -1591, -1608, -1625, -1642, -1659, -1677, -1694, -1711, -1729, -1747, -1765, -1783, -1801, -1819, -1838, -1856, -1875, -1894, -1913, -1932, -1951, -1971, -1990, -2010, -2030, -2050, -2070, -2091, -2111, -2132, -2153, -2174, -2196, -2217, -2239, -2261, -2283, -2306, -2328, -2351, -2375, -2398, -2422, -2446, -2470, -2494, -2519, -2544, -2569, -2595, -2621, -2647, -2674, -2701, -2728, -2756, -2784, -2812, -2841, -2870, -2900, -2930, -2961, -2992, -3024, -3056, -3089, -3122, -3156, -3190, -3225, -3261, -3297, -3334, -3372, -3411, -3451, -3491, -3533, -3575, -3618, -3663, -3708, -3755, -3803, -3853, -3904, -3956, -4010, -4066, -4124, -4184, -4247, -4311, -4379, -4449, -4523, -4600, -4682, -4768, -4859, -4956, -5059, -5171, -5291, -5423, -5568, -5731, -5915, -6128, -6384, -6704, -7138, -7839, -32768}; // use this form
const PROGMEM uint16_t current_sensing_conversion[] = {64532, 64280, 64029, 63778, 63527, 63277, 63027, 62778, 62530, 62281, 62034, 61787, 61540, 61294, 61048, 60803, 60558, 60314, 60070, 59827, 59585, 59342, 59101, 58859, 58619, 58378, 58139, 57899, 57661, 57422, 57185, 56947, 56711, 56474, 56238, 56003, 55768, 55534, 55300, 55067, 54834, 54602, 54370, 54138, 53908, 53677, 53447, 53218, 52989, 52761, 52533, 52305, 52078, 51852, 51626, 51401, 51176, 50951, 50727, 50504, 50281, 50058, 49836, 49615, 49394, 49173, 48953, 48734, 48515, 48296, 48078, 47861, 47644, 47427, 47211, 46995, 46780, 46566, 46352, 46138, 45925, 45712, 45500, 45289, 45077, 44867, 44657, 44447, 44238, 44029, 43821, 43613, 43406, 43199, 42993, 42787, 42582, 42378, 42173, 41970, 41766, 41564, 41361, 41160, 40958, 40758, 40557, 40357, 40158, 39959, 39761, 39563, 39366, 39169, 38973, 38777, 38582, 38387, 38192, 37998, 37805, 37612, 37420, 37228, 37036, 36846, 36655, 36465, 36276, 36087, 35898, 35710, 35523, 35336, 35150, 34964, 34778, 34593, 34409, 34225, 34041, 33858, 33676, 33494, 33312, 33131, 32950, 32770, 32591, 32412, 32233, 32055, 31877, 31700, 31524, 31348, 31172, 30997, 30822, 30648, 30475, 30301, 30129, 29957, 29785, 29614, 29443, 29273, 29103, 28934, 28765, 28597, 28429, 28262, 28095, 27929, 27763, 27598, 27433, 27269, 27105, 26942, 26779, 26617, 26455, 26294, 26133, 25973, 25813, 25653, 25495, 25336, 25178, 25021, 24864, 24708, 24552, 24397, 24242, 24087, 23933, 23780, 23627, 23475, 23323, 23171, 23020, 22870, 22720, 22570, 22421, 22273, 22125, 21977, 21830, 21684, 21538, 21392, 21247, 21103, 20959, 20815, 20672, 20529, 20387, 20246, 20105, 19964, 19824, 19684, 19545, 19407, 19269, 19131, 18994, 18857, 18721, 18585, 18450, 18316, 18181, 18048, 17914, 17782, 17650, 17518, 17387, 17256, 17126, 16996, 16867, 16738, 16610, 16482, 16355, 16228, 16102, 15976, 15850, 15726, 15601, 15478, 15354, 15231, 15109, 14987, 14866, 14745, 14625, 14505, 14385, 14266, 14148, 14030, 13913, 13796, 13679, 13564, 13448, 13333, 13219, 13105, 12991, 12878, 12766, 12654, 12542, 12431, 12321, 12211, 12101, 11992, 11884, 11776, 11668, 11561, 11455, 11349, 11243, 11138, 11033, 10929, 10826, 10723, 10620, 10518, 10416, 10315, 10214, 10114, 10015, 9916, 9817, 9719, 9621, 9524, 9427, 9331, 9235, 9140, 9045, 8951, 8857, 8764, 8671, 8579, 8487, 8396, 8305, 8215, 8125, 8036, 7947, 7859, 7771, 7684, 7597, 7511, 7425, 7339, 7255, 7170, 7086, 7003, 6920, 6838, 6756, 6674, 6594, 6513, 6433, 6354, 6275, 6196, 6119, 6041, 5964, 5888, 5812, 5736, 5661, 5587, 5513, 5439, 5366, 5294, 5222, 5150, 5079, 5009, 4939, 4869, 4800, 4731, 4663, 4596, 4529, 4462, 4396, 4330, 4265, 4201, 4136, 4073, 4010, 3947, 3885, 3823, 3762, 3701, 3641, 3581, 3522, 3464, 3405, 3348, 3290, 3234, 3177, 3122, 3066, 3012, 2957, 2904, 2850, 2798, 2745, 2694, 2642, 2591, 2541, 2491, 2442, 2393, 2345, 2297, 2250, 2203, 2156, 2110, 2065, 2020, 1976, 1932, 1888, 1846, 1803, 1761, 1720, 1679, 1638, 1598, 1559, 1520, 1481, 1443, 1406, 1369, 1332, 1296, 1261, 1226, 1191, 1157, 1124, 1091, 1058, 1026, 995, 963, 933, 903, 873, 844, 815, 787, 760, 733, 706, 680, 654, 629, 604, 580, 557, 533, 511, 488, 467, 446, 425, 405, 385, 366, 347, 329, 311, 294, 277, 261, 245, 229, 215, 200, 187, 173, 160, 148, 136, 125, 114, 104, 94, 84, 76, 67, 59, 52, 45, 39, 33, 27, 22, 18, 14, 10, 7, 5, 3, 2, 1, 0, 0, 1, 2, 3, 5, 7, 10, 14, 18, 22, 27, 33, 39, 45, 52, 59, 67, 76, 84, 94, 104, 114, 125, 136, 148, 160, 173, 187, 200, 215, 229, 245, 261, 277, 294, 311, 329, 347, 366, 385, 405, 425, 446, 467, 488, 511, 533, 557, 580, 604, 629, 654, 680, 706, 733, 760, 787, 815, 844, 873, 903, 933, 963, 995, 1026, 1058, 1091, 1124, 1157, 1191, 1226, 1261, 1296, 1332, 1369, 1406, 1443, 1481, 1520, 1559, 1598, 1638, 1679, 1720, 1761, 1803, 1846, 1888, 1932, 1976, 2020, 2065, 2110, 2156, 2203, 2250, 2297, 2345, 2393, 2442, 2491, 2541, 2591, 2642, 2694, 2745, 2798, 2850, 2904, 2957, 3012, 3066, 3122, 3177, 3234, 3290, 3348, 3405, 3464, 3522, 3581, 3641, 3701, 3762, 3823, 3885, 3947, 4010, 4073, 4136, 4201, 4265, 4330, 4396, 4462, 4529, 4596, 4663, 4731, 4800, 4869, 4939, 5009, 5079, 5150, 5222, 5294, 5366, 5439, 5513, 5587, 5661, 5736, 5812, 5888, 5964, 6041, 6119, 6196, 6275, 6354, 6433, 6513, 6594, 6674, 6756, 6838, 6920, 7003, 7086, 7170, 7255, 7339, 7425, 7511, 7597, 7684, 7771, 7859, 7947, 8036, 8125, 8215, 8305, 8396, 8487, 8579, 8671, 8764, 8857, 8951, 9045, 9140, 9235, 9331, 9427, 9524, 9621, 9719, 9817, 9916, 10015, 10114, 10214, 10315, 10416, 10518, 10620, 10723, 10826, 10929, 11033, 11138, 11243, 11349, 11455, 11561, 11668, 11776, 11884, 11992, 12101, 12211, 12321, 12431, 12542, 12654, 12766, 12878, 12991, 13105, 13219, 13333, 13448, 13564, 13679, 13796, 13913, 14030, 14148, 14266, 14385, 14505, 14625, 14745, 14866, 14987, 15109, 15231, 15354, 15478, 15601, 15726, 15850, 15976, 16102, 16228, 16355, 16482, 16610, 16738, 16867, 16996, 17126, 17256, 17387, 17518, 17650, 17782, 17914, 18048, 18181, 18316, 18450, 18585, 18721, 18857, 18994, 19131, 19269, 19407, 19545, 19684, 19824, 19964, 20105, 20246, 20387, 20529, 20672, 20815, 20959, 21103, 21247, 21392, 21538, 21684, 21830, 21977, 22125, 22273, 22421, 22570, 22720, 22870, 23020, 23171, 23323, 23475, 23627, 23780, 23933, 24087, 24242, 24397, 24552, 24708, 24864, 25021, 25178, 25336, 25495, 25653, 25813, 25973, 26133, 26294, 26455, 26617, 26779, 26942, 27105, 27269, 27433, 27598, 27763, 27929, 28095, 28262, 28429, 28597, 28765, 28934, 29103, 29273, 29443, 29614, 29785, 29957, 30129, 30301, 30475, 30648, 30822, 30997, 31172, 31348, 31524, 31700, 31877, 32055, 32233, 32412, 32591, 32770, 32950, 33131, 33312, 33494, 33676, 33858, 34041, 34225, 34409, 34593, 34778, 34964, 35150, 35336, 35523, 35710, 35898, 36087, 36276, 36465, 36655, 36846, 37036, 37228, 37420, 37612, 37805, 37998, 38192, 38387, 38582, 38777, 38973, 39169, 39366, 39563, 39761, 39959, 40158, 40357, 40557, 40758, 40958, 41160, 41361, 41564, 41766, 41970, 42173, 42378, 42582, 42787, 42993, 43199, 43406, 43613, 43821, 44029, 44238, 44447, 44657, 44867, 45077, 45289, 45500, 45712, 45925, 46138, 46352, 46566, 46780, 46995, 47211, 47427, 47644, 47861, 48078, 48296, 48515, 48734, 48953, 49173, 49394, 49615, 49836, 50058, 50281, 50504, 50727, 50951, 51176, 51401, 51626, 51852, 52078, 52305, 52533, 52761, 52989, 53218, 53447, 53677, 53908, 54138, 54370, 54602, 54834, 55067, 55300, 55534, 55768, 56003, 56238, 56474, 56711, 56947, 57185, 57422, 57661, 57899, 58139, 58378, 58619, 58859, 59101, 59342, 59585, 59827, 60070, 60314, 60558, 60803, 61048, 61294, 61540, 61787, 62034, 62281, 62530, 62778, 63027, 63277, 63527, 63778, 64029, 64280, 64532};
const PROGMEM byte LED1[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2};
const PROGMEM byte LED2[] = {0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2};
const PROGMEM byte LED3[] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2};

// thermistor conversion :  the "analogread" value is used as an index, what is stored in the flash are the correspoding 1024 temperatures (in 1/100 C)
// current sense conversion, the instanteous current (mA) is given by :   sqrt(current_sensing_conversion)/H_current_conversion_1*HB_current_conversion_2 for the heat strips and  sqrt(current_sensing_conversion)/B_current_conversion_1*HB_current_conversion_2 for the blower



// working variables

byte current_speed = 0;                                                    // 0 min speed, 1,2,3 max speed (blower, current state)
byte new_speed = 0;                                                        // 0 min speed, 1,2,3 max speed (blower, new state)
byte G_current_status = 0;                                                 // current status of the G line (0=OFF, 1=ON)
byte G_new_status = 0;                                                     // new status of the G line (0=OFF, 1=ON)
byte W1_current_status = 0;                                                // current status of the W1 line (0=OFF, 1=ON)
byte W1_new_status = 0;                                                    // new status of the W1 line (0=OFF, 1=ON)
byte W2_current_status = 0;                                                // current status of the W2 line (0=OFF, 1=ON)
byte W2_new_status = 0;                                                    // new status of the W2 line (0=OFF, 1=ON)
byte W3_current_status = 0;                                                // current status of the W3 line (0=OFF, 1=ON)
byte W3_new_status = 0;                                                    // new status of the W3 line (0=OFF, 1=ON)
int TH1_value = 0;                                                         // Temperature value (1/100 C), thermistor 1
int TH2_value = 0;                                                         // Temperature value (1/100 C), thermistor 2
byte active_elements_W1_W2[5] = {H4_ctrl, H1_ctrl, H2_ctrl, H3_ctrl, H4_ctrl}; // indexes 0 and 1 correspond to W1, indexes 2 and 3 to W2, the addresses of the heat strips are cycled through in the vector each time a new activation of W1 occcurs
byte W1_heating_state = 0;                                                 // heating states associated with W1  (0: no heat strips activates, 1: first heat strip is activated, 2: both heat strips are activated)
byte W2_heating_state = 0;                                                 // heating states associated with W2  (0: no heat strips activates, 1: first heat strip is activated, 2: both heat strips are activated)
byte speed_change_stage = 3;                                               // blower speed change states (0: beginning of speed change sequence, 1: the circuit is open, 2: The speed adjustment has been made, 3: the circuit is closed)
byte fan_activation_state = 0;                                             // blower fan state sequence for shutdown (=0 fan is ready for first step of startup or shutdown sequence, =1 shutdown sequence intermediate step, after SSR has openend the circuit before fan speed goes back to minimal value, =2 fan is off and speed is at low)
byte pointer_CS = 0;                                                       // current sense consist of measuring multiplie instantaneous current value samples (4 per 60 Hz cycle, 36 consecutives cycles, 144 samples in total representing 600 ms) and calculating the RMS value, pointer_CS is the index in the vector current
long correction_us = 0;                                                    // To ensure the current measurement are performed every 4.16 ms, a delay is adjusted each cycle to compensate for the small variations, correction_us is the correction to the fized latenccy in us, reevaluated at each cycle
byte system_status_LED[] = {0, 0, 0, 0, 0, 0, 0, 0};                       //  LED1 LED2 LED3 color   0 =green  1 = yellow  2 = red,   FAN status  H1 H2 H3 H4   0 = OK(green)   1 = UC(yellow)  2 = OC(red)   PRELIMINARY
byte FAN_status = 0;                                                       // FAN_status (0= OFF, 1= LOW, 2= MED, 3= HIGH, 4= VHIGH) to be used as an indicator in the reporting system (USB) and eventually in the LED control PRELIMINARY
byte HEATER_status = 0;                                                    // HEATER_status (0= OFF, 1= W1 1st heat strip, 2= W2 both heat strip, 3= W2 1st heat strip, 4= W2 both heat strip) to be used as an indicator in the reporting system (USB) and eventually in the LED control PRELIMINARY
byte system_status_index = 0;                                              // global value indicating the status in which the system is in, 27 possible states PRELIMINARY
byte USB_connection_status = 0;                                            // indication whether a USB connection is present or not
unsigned int H1_current_vector[CS_vector_size];                            // current sense vector of 144 samples for heat strip #1
unsigned int H2_current_vector[CS_vector_size];                            // current sense vector of 144 samples for heat strip #2
unsigned int H3_current_vector[CS_vector_size];                            // current sense vector of 144 samples for heat strip #3
unsigned int H4_current_vector[CS_vector_size];                            // current sense vector of 144 samples for heat strip #4
unsigned int FAN_current_vector[CS_vector_size];                           // current sense vector of 144 samples for the blower
unsigned long H1_CS_sum = 0;                                               // current sense sum for heat strip #1
unsigned long H2_CS_sum = 0;                                               // current sense sum for heat strip #2
unsigned long H3_CS_sum = 0;                                               // current sense sum for heat strip #3
unsigned long H4_CS_sum = 0;                                               // current sense sum for heat strip #4
unsigned long FAN_CS_sum = 0;                                              // current sense sum for the blower
unsigned int current_sense_H_vector[nb_of_heating_elements];               // actual current value in mA for the heat strips
unsigned int current_sense_FAN = 0;                                        // actual current value in mA for the blower




// time measurements

unsigned long time_H0 = 0;                  // absolute time of the last relay activation
unsigned long time_W1 = 0;                  // absolute time of the last W1 activation
unsigned long time_W2 = 0;                  // absolute time of the last W2 activation
unsigned long time_G0 = 0;                  // absolute time of the last blower activation
unsigned long time_G = 0;                   // absolute time of the last speed change
unsigned long time_SC = 0;                  // absolute time used for counting during the intermediate steps associated with a speed change
unsigned long time_AF = 0;                  // absolute time used for couting during the intermediate steps associated with the activation of the fan
unsigned long time_USB = 0;                 // absolute time used for the delays in the USB connection presence check
unsigned long current_time = 0;             // absolute time to verify, at each cycle whether the time overflow has occured (~ every ~ 50 days)
unsigned long old_current_time = 0;         // previous value of the absolute time to verify whether the time overflow has occured
unsigned long time_old_CS = 0;              // absolute time used in current sense algorithm to synchronize the measurements on a 60 Hz cycle (previous value)
unsigned long time_new_CS = 0;              // absolute time used in current sense algorithm to synchronize the measurements on a 60 Hz cycle (new value)
unsigned int fixed_latency_old = 0;         // latency to be waited at each cycle to stay synchronized with the 60 Hz electrical supply (previous value)
unsigned int fixed_latency_new = 0;         // latency to be waited at each cycle to stay synchronized with the 60 Hz electrical supply (new value)


//
//
//
//
// end of global variables and constant declarations
//
//
//
//


/////////////////////////////////////////////////////////////////////////////
// routine for evaluating whether a speed change of the blower is required
/////////////////////////////////////////////////////////////////////////////

void new_speed_evaluation() {


  // The verification is performed only if we're are not currently in the process of changing the speed
  if (speed_change_stage == 3) {


    // If the heat pump is active (W3) we force the blower to high speed for maximum efficiency
    if (W3_current_status == 1) {
      new_speed = 3;
    }
    else {



      // validation of the current speed and check whether the temperature difference is over or under the thresholds we have defined
      switch (current_speed) {
        case 0:   // LO speed
          if (abs(TH1_value - TH2_value) >= delta_T_LO_up) {
            new_speed = 1;
          }
          break;

        case 1:   // MED speed
          if (abs(TH1_value - TH2_value) >= delta_T_MED_up) {
            new_speed = 2;
          }
          if (abs(TH1_value - TH2_value) < delta_T_MED_down) {
            new_speed = 0;
          }
          break;

        case 2:   // HI speed
          if (abs(TH1_value - TH2_value) >= delta_T_HI_up) {
            new_speed = 3;
          }
          if (abs(TH1_value - TH2_value) < delta_T_HI_down) {
            new_speed = 1;
          }
          break;

        case 3:   // VHI speed
          if (abs(TH1_value - TH2_value) < delta_T_VHI_down) {
            new_speed = 2;
          }
          break;
      }

    }
  }
}

/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for changing the fan speed
/////////////////////////////////////////////////////////////////////////////

void speed_change() {

  if (new_speed != current_speed) {

    // if the speed_change_stage is equal to 3, its a new speed change sequence (and not an ongoing sequence), therefore we start at the beginning (speed_stage_stage=0)
    if (speed_change_stage == 3) {
      speed_change_stage = 0;
    }

  }



  switch (speed_change_stage) {

    // The electrical circuit is openend with the SSR relay
    case 0:

      digitalWrite(BSSR_ctrl, LOW);
      speed_change_stage = 1;
      time_SC = millis();

      break;


    // if the delay after opening the circuit is over the 10 ms minimum, we select the new speed
    case 1:

      if ((long)(current_time - time_SC) >= delai_SSR) {

        switch (new_speed) {
          case 0:   // LOWEST SPEED

            digitalWrite(BHI_ctrl, LOW);
            digitalWrite(BMED_ctrl, LOW);
            digitalWrite(BLO_ctrl, LOW);

            break;
          case 1:  // SECOND LOWEST
            digitalWrite(BHI_ctrl, LOW);
            digitalWrite(BMED_ctrl, LOW);
            digitalWrite(BLO_ctrl, HIGH);
            break;
          case 2:  // SECOND HIGHEST
            digitalWrite(BHI_ctrl, LOW);
            digitalWrite(BMED_ctrl, HIGH);
            digitalWrite(BLO_ctrl, HIGH);
            break;
          case 3:  // HIGHEST
            digitalWrite(BHI_ctrl, HIGH);
            digitalWrite(BMED_ctrl, HIGH);
            digitalWrite(BLO_ctrl, HIGH);
            break;
        }
        speed_change_stage = 2;
        time_SC = millis();


      }
      break;



    // if the delay after selecting the new speed is over the 15 ms minimum (mechanical relay boucing), we close the circuit again with the SSR
    case 2:

      if ((long)(current_time - time_SC) >= delai_relay) {
        digitalWrite(BSSR_ctrl, HIGH);
        current_speed = new_speed;
        time_G = millis();
        speed_change_stage = 3;
        break;


      }
  }
}

/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// temperature reading routine
/////////////////////////////////////////////////////////////////////////////

int read_temp(int pin_adress) {


  int TH_raw_cnt;
  int read_temp_value;

  TH_raw_cnt = analogRead(pin_adress);

  read_temp_value = pgm_read_word(&thermistor_conversion[TH_raw_cnt]);

  return read_temp_value;
}












/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for the activation of the relays associated withe the W1 heat command
/////////////////////////////////////////////////////////////////////////////

void activate_W1() {

  switch (W1_new_status) {

    // W1 heat strips deactivation
    case 0:

      // Validation of which heating state we're in (state 0 should not be an option)
      switch (W1_heating_state) {

        // deactivation of heat strip #1 associated with W1
        case 1:
          digitalWrite(active_elements_W1_W2[0], W1_new_status);
          W1_current_status = W1_new_status;
          W1_heating_state = 0;
          break;

        // deactivation of heat strip #2 associated with W1
        case 2:
          digitalWrite(active_elements_W1_W2[1], W1_new_status);
          W1_heating_state = 1;
          break;
      }
      break;


    // W1 heat strips activation
    case 1:

      // Validation of which heating state we're in (state 2 should not be an option)
      switch (W1_heating_state) {

        // activation of heat strip #1 associated with W1
        case 0:


          // rotation of the heat strips associated with W1 and W2 (for even wear)
          // before performing the rotation, we validate that W2 is not active, it should not as thermostat usually don't activate W2 without activating W1
          if (W2_current_status == 0) {


            int i;
            for (i = 0; i < nb_of_heating_elements; i++) {

              active_elements_W1_W2[i] = active_elements_W1_W2[i + 1];
            }

            active_elements_W1_W2[nb_of_heating_elements] = active_elements_W1_W2[0];

          }


          // activation of heat strip #1
          digitalWrite(active_elements_W1_W2[0], W1_new_status);
          time_W1 = millis();
          W1_current_status = W1_new_status;
          W1_heating_state = 1;
          break;

        // activation of heat strip #2 assocviated with W1
        case 1:
          digitalWrite(active_elements_W1_W2[1], W1_new_status);
          W1_heating_state = 2;
          break;

      }
      break;

  }

  time_H0 = millis();

}


/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for the activation of the relays associated withe the W2 heat command
/////////////////////////////////////////////////////////////////////////////

void activate_W2() {

  switch (W2_new_status) {

    // W2 heat strips deactivation
    case 0:

      // Validation of which heating state we're in (state 0 should not be an option)
      switch (W2_heating_state) {

        // deactivation of heat strip #1 associated with W2
        case 1:
          digitalWrite(active_elements_W1_W2[2], W2_new_status);
          W2_current_status = W2_new_status;
          W2_heating_state = 0;
          break;

        // deactivation of heat strip #2 associated with W2
        case 2:
          digitalWrite(active_elements_W1_W2[3], W2_new_status);
          W2_heating_state = 1;
          break;
      }
      break;


    // W2 heat strips activation
    case 1:

      // Validation of which heating state we're in (state 2 should not be an option)
      switch (W2_heating_state) {

        // activation of heat strip #1 associated with W2
        case 0:
          digitalWrite(active_elements_W1_W2[2], W2_new_status);
          time_W2 = millis();
          W2_current_status = W2_new_status; // on met  jour le status W2
          W2_heating_state = 1;
          break;

        // activation of heat strip #2 associated with W2
        case 1:
          digitalWrite(active_elements_W1_W2[3], W2_new_status);
          W2_heating_state = 2;
          break;

      }
      break;

  }
  time_H0 = millis();

}



/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for the activation of the blower
/////////////////////////////////////////////////////////////////////////////


void activate_G() {


  // check whether the fan was in a steady state (OFF or ON)
  // if the answer is positive, put the fan state at value for a new sequence (ON or OFF)

  if (fan_activation_state == 2) {
    fan_activation_state = 0;

  }



  switch (G_new_status) {


    // blower deactivation, by default we set the speed to the lowest setting for the next power-up
    case 0:

      switch (fan_activation_state) {

        // The electrical circuit is openend with the SSR relay
        case 0:
          digitalWrite(BSSR_ctrl, LOW);
          fan_activation_state = 1;
          time_AF = millis();
          break;

        // if the delay after opening the circuit is over the 10 ms minimum, we select the new speed (lowest value for next power-up)
        case 1:

          if ((long)(current_time - time_AF) >= delai_SSR) {

            digitalWrite(BHI_ctrl, LOW);
            digitalWrite(BMED_ctrl, LOW);
            digitalWrite(BLO_ctrl, LOW);
            current_speed = 0;
            new_speed = 0;
            fan_activation_state = 2;
            G_current_status = G_new_status;

          }
          break;
      }
      break;


    // We close the circuit with the SSR for the blower power-up
    case 1:

      digitalWrite(BSSR_ctrl, HIGH);

      time_G0 = millis();
      time_G = time_G0;
      G_current_status = G_new_status;
      break;
  }

}


/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for the evaluation of the current sense values
/////////////////////////////////////////////////////////////////////////////


void current_update() {

  int CS_raw_cnt;
  float temp_FLOAT_var;


  // we substract the raw instantaneous current value at the current pointer position

  H1_CS_sum = H1_CS_sum - H1_current_vector[pointer_CS];
  H2_CS_sum = H2_CS_sum - H2_current_vector[pointer_CS];
  H3_CS_sum = H3_CS_sum - H3_current_vector[pointer_CS];
  H4_CS_sum = H4_CS_sum - H4_current_vector[pointer_CS];
  FAN_CS_sum = FAN_CS_sum - FAN_current_vector[pointer_CS];


  // we update in the raw instanteous current vectors the value at the current pointer position

  CS_raw_cnt = analogRead(CSH1);
  H1_current_vector[pointer_CS] = pgm_read_word(&current_sensing_conversion[CS_raw_cnt]);

  CS_raw_cnt = analogRead(CSH2);
  H2_current_vector[pointer_CS] = pgm_read_word(&current_sensing_conversion[CS_raw_cnt]);

  CS_raw_cnt = analogRead(CSH3);
  H3_current_vector[pointer_CS] = pgm_read_word(&current_sensing_conversion[CS_raw_cnt]);

  CS_raw_cnt = analogRead(CSH4);
  H4_current_vector[pointer_CS] = pgm_read_word(&current_sensing_conversion[CS_raw_cnt]);

  CS_raw_cnt = analogRead(CSFAN);
  FAN_current_vector[pointer_CS] = pgm_read_word(&current_sensing_conversion[CS_raw_cnt]);

  // we update the raw sum

  H1_CS_sum = H1_CS_sum + H1_current_vector[pointer_CS];
  H2_CS_sum = H2_CS_sum + H2_current_vector[pointer_CS];
  H3_CS_sum = H3_CS_sum + H3_current_vector[pointer_CS];
  H4_CS_sum = H4_CS_sum + H4_current_vector[pointer_CS];
  FAN_CS_sum = FAN_CS_sum + FAN_current_vector[pointer_CS];

  // check whether the pointer has reached the end of the vector and update of the latency value for synchronizing the current evaluation with the 60 Hz electrical cycle

  if (pointer_CS == CS_vector_size - 1) {
    pointer_CS = 0;
    time_new_CS = millis();
    correction_us = (long) 100000 / 6 / nb_pts_period - (time_new_CS - time_old_CS) * 1000 / CS_vector_size;
    time_old_CS = time_new_CS;


    fixed_latency_new = fixed_latency_old + correction_us;

    fixed_latency_old = fixed_latency_new;


  }
  else {
    pointer_CS++;
  }

  // conversion of the current value from raw to physical value in mA

  temp_FLOAT_var = (float) sqrt(H1_CS_sum / CS_vector_size) / H_current_conversion_1 * HB_current_conversion_2;
  current_sense_H_vector[0] = temp_FLOAT_var;

  temp_FLOAT_var = (float) sqrt(H2_CS_sum / CS_vector_size) / H_current_conversion_1 * HB_current_conversion_2;
  current_sense_H_vector[1] = temp_FLOAT_var;

  temp_FLOAT_var = (float) sqrt(H3_CS_sum / CS_vector_size) / H_current_conversion_1 * HB_current_conversion_2;
  current_sense_H_vector[2] = temp_FLOAT_var;

  temp_FLOAT_var = (float) sqrt(H4_CS_sum / CS_vector_size) / H_current_conversion_1 * HB_current_conversion_2;
  current_sense_H_vector[3] = temp_FLOAT_var;

  temp_FLOAT_var = (float) sqrt(FAN_CS_sum / CS_vector_size) / B_current_conversion_1 * HB_current_conversion_2;
  current_sense_FAN = temp_FLOAT_var;
}


/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for comparing the current sense values to the thresholds for error management     PRELIMINARY
/////////////////////////////////////////////////////////////////////////////

void validation_current_vs_status() {

  // blower current management
  switch (G_current_status) {

    case 0:
      if (current_sense_FAN < current_FAN_min[0]) {
        system_status_LED[3] = 1;
      }
      if (current_sense_FAN > current_FAN_max[0]) {
        system_status_LED[3] = 1;
      }

      break;
    case 1:

      if (current_sense_FAN < current_FAN_min[current_speed + 1]) {
        system_status_LED[3] = 1;
      }
      if (current_sense_FAN > current_FAN_max[current_speed + 1]) {
        system_status_LED[3] = 1;
      }
      break;
  }





}


/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// routine for sending the monitored values on the virtual COM port if a USB connection is detected
/////////////////////////////////////////////////////////////////////////////


void USB_status_verbose() {

  //Serial.print("Current time = ");
  Serial.println(current_time);
  //Serial.print("system status index = ");
  Serial.println(system_status_index);
  //Serial.print("FAN status  = ");
  Serial.println(FAN_status);
  //Serial.print("HEATER status  = ");
  Serial.println(HEATER_status);
  //Serial.print("FAN UC / OC status = ");
  Serial.println(system_status_LED[3]);
  //Serial.print("H1 UC / OC status = ");
  Serial.println(system_status_LED[4]);
  //Serial.print("H2 UC / OC status = ");
  Serial.println(system_status_LED[5]);
  //Serial.print("H3 UC / OC status = ");
  Serial.println(system_status_LED[6]);
  //Serial.print("H4 UC / OC status = ");
  Serial.println(system_status_LED[7]);
  //Serial.print("G status  = ");
  Serial.println(G_current_status);
  //Serial.print("W1 status  = ");
  Serial.println(W1_current_status);
  //Serial.print("W2 status  = ");
  Serial.println(W2_current_status);
  //Serial.print("W3 status  = ");
  Serial.println(W3_current_status);
  //Serial.print("Thermistor 1 value (C) = ");
  Serial.println(TH1_value);
  //Serial.print("Thermistor 2 value (C) = ");
  Serial.println(TH2_value);
  //Serial.print("FAN RMS current (mA) = ");
  Serial.println(current_sense_FAN);
  //Serial.print("H1 RMS current (mA) = ");
  Serial.println(current_sense_H_vector[0]);
  //Serial.print("H2 RMS current (mA) = ");
  Serial.println(current_sense_H_vector[1]);
  //Serial.print("H3 RMS current (mA) = ");
  Serial.println(current_sense_H_vector[2]);
  //Serial.print("H4 RMS current (mA) = ");
  Serial.println(current_sense_H_vector[3]);
  //Serial.print("loop delay = ");
  Serial.println(fixed_latency_new);

  time_USB = millis();
}


/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// error management and system status update    PRELIMINARY
/////////////////////////////////////////////////////////////////////////////

void system_status_update() {

  FAN_status = 0;

  if (G_current_status == 1) {
    FAN_status = current_speed + 1;
  }

  HEATER_status = 0;

  if (W1_current_status == 1) {
    HEATER_status = HEATER_status + W1_heating_state;

    if (W2_current_status == 1) {
      HEATER_status = HEATER_status + W2_heating_state;
    }
  }

  system_status_index = FAN_status * 5 + HEATER_status;

  // Check whether we are in error mode

  if (system_status_LED[3] > 0) {


    // blower issue, major error, heat strips cannot be operated (not implemented yet)

    system_status_index = 26;

  }
  else
  {


    // Limp mode management, blower is working but at least 1 heat strip is not functionnal

    if (system_status_LED[4] + system_status_LED[5] + system_status_LED[6] + system_status_LED[7] > 0) {

      if ((system_status_LED[4] == 0) || (system_status_LED[5] == 0) || (system_status_LED[6] == 0) || (system_status_LED[7] == 0)) {


        // At least one heat strip is not functionnal, we are in limp mode

        system_status_index = 25;
      }
      else
      {

        // Every heat strips are not functionnal, major error
        system_status_index = 26;
      }
    }
  }

  system_status_LED[0] = pgm_read_word(&LED1[system_status_index]);
  system_status_LED[1] = pgm_read_word(&LED2[system_status_index]);
  system_status_LED[2] = pgm_read_word(&LED3[system_status_index]);

}

/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// main loop initialization
/////////////////////////////////////////////////////////////////////////////


void setup() {

  Serial.begin(9600);

  // initialisation

  pinMode(LED_ctrl, OUTPUT);
  pinMode(H1_ctrl, OUTPUT);
  pinMode(H2_ctrl, OUTPUT);
  pinMode(H3_ctrl, OUTPUT);
  pinMode(H4_ctrl, OUTPUT);
  pinMode(BLO_ctrl, OUTPUT);
  pinMode(BMED_ctrl, OUTPUT);
  pinMode(BHI_ctrl, OUTPUT);
  pinMode(BSSR_ctrl, OUTPUT);
  pinMode(G, INPUT);
  pinMode(W1, INPUT);
  pinMode(W2, INPUT);
  pinMode(W3, INPUT);

  // analog inputs initialization

  analogRead(TH1);
  analogRead(TH2);
  analogRead(CSH1);
  analogRead(CSH2);
  analogRead(CSH3);
  analogRead(CSH4);
  analogRead(CSFAN);

  // LED light initialization

  digitalWrite(LED_ctrl, LOW);   // HIGH = RED,   LOW = GREEN

  // initialization of the heat strips relays
  digitalWrite(H1_ctrl, LOW);
  digitalWrite(H2_ctrl, LOW);
  digitalWrite(H3_ctrl, LOW);
  digitalWrite(H4_ctrl, LOW);

  // initialization of blower relays
  digitalWrite(BSSR_ctrl, LOW);
  digitalWrite(BHI_ctrl, LOW);
  digitalWrite(BMED_ctrl, LOW);
  digitalWrite(BLO_ctrl, LOW);


  // initialization of the vectors
  memset(H1_current_vector, 0, CS_vector_size);
  memset(H2_current_vector, 0, CS_vector_size);
  memset(H3_current_vector, 0, CS_vector_size);
  memset(H4_current_vector, 0, CS_vector_size);
  memset(FAN_current_vector, 0, CS_vector_size);
  memset(current_sense_H_vector, 0, nb_of_heating_elements);


}


/////////////////////////////////////////////////////////////////////////////






/////////////////////////////////////////////////////////////////////////////
// main loop
/////////////////////////////////////////////////////////////////////////////



void loop() {


  // reading of the thermostat signals


  G_new_status = (digitalRead(G) == LOW);   //   inverse logic, G_new_status is equal to 1 (fan activation) when the actual physical signal on the board is LOW
  W1_new_status = (digitalRead(W1) == LOW); //   inverse logic, W1_new_status is equal to 1 (heat command active) when the actual physical signal on the board is LOW
  W2_new_status = (digitalRead(W2) == LOW); //   inverse logic, W2_new_status is equal to 1 (heat command active) when the actual physical signal on the board is LOW
  W3_new_status = (digitalRead(W3) == LOW); //   inverse logic, W3_new_status is equal to 1 (heat pump is active) when the actual physical signal on the board is LOW

  // current time is updated once per cycle in the main loop
  current_time = millis();


  // rollover just happenend, reseeting of all the time stamps, the usual delays between the different steps will be longer just this one time, once every 50 days
  if (old_current_time > current_time) {

    time_H0 = current_time;
    time_W1 = current_time;
    time_W2 = current_time;
    time_G0 = current_time;
    time_G = current_time;
    time_old_CS = current_time;
    time_new_CS = current_time;
    time_SC = current_time;
    time_AF = current_time;
    time_USB = current_time;
  }

  old_current_time = current_time;


  // Update of the thermistor temperature at every loop (mostly for monitoring purposes as it could be performed only when the blower is active)
  TH1_value = read_temp(TH1);
  TH2_value = read_temp(TH2);

  // security check, if the heat commands (W1 or W2) or the heat pump (W3) are active, we make sure to operate the blower
  if ((W1_current_status == 1 || W2_current_status == 1 || W3_current_status == 1) && (G_current_status == 0 || G_new_status == 0 )) {

    G_new_status = 1;

  }



  // a change has been detected on the blower G command status
  if (G_new_status != G_current_status) {

    activate_G();
  }


  // optimization of the blower speed and change of its speed are performed only after a fixed delay (after power-up and after a previous speed change)
  if (G_current_status == 1) {

    if ((long)(current_time - time_G0) >= delta_G0) {

      if ((long)(current_time - time_G) >= delta_G) {

        new_speed_evaluation();
        speed_change();
      }
    }
  }

  // a change has been detected on the W1 heat command status
  if (W1_new_status != W1_current_status && (long)(current_time - time_H0) >= delta_H)  {
    activate_W1();
  }



  // a change has been detected on the W2 heat command status
  if (W2_new_status != W2_current_status && (long)(current_time - time_H0) >= delta_H) {
    activate_W2();
  }


  // delay validation before activating the second heat strip of W1
  if (W1_current_status == 1 && W1_new_status == 1) {
    if ((long)(current_time - time_W1) / 1000.0 >= delta_W1_level_1 && W1_heating_state == 1 && (long)(current_time - time_H0) >= delta_H) {
      activate_W1();
    }
  }


  // delay validation before activating the second heat strip of W2
  if (W2_current_status == 1 && W2_new_status == 1) {
    if ((long)(current_time - time_W2) / 1000.0 >= delta_W2_level_1 && W2_heating_state == 1 && (long)(current_time - time_H0) >= delta_H) {
      activate_W2();
    }

  }

  // a change has been detected on the W3 heat command status (heat pump)
  if (W3_new_status != W3_current_status) {
    W3_current_status = W3_new_status;
  }



  // current sense value update
  current_update();


  // error management PRELIMINARY
  validation_current_vs_status();

  // system status update PRELIMINARY
  system_status_update();



  // Verification whether a USB connection is present

  if (Serial && USB_connection_status == 0) {
    USB_connection_status = 1;
  }

  if (!Serial && USB_connection_status == 1) {
    USB_connection_status = 0;
  }


  // Delay validation and communication with the PC if the USB connection is active
  if ((long)(current_time - time_USB) >= USB_comm_period && USB_connection_status == 1) {

    USB_status_verbose();
  }




  // fixed latency for perfect synchonization with the 60 Hz electrical AC for precise current sense evaluation
  delayMicroseconds(fixed_latency_new);

}

Credits

desbiensl
0 projects • 12 followers

Comments