-
Notifications
You must be signed in to change notification settings - Fork 0
/
thermostat.ino
1899 lines (1817 loc) · 94.9 KB
/
thermostat.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*******************(C) COPYRIGHT 2018 KENNETH L ANDERSON *********************
* ARDUINO SINGLE REGULATED SPACE THERMOSTAT
* File Name : thermostat.ino
* Author : KENNETH L ANDERSON
* Version : v.1.0
* Date : 27-March-2018
* Description : Implements residential thermostat functionality on Arduino-compatible hardware platforms having 32K minimum Flash + 64 bytes minimum EEPROM
* Boards tested on : Uno Mega2560 WeMo XI/TTGO XI Leonardo Nano
* Known limitations : Only a host serially-connected computer can effect run-time change and/or viewing of thermostat settings ("Here, have yourself a thermostat that is locked from changing if not attached to a host computer")
* : No scheduling ability to work around that limitation is integrated
* : No time-of-day ability is even vaguely allowed for except if implemented on a host computer
********************************************************************************
* THE PRESENT PRODUCT WHICH IS ONLY FOR USE BY SOFTWARE- AND HARDWARE-COMPETENT USERS
* IS INTENDED TO OFFER VARIOUS CAPABILITIES. BECAUSE ABSOLUTELY NO CONSIDERATION
* IS EXPECTED IN RETURN, NO CONTRACT, WHETHER OFFERING SUITABILITY, RELIABILITY,
* FUNCTIONALITY, SAFETY, OR OTHER, EXISTS. BY USING THIS PRODUCT YOU EXPRESSLY AND
* TOTALLY ACCEPT FULL LIABILITY FOR ANY DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES
* WITH RESPECT TO ANY CLAIMS ARISING FROM SOFTWARE AND/OR HARDWARE DERIVATIONS OF PRODUCT.
* I MAKE ABSOLUTELY NO CLAIM OF MY OWN COMPETENCE, LICENSURE, CERTIFICATION, EXPERIENCE,
* OR PROFESSIONAL STANDING OTHER THAN BEING AN ELECTRONICS TECHNICIAN BY FORMAL
* DEGREE AND UNITED STATES FCC RADIOTELEPHONE OPERATORS LICENSE, AND
* A SOFTWARE DEVELOPER BY LIMITED PRACTICAL EXPERIENCE
********************************************************************************
*
* I RECOMMEND WHEN USING A DIGITAL SENSOR ON A PIN THAT YOU ADD 128 TO THE PIN NUMBER WHEN STORING IT IN EEPROM SO IF THE DIGITAL SENSOR FAILS THE SKETCH WILL NOT REVERT TO READ AN INVALID ANALOG VALUE FROM THAT PIN!
*
*
* TODO: Scheduling and GPS-acquired time using NMEA link to/from GPS module
* labels to pins
* Add more externally-scripted functions, like entire port pin changes, watches on pins with routines that will execute routines to any combo of pins upon pin[s] conditions,
* alert when back pressure within furnace indicates to change filter
* damper operation with multiple temp sensors
*
* https://github.com/wemos/Arduino_XI for the IDE support for TTGO XI/WeMo XI
*
* Standard furnace nomenclature can be somewhat disorienting: blower is the term for fan when the furnace unit is being referenced by part for a furnace workman,
* fan is the term for same part but for the thermostat operator person
*
*************************************************************************************************************************/
#define VERSION "1.0"
//On the first run of this sketch, if you received an error message about the following line...
//#define RESTORE_FACTORY_DEFAULTS //As the error message said, uncomment this line, compile & load for first run EEPROM setup in WeMo XI/TTGO XI and any other board that needs it, then comment back out and recompile and load b/c sketch would be too long otherwise
//If you've connected the analog temperature sensor[s] the opposite direction, thus seeing reported temperature go the opposite direction the real temperature goes, you may re-compile with the following line commented out rather than re-wire:
#define ALL_ANALOG_SENSOR_CIRCUITS_ARE_THERMISTOR_TO_EXCITATION_VOLTS_AND_RESISTOR_TO_GROUND
#undef u8
#define u8 uint8_t
#undef u16
#define u16 uint16_t
#include "analog_pin_adjust.h"
#include <EEPROM.h> // Any board that errors compiling this line is unsuitable to be a thermostat because it cannot store settings persistently
double analogReadLeast( u8 pin )
{
double raw = analogRead( pin );
for( u8 times = 0; times < 4 ; times++ )
{//go for more and keep lowest value
double raw2 = analogRead( pin );
if( raw2 < raw ) raw = raw2;
}
return raw;
}
#include "DHTdirectRead.h"
#undef BAUD_RATE
#define BAUD_RATE 57600 //Very much dependent upon the capability of the host computer to process talkback data, not just baud rate of its interface
#if not defined ( __LGT8FX8E__ ) && not defined ( ARDUINO_AVR_YUN ) && not defined ( ARDUINO_AVR_LEONARDO ) && not defined ( ARDUINO_AVR_LEONARDO_ETH ) && not defined ( ARDUINO_AVR_MICRO ) && not defined ( ARDUINO_AVR_ESPLORA ) && not defined ( ARDUINO_AVR_LILYPAD_USB ) && not defined ( ARDUINO_AVR_YUNMINI ) && not defined ( ARDUINO_AVR_INDUSTRIAL101 ) && not defined ( ARDUINO_AVR_LININO_ONE )
#ifndef RESTORE_FACTORY_DEFAULTS
#define RESTORE_FACTORY_DEFAULTS
#endif
#else
#if defined ( __LGT8FX8E__ )
#define BAUD_RATE 19200 //During sketch development it was found that the XI tends to revert to baud 19200 after programming or need that rate prior to programming so we can't risk setting baud to anything but that until trusted in the future
#define LED_BUILTIN 12
//Commonly available TTGO XI/WeMo XI EEPROM library has only .read() and .write() methods, no EEPROM.length().
#define EEPROMlength 1024
#define NUM_DIGITAL_PINS 14 //here if necessary or FYI. TTGO XI has no D13 labeled but its prob sck, https://www.avrfreaks.net/comment/2247501#comment-2247501 says why D14 and up don't work digital: "lgt8fx8x_init() disables digital with DIDR0 register. Subsequent pinMode() should enable/disable digital as required. i.e. needs a patch in wiring_digital.c"
#endif
#endif
#ifndef EEPROMlength
#define EEPROMlength EEPROM.length()
#endif
#ifndef SERIAL_PORT_HARDWARE
#define SERIAL_PORT_HARDWARE 0 // We make a risky but necessary assumption here
#endif
//All temps are shorts until displayed
//Where are other indoor sensors? TODO WITH DAMPERS
//What about utilizing PCI ISRs and how they control which pins to use? POSSIBLY TODO
// INTENT: Access these values by reference and pointers. They will be hardcoded here only. The run-time code can only find out what they are, how many there are, and their names by calls to this function
// The following are addresses in EEPROM of values that need to be persistent across power outages.
//The first two address locations in EEPROM store a tatoo used to ensure EEPROM contents are valid for this sketch
u8 primary_temp_sensor_pin_address = 2;
u8 heat_pin_address = 3;
u8 furnace_blower_pin_address = 4;
u8 power_cycle_pin_address = 5;
u8 thermostat_mode_address = 6;
u8 fan_mode_address = 7;
u8 secondary_temp_sensor_pin_address = 8;
u8 cool_pin_address = 9;
u8 outdoor_temp_sensor1_pin_address = 10;
u8 outdoor_temp_sensor2_pin_address = 11;
#ifdef PIN_A0
u8 calibration_offset_array_start_address_first_byte = 12;//will occupy two bytes
#endif
#define NO_BOUNDS_CHECK 0
#define HEAT_TEMP_BOUNDS_CHECK 1
#define COOL_TEMP_BOUNDS_CHECK 2
#define BOTH_TEMPS_BOUNDS_CHECK 3
#define ADJUST_START_AND_STOP_LIMITS_TOGETHER 4
#define SINGLE_LIMIT_ONLY 0
#define ADJUST 0
#define SETFULL 16
#define HEAT_UPPER_BOUNDS 27
#define HEAT_LOWER_BOUNDS 10
#define COOL_UPPER_BOUNDS 32
#define COOL_LOWER_BOUNDS 15
#define NO_TEMP_ENTERED 2
#define SHORTEN 0
#define REPLACE 1
#define NOT_RUNNING false
#define ALREADY_RUNNING true
#define KY013 0
#undef RAW //just in case
#define RAW 1
#define TO_END_OF_STRING 1
#define NOT_TO_END_OF_STRING 0
unsigned int logging_address = EEPROMlength - sizeof( boolean );
unsigned int upper_heat_temp_address = logging_address - sizeof( short );//EEPROMlength - 2;
unsigned int lower_heat_temp_address = upper_heat_temp_address - sizeof( short );//EEPROMlength - 3;
unsigned int logging_temp_changes_address = lower_heat_temp_address - sizeof( boolean );//EEPROMlength - 4;
unsigned int upper_cool_temp_address = logging_temp_changes_address - sizeof( short );
unsigned int lower_cool_temp_address = upper_cool_temp_address - sizeof( short );
// So we don't assume every board has exactly three
//in case of boards that have more than 24 pins ( three virtual 8-bit ports )
//int number_of_ports_that_present_pins = EEPROM.read( EEPROMlength - 5 ); // NOT UTILIZED THIS VERSION
// main temperature sensor, heat, auxiliary heat device, system power cycle, porch light, dining room coffee outlet
// The following values need to be stored persistent through power outages. Sadly, the __LGT8FX8E__ will not read EEPROM until the setup() loop starts executing, so these values get set there for all boards for simplicity
u8 primary_temp_sensor_pin;// = EEPROM.read( primary_temp_sensor_pin_address );
u8 secondary_temp_sensor_pin;// = EEPROM.read( secondary_temp_sensor_pin_address );
u8 heat_pin;// = EEPROM.read( heat_pin_address );
u8 cool_pin;// = EEPROM.read( cool_pin_address );
u8 furnace_blower_pin;// = EEPROM.read( furnace_blower_pin_address );
u8 power_cycle_pin;// = EEPROM.read( power_cycle_pin_address );
u8 outdoor_temp_sensor1_pin;
u8 outdoor_temp_sensor2_pin;
boolean bLogging;// = ( boolean )EEPROM.read( logging_address );
boolean bLogging_temp_changes;// = ( boolean )EEPROM.read( logging_temp_changes_address );
float lower_heat_temp_floated;//filled in setup
float upper_heat_temp_floated;
float upper_cool_temp_floated;
float lower_cool_temp_floated;
char thermostat_mode;// = ( char )EEPROM.read( thermostat_mode_address );
char fan_mode;// = ( char )EEPROM.read( fan_mode_address );//a';//Can be either auto (a) or on (o)
char szFull[ ] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, \
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
float last_three_temps[] = { -100, -101, -102 };
float heat_started_temp_x_3 = last_three_temps[ 0 ] + last_three_temps[ 1 ] + last_three_temps[ 2 ];
u8 pin_specified;
float temp_specified_floated;
boolean bFresh_powerup = true;
long unsigned timer_alert_furnace_sent = 0;
const bool PROGMEM bFactory_setting_logging_setting PROGMEM = true;
const bool PROGMEM bFactory_setting_logging_temp_changes_setting PROGMEM = true;
const char factory_setting_fan_mode PROGMEM = 'a';
const char PROGMEM factory_setting_thermostat_mode PROGMEM = 'h';
const u8 factory_setting_primary_temp_sensor_pin PROGMEM = 2 + 128;//the 128 restricts the pin to digital mode only so if device fails in the future unexpectedly sketch would not revert the pin to analog mode if the pin is capable of analog mode
const u8 PROGMEM factory_setting_heat_pin PROGMEM = 3;
const u8 PROGMEM factory_setting_furnace_blower_pin PROGMEM = 4;
const u8 PROGMEM factory_setting_power_cycle_pin PROGMEM = 5;
const u8 PROGMEM factory_setting_cool_pin PROGMEM = 9;
const float factory_setting_lower_heat_temp_floated PROGMEM = 22.4;
const float factory_setting_upper_heat_temp_floated PROGMEM = 23;
const float factory_setting_lower_cool_temp_floated PROGMEM = 21.5;
const float factory_setting_upper_cool_temp_floated PROGMEM = 22.1;
const u8 factory_setting_secondary_temp_sensor_pin PROGMEM = 8 + 128;//the 128 restricts it to only digital sensor
const u8 factory_setting_outdoor_temp_sensor1_pin PROGMEM = 10 + 128;//the 128 restricts it to only digital sensor
const u8 factory_setting_outdoor_temp_sensor2_pin PROGMEM = 11 + 128;//the 128 restricts it to only digital sensor
const float minutes_furnace_should_be_effective_after PROGMEM = 5.5; //Can be decimal this way
const unsigned long loop_cycles_to_skip_between_alert_outputs PROGMEM = 4 * 60 * 30;//estimating 4 loops per second, 60 seconds per minute, 30 minutes per alert
const char szTalkback[] PROGMEM = "talkback";
const char szPin_set[] PROGMEM = "pin set";
const char szPins_read[] PROGMEM = "pins read";
const char szRead_pins[] PROGMEM = "read pins";
const char szSet_pin_to[] PROGMEM = "set pin to";
//const char szPin_set_to[] PROGMEM = "pin set to";
const char szView[] PROGMEM = "view";
const char szSensor[] PROGMEM = "sensor";
const char szPersistent_memory[] PROGMEM = "persistent memory";
const char szChanges[] PROGMEM = "changes";
const char szChange[] PROGMEM = "change";
const char szThermostat[] PROGMEM = "thermostat";
const char szFan_auto[] PROGMEM = "fan auto";
const char szFan_on[] PROGMEM = "fan on";
const char szFactory[] PROGMEM = "factory";
const char szCycle_power[] PROGMEM = "cycle power";
const char szPin_read[] PROGMEM = "pin read";
const char szAuto[] PROGMEM = "auto";
const char szOff[] PROGMEM = "off";
const char szHeat[] PROGMEM = "heat";
const char szCool[] PROGMEM = "cool";
const char szHeat_temps[] PROGMEM = "heat temps";
const char szCool_temps[] PROGMEM = "cool temps";
const char szAll_temps[] PROGMEM = "all temps";
const char szPower_cycle[] PROGMEM = "power cycle";
const char szRead_pin[] PROGMEM = "read pin";
const char szRead_pin_dot[] PROGMEM = "read pin .";
const char szSet_pin[] PROGMEM = "set pin";
const char szLogging[] PROGMEM = "logging";
const char szReport_master_room_temp[] PROGMEM = "report master room temp";
const char szSet_pin_high[] PROGMEM = "set pin high";
const char szSet_pin_low[] PROGMEM = "set pin low";
const char szSet_pin_output[] PROGMEM = "set pin output";
const char szSet_pin_input_with_pullup[] PROGMEM = "set pin input with pullup";
const char szSet_pin_input[] PROGMEM = "set pin input";
const char szHeatStartLowTemp[] PROGMEM = "heat start low temp";
const char szHeatStopHighTemp[] PROGMEM = "heat stop high temp";
const char szCoolStopLowTemp[] PROGMEM = "cool stop low temp";
const char szCoolStartHighTemp[] PROGMEM = "cool start high temp";
const char szTher[] PROGMEM = "ther";
const char szFan_a[] PROGMEM = "fan a";
const char szFan_o[] PROGMEM = "fan o";
const char szFan_[] PROGMEM = "fan ";
const char szFan[] PROGMEM = "fan";
const char szLogging_temp_ch_o[] PROGMEM = "logging temp ch o";
const char szLogging_temp_ch[] PROGMEM = "logging temp ch";
const char szLogging_o[] PROGMEM = "logging o";
const char szVi_pers[] PROGMEM = "vi pers";
const char szCh_pers[] PROGMEM = "ch pers";
const char szVi_fact[] PROGMEM = "vi fact";
const char szReset[] PROGMEM = "reset";
const char szTest_alert[] PROGMEM = "test alert";
const char szSens_read[] PROGMEM = "sens read";
const char szRead_sens[] PROGMEM = "read sens";
const char szHelp[] PROGMEM = "help";
bool bHeat_state = false;
bool bCool_state = false;
long unsigned check_furnace_effectiveness_time;
void printBasicInfo();
void printThermoModeWord( char, bool );
void assign_pins( bool );
#ifdef __LGT8FX8E__
void EEPROMupdate ( unsigned long address, u8 val )
{
if( EEPROM.read( address ) != val ) EEPROM.write( address, val );
}
#endif
#ifdef RESTORE_FACTORY_DEFAULTS
#include "restore_factory_defaults.h"
#endif
void printAnalogLevel( u8 pin_specified_local )
{
#ifdef ADC_BITS
analogReadResolution( 12 );
#endif
Serial.print( F( " level: " ) );
Serial.print( analogReadLeast( pin_specified_local ), 0 );
#ifdef ADC_BITS
analogReadResolution( 10 );//needed for the temperature algorithm
#endif
}
void assign_pins( bool already_running )
{
digitalWrite( heat_pin, LOW );
digitalWrite( cool_pin, LOW );
digitalWrite( furnace_blower_pin, LOW );
//lines above cover pins getting changed below. It is actually a feature of this sketch
fan_mode = ( char )EEPROM.read( fan_mode_address );//a';//Can be either auto (a) or on (o)
primary_temp_sensor_pin = EEPROM.read( primary_temp_sensor_pin_address );
secondary_temp_sensor_pin = EEPROM.read( secondary_temp_sensor_pin_address );
outdoor_temp_sensor1_pin = EEPROM.read( outdoor_temp_sensor1_pin_address );
outdoor_temp_sensor2_pin = EEPROM.read( outdoor_temp_sensor2_pin_address );
heat_pin = EEPROM.read( heat_pin_address );
cool_pin = EEPROM.read( cool_pin_address );
furnace_blower_pin = EEPROM.read( furnace_blower_pin_address );
power_cycle_pin = EEPROM.read( power_cycle_pin_address );
pinMode( power_cycle_pin, OUTPUT );
digitalWrite( power_cycle_pin, LOW );
pinMode( heat_pin, OUTPUT );
digitalWrite( heat_pin, bHeat_state );
pinMode( cool_pin, OUTPUT );
digitalWrite( cool_pin, bCool_state );
pinMode( furnace_blower_pin, OUTPUT );
if( fan_mode == 'o' ) digitalWrite( furnace_blower_pin, HIGH );
else digitalWrite( furnace_blower_pin, LOW );
}
void printThermoModeWord( char setting_char, bool bNewline )
{
const char *pSetting PROGMEM;
if( setting_char == 'o' )
pSetting = szOff;
else if( setting_char == 'h' )
pSetting = szHeat;
else if( setting_char == 'c' )
pSetting = szCool;
#if not defined ( __LGT8FX8E__ ) && not defined ( ARDUINO_AVR_YUN ) && not defined ( ARDUINO_AVR_LEONARDO ) && not defined ( ARDUINO_AVR_LEONARDO_ETH ) && not defined ( ARDUINO_AVR_MICRO ) && not defined ( ARDUINO_AVR_ESPLORA ) && not defined ( ARDUINO_AVR_LILYPAD_USB ) && not defined ( ARDUINO_AVR_YUNMINI ) && not defined ( ARDUINO_AVR_INDUSTRIAL101 ) && not defined ( ARDUINO_AVR_LININO_ONE )
else if( setting_char == 'a' )
pSetting = szAuto;
#endif
else
{
Serial.println();
return;
}
Serial.print( ( const __FlashStringHelper * )pSetting );
if( bNewline ) Serial.println();
}
void printThermoModeWord( long unsigned var_with_setting, bool bNewline )
{
if( thermostat_mode == 'o' )
var_with_setting = ( long unsigned )szOff;
else if( thermostat_mode == 'h' )
var_with_setting = ( long unsigned )szHeat;
else if( thermostat_mode == 'c' )
var_with_setting = ( long unsigned )szCool;
#if not defined ( __LGT8FX8E__ ) && not defined ( ARDUINO_AVR_YUN ) && not defined ( ARDUINO_AVR_LEONARDO ) && not defined ( ARDUINO_AVR_LEONARDO_ETH ) && not defined ( ARDUINO_AVR_MICRO ) && not defined ( ARDUINO_AVR_ESPLORA ) && not defined ( ARDUINO_AVR_LILYPAD_USB ) && not defined ( ARDUINO_AVR_YUNMINI ) && not defined ( ARDUINO_AVR_INDUSTRIAL101 ) && not defined ( ARDUINO_AVR_LININO_ONE )
else if( thermostat_mode == 'a' )
var_with_setting = ( long unsigned )szAuto;
#endif
else
{
Serial.println();
return;
}
Serial.print( ( const __FlashStringHelper * )var_with_setting );
if( bNewline ) Serial.println();
}
void refusedNo_exclamation() //putting this in a function for just 2 calls saves 64 bytes due to short memory in WeMo/TTGO XI
{
if( bLogging )
{
Serial.print( F( "Append a '!' or pin " ) );
Serial.print( pin_specified );
Serial.println( F( " is reserved" ) );
}
}
void setFurnaceEffectivenessTime()
{
check_furnace_effectiveness_time = millis();//Will this become invalid if heat temp setting gets adjusted? TODO: Account for that condition
heat_started_temp_x_3 = last_three_temps[ 0 ] + last_three_temps[ 1 ] + last_three_temps[ 2 ];
}
void IfReservedPinGettingForced( bool level )
{
if( pin_specified == furnace_blower_pin )
{
if( (bool )level ) fan_mode = 'a'; //high; low = 'a'
else fan_mode = 'o'; //high; low = 'a'
#ifndef __LGT8FX8E__
EEPROM.update( fan_mode_address, fan_mode ); //high; low = 'a'
#else
EEPROMupdate( fan_mode_address, fan_mode );
#endif
}
else if( pin_specified == heat_pin )
{
timer_alert_furnace_sent = 0;
if( level )
{
setFurnaceEffectivenessTime();
}
bHeat_state = ( bool ) level; //high = true; low = false
}
else if( pin_specified == cool_pin ) bCool_state = ( bool ) level; //high; = true low = false
}
bool refuseInput()
{
if( pin_specified == power_cycle_pin || pin_specified == furnace_blower_pin || pin_specified == heat_pin || pin_specified == cool_pin )
{
if( bLogging )
{
if( pin_specified == power_cycle_pin ) Serial.print( F( "Host pwr" ) );
if( pin_specified == furnace_blower_pin ) Serial.print( F( "Furnace fan" ) );
if( pin_specified == heat_pin ) Serial.print( F( "Furnace" ) );
if( pin_specified == cool_pin ) Serial.print( F( "A/C" ) );
Serial.print( F( " pin " ) );
Serial.print( pin_specified );
Serial.println( F( " skipped" ) );
}
return true;
}
return false;
}
bool pin_print_and_not_sensor( bool bSetting, u8 pin_specified )
{
if( ( pin_specified == ( primary_temp_sensor_pin & 0x7F ) || pin_specified == ( secondary_temp_sensor_pin & 0x7F ) ) || ( pin_specified == ( outdoor_temp_sensor1_pin & 0x7F ) && DHTfunctionResultsArray[ outdoor_temp_sensor1_pin ].ErrorCode == DEVICE_READ_SUCCESS ) || ( pin_specified == ( outdoor_temp_sensor2_pin & 0x7F ) && DHTfunctionResultsArray[ outdoor_temp_sensor2_pin ].ErrorCode == DEVICE_READ_SUCCESS ) )
{
if( pin_specified == ( primary_temp_sensor_pin & 0x7F ) ) Serial.print( F( "Indoor prim" ) );
else if( pin_specified == ( secondary_temp_sensor_pin & 0x7F ) ) Serial.print( F( "Indoor second" ) );
else if( pin_specified == ( outdoor_temp_sensor1_pin & 0x7F ) ) Serial.print( F( "Outdoor prim" ) );
else if( pin_specified == ( outdoor_temp_sensor2_pin & 0x7F ) ) Serial.print( F( "Outdoor second" ) );
Serial.print( F( "ary temp sensor pin " ) );
return( false );
}
if( bSetting ) Serial.print( F( "time_stamp_this " ) );//only do if returning true
Serial.print( F( "Pin " ) );
Serial.print( pin_specified );
if( bSetting ) Serial.print( F( " now set to " ) );
}
void illegal_attempt_SERIAL_PORT_HARDWARE()
{
Serial.print( F( "Rx from host pin " ) );
Serial.print( SERIAL_PORT_HARDWARE );
Serial.println( F( " skipped" ) );
}
boolean IsValidPinNumber( const char* str, u8 type_analog_allowed )
{
u8 i = 0;
while( str[ i ] == 32 ) i++;
if( str[ i ] == '.' && ( str[ i + 1 ] == 0 || str[ i + 1 ] == 32 || ( ( str[ i + 1 ] == '+' || str[ i + 1 ] == '-' ) && ( str[ i + 2 ] == 0 || str[ i + 2 ] == 32 ) ) || ( str[ i + 1 ] == '!' && ( str[ i + 2 ] == 0 || str[ i + 2 ] == 32 ) ) || ( ( str[ i + 1 ] == '+' || str[ i + 1 ] == '-' ) && str[ i + 2 ] == '!' && ( str[ i + 3 ] == 0 || str[ i + 3 ] == 32 ) ) ) )
{
pin_specified = 0;
return true;
}
u8 j = i;
while( isdigit( str[ j ] ) ) j++;
pin_specified = ( u8 )atoi( str );
if( j == i || ( !type_analog_allowed && ( ( pin_specified & 0x7F ) >= NUM_DIGITAL_PINS ) ) || ( ( pin_specified & 0x7F ) >= NUM_DIGITAL_PINS && !memchr( analog_pin_list, pin_specified, PIN_Amax ) ) )
{
Serial.println( F( " Pin # error-see help screen " ) );
Serial.println( str );
// Serial.println( NUM_DIGITAL_PINS );
// for( u8 i = 0; i < sizeof( analog_pin_list ); i++ )
// Serial.println( analog_pin_list[ i ] );
return false;
}
return true;
}
boolean isanoutput( int pin, boolean reply )
{
uint8_t bit = digitalPinToBitMask( pin );
volatile uint8_t *reg = portModeRegister( digitalPinToPort( pin ) );
if( *reg & bit ) return true;
if( reply )
{
Serial.print( F( "Pin " ) );
Serial.print( pin );
Serial.println( F( " not set to output" ) );
}
return false;
}
u8 IsValidTemp( const char* str, u8 boundsCheckAndAdjustTogether )
{
for( unsigned int i = 0; i < strlen( str ); i++ )
{
if( !i && str[ 0 ] == '-' );
else if( !( isdigit( str[ i ] ) || str[ i ] == '.' ) || ( strchr( str, '.' ) != strrchr( str, '.' ) ) ) //allow one and only one decimal point in str
{
return NO_TEMP_ENTERED;
}
}
temp_specified_floated = ( float )atoi( str );
if( strchr( str, '.' ) ){ str = strchr( str, '.' ) + 1; temp_specified_floated += ( ( float )atoi( str ) ) / 10; };
if( ( ( boundsCheckAndAdjustTogether & HEAT_TEMP_BOUNDS_CHECK ) && !( boundsCheckAndAdjustTogether & ADJUST_START_AND_STOP_LIMITS_TOGETHER ) && ( temp_specified_floated < HEAT_LOWER_BOUNDS || temp_specified_floated > HEAT_UPPER_BOUNDS ) ) || \
( ( boundsCheckAndAdjustTogether & COOL_TEMP_BOUNDS_CHECK ) && !( boundsCheckAndAdjustTogether & ADJUST_START_AND_STOP_LIMITS_TOGETHER ) && ( temp_specified_floated < COOL_LOWER_BOUNDS || temp_specified_floated > COOL_UPPER_BOUNDS ) ) || \
( ( boundsCheckAndAdjustTogether & HEAT_TEMP_BOUNDS_CHECK ) && ( boundsCheckAndAdjustTogether & ADJUST_START_AND_STOP_LIMITS_TOGETHER ) && ( lower_heat_temp_floated + temp_specified_floated < HEAT_LOWER_BOUNDS || upper_heat_temp_floated + temp_specified_floated > HEAT_UPPER_BOUNDS ) ) || \
( ( boundsCheckAndAdjustTogether & COOL_TEMP_BOUNDS_CHECK ) && ( boundsCheckAndAdjustTogether & ADJUST_START_AND_STOP_LIMITS_TOGETHER ) && ( lower_cool_temp_floated + temp_specified_floated < COOL_LOWER_BOUNDS || upper_cool_temp_floated + temp_specified_floated > COOL_UPPER_BOUNDS ) ) )//This check should be called for the rooms where it matters
{
Serial.println( F( "Would be usafe" ) );
return ( u8 )false;
}
return ( u8 )true;
}
void printBasicInfo()
{
Serial.println( F( ".." ) );
if( bFresh_powerup )
{
Serial.println( F( "A way to save talkbacks into a file in Linux is: (except change \"TIME_STAMP_THIS\" to lower case, not shown so this won't get filtered in by such command)" ) );
Serial.print( F( " nohup stty igncr -F \$(ls /dev/ttyA* /dev/ttyU* 2>/dev/null|tail -n1) " ) );
Serial.print( BAUD_RATE );
//The following would get time stamped inadvertently:
// Serial.println( F( " -echo;while true;do cat \$(ls /dev/ttyA* /dev/ttyU* 2>/dev/null|tail -n1)|while IFS= read -r line;do if ! [[ -z \"\$line\" ]];then echo \"\$line\"|sed \'s/\^time_stamp_this/\'\"\$(date )\"\'/g\';fi;done;done >> /log_directory/arduino.log 2>/dev/null &" ) );
//So we have to capitalize time_stamp_this
Serial.println( F( " -echo;while true;do cat \$(ls /dev/ttyA* /dev/ttyU* 2>/dev/null|tail -n1)|while IFS= read -r line;do if ! [[ -z \"\$line\" ]];then echo \"\$line\"|sed \'s/\^TIME_STAMP_THIS/\'\"\$(date )\"\'/g\';fi;done;done >> /log_directory/arduino.log 2>/dev/null &" ) );
Serial.println( F( "." ) );
Serial.println( F( "time_stamp_this New power up:" ) );
}
Serial.print( F( "Version: " ) );
Serial.println( F( VERSION ) );
#if not defined ( __LGT8FX8E__ ) && not defined ( ARDUINO_AVR_YUN ) && not defined ( ARDUINO_AVR_LEONARDO ) && not defined ( ARDUINO_AVR_LEONARDO_ETH ) && not defined ( ARDUINO_AVR_MICRO ) && not defined ( ARDUINO_AVR_ESPLORA ) && not defined ( ARDUINO_AVR_LILYPAD_USB ) && not defined ( ARDUINO_AVR_YUNMINI ) && not defined ( ARDUINO_AVR_INDUSTRIAL101 ) && not defined ( ARDUINO_AVR_LININO_ONE )
Serial.print( F( "Operating mode (heat/cool/auto/off)=" ) );
#else
Serial.print( F( "Operating mode (heat/cool/off)=" ) );
#endif
printThermoModeWord( thermostat_mode, true );
Serial.print( F( "Fan mode=" ) );
if( fan_mode == 'a' ) Serial.println( F( "auto" ) );
else if( fan_mode == 'o' ) Serial.println( F( "on" ) );
Serial.print( ( const __FlashStringHelper * )szHeatStartLowTemp );
Serial.print( '=' );
Serial.println( lower_heat_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szHeatStopHighTemp );
Serial.print( '=' );
Serial.println( upper_heat_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szCoolStopLowTemp );
Serial.print( '=' );
Serial.println( lower_cool_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szCoolStartHighTemp );
Serial.print( '=' );
Serial.println( upper_cool_temp_floated, 1 );
Serial.print( F( "Logging talkback is turned o" ) );
if( bLogging ) Serial.println( F( "n" ) );
else Serial.println( F( "ff" ) );
Serial.print( F( "Talkback of temp changes is turned o" ) );
if( bLogging_temp_changes ) Serial.println( F( "n" ) );
else Serial.println( F( "ff" ) );
Serial.print( F( "primary, secondary indoor temp sensor pins=" ) );
Serial.print( primary_temp_sensor_pin & 0x7F );
Serial.print( F( "@" ) );
Serial.print( primary_temp_sensor_pin_address );
Serial.print( F( ", " ) );
Serial.print( secondary_temp_sensor_pin & 0x7F );
Serial.print( F( "@" ) );
Serial.println( secondary_temp_sensor_pin_address );
Serial.print( F( "outdoor temp sensor1, 2 pins=" ) );
Serial.print( outdoor_temp_sensor1_pin & 0x7F );
Serial.print( F( "@" ) );
Serial.print( outdoor_temp_sensor1_pin_address );
Serial.print( F( ", " ) );
Serial.print( outdoor_temp_sensor2_pin & 0x7F );
Serial.print( F( "@" ) );
Serial.println( outdoor_temp_sensor2_pin_address );
Serial.print( F( "heat, fan, cool pins=" ) );
Serial.print( heat_pin );
Serial.print( F( "@" ) );
Serial.print( heat_pin_address );
Serial.print( F( ", " ) );
Serial.print( furnace_blower_pin );
Serial.print( F( "@" ) );
Serial.print( furnace_blower_pin_address );
Serial.print( F( ", " ) );
Serial.print( cool_pin );
Serial.print( F( "@" ) );
Serial.println( cool_pin_address );
Serial.print( F( "system power pin=" ) );
Serial.print( power_cycle_pin );
Serial.print( F( "@" ) );
Serial.println( power_cycle_pin_address );
Serial.println( F( " (pin@EEPROM address)" ) );
Serial.print( F( "LED_BUILTIN pin=" ) );
Serial.println( LED_BUILTIN );
Serial.print( F( "Digital pins=0 to " ) );
Serial.println( NUM_DIGITAL_PINS - 1 );
Serial.print( F( "Analog pins=" ) );
for( u8 i = 0; i < sizeof( analog_pin_list ); i++ )
{
Serial.print( analog_pin_list[ i ] );
if( i < sizeof( analog_pin_list ) - 1 )
{
Serial.print( F( "," ) );
}
else
{
Serial.println();
}
}
Serial.println( F( "." ) );
Serial.println( F( "Pin numbers may otherwise be period (all pins) with +/-/! for setting and forcing reserved pins" ) );
Serial.println( F( "Example: pin set to output .-! (results in all pins [.] being set to output with low logic level [-], even reserved pins [!])" ) );
Serial.println( F( "Valid commands, cAsE sEnSiTiVe:" ) );
Serial.println( F( "." ) );
Serial.println( F( "help (show this screen)" ) );
#if not defined ( __LGT8FX8E__ ) && not defined ( ARDUINO_AVR_YUN ) && not defined ( ARDUINO_AVR_LEONARDO ) && not defined ( ARDUINO_AVR_LEONARDO_ETH ) && not defined ( ARDUINO_AVR_MICRO ) && not defined ( ARDUINO_AVR_ESPLORA ) && not defined ( ARDUINO_AVR_LILYPAD_USB ) && not defined ( ARDUINO_AVR_YUNMINI ) && not defined ( ARDUINO_AVR_INDUSTRIAL101 ) && not defined ( ARDUINO_AVR_LININO_ONE )
Serial.println( F( "ther[mostat][ a[uto]/ o[ff]/ h[eat]/ c[ool]] (read or set thermostat mode)" ) );//, auto requires outdoor sensor[s] and reverts to heat if sensor[s] fail)" ) );
#else
Serial.println( F( "ther[mostat][ o[ff]/ h[eat]/ c[ool]] (read or set thermostat mode)" ) );//, auto requires outdoor sensor[s] and reverts to heat if sensor[s] fail)" ) );
#endif
Serial.println( F( "fan[ a[uto]/ o[n]] (read or set fan)" ) );
Serial.println( F( "heat start low temp[ <°C>] (heat turns on at this or lower temperature)" ) );//not worth converting for some unkown odd reason
Serial.println( F( "heat stop high temp[ <°C>] (heat turns off at this or higher temperature)" ) );
Serial.println( F( "cool stop low temp[ <°C>] (A/C turns off at this or lower temperature)" ) );
Serial.println( F( "cool start high temp[ <°C>] (A/C turns on at this or higher temperature)" ) );
// Serial.print( ( const __FlashStringHelper * )szHeat_temps );//uses more bytes this way
Serial.println( F( "heat temps[ <°C>] (adjust heat settings up, use - for down)" ) );
// Serial.print( ( const __FlashStringHelper * )szCool_temps );//uses more bytes this way
Serial.println( F( "cool temps[ <°C>] (adjust cool settings up, use - for down)" ) );
// Serial.print( ( const __FlashStringHelper * )szAll_temps );//uses more bytes this way
Serial.println( F( "all temps[ <°C>] (adjust all temperature settings up, use - for down)" ) );
Serial.println( F( "talkback[ on/off] (or logging[ on/off], off may confuse user)" ) );//(for the host system to log when each output pin gets set high or low, always persistent)" ) );
Serial.println( F( "talkback temp change[s[ on/off]] (or logging temp changes[ on/off])(requires normal talkback on)" ) );// - for the host system to log whenever the main room temperature changes, always persistent)" ) );
Serial.println( ( const __FlashStringHelper * )szReport_master_room_temp );
Serial.println( F( "power cycle (or cycle power)" ) );
Serial.println( F( "read pin <pin number or .> (or ...pin read...)(obtain the name if any, setting and voltage)" ) );
Serial.println( F( "read pins (or pins read )(obtain the names, settings and voltages of ALL pins)" ) );
Serial.println( F( "read sens[or] <pin number or .> (retrieves sensor reading, a period with due care in place of pin number for all pins)" ) );
Serial.println( F( "set pin [to] output <pin number or .> (or ...pin set)" ) );
Serial.println( F( "set pin [to] input <pin number or .> [pers] (or ...pin set...)" ) );
Serial.println( F( "set pin [to] input with pullup <pin number or .> [pers] (or ...pin set...)" ) );
Serial.println( F( "set pin [to] low <pin number or .> [pers] (or ...pin set...)(only allowed to pins assigned as output)" ) );
Serial.println( F( "set pin [to] high <pin number or .> [pers] (or ...pin set...)(only allowed to pins assigned as output)" ) );
Serial.println( F( "ch[ange] pers[istent memory] <address> <value> (changes EEPROM, see source code for addresses of data)" ) );
Serial.println( F( "ch[ange] pers[istent memory] <StartingAddress> \"<character string>[\"[ 0]] (store character string in EEPROM as long as desired, optional null-terminated. Reminder: echo -e and escape the quote[s])" ) );
Serial.println( F( "vi[ew] pers[istent memory] <StartingAddress>[ <EndingAddress>] (views EEPROM)" ) );
#ifdef RESTORE_FACTORY_DEFAULTS
Serial.println( F( "vi[ew] fact[ory defaults] (see what would happen before you reset to them)" ) );
Serial.println( F( "reset (factory defaults: pure, simple and absolute)" ) );
#else
#ifndef __AVR_ATmega32U4__
Serial.println( F( "vi[ew] fact[ory defaults] (sketch re-compile would be required this board. See source code comments)" ) );
#endif
#endif
Serial.println( F( "assign pins (from EEPROM)" ) );
Serial.println( F( "test alert (sends an alert message for testing)" ) );
Serial.println( F( ".." ) );
}
void print_factory_defaults()
{
Serial.print( F( "Version: " ) );
Serial.print( F( VERSION ) );
Serial.println( F( " Factory defaults:" ) );
#if not defined ( __LGT8FX8E__ ) && not defined ( ARDUINO_AVR_YUN ) && not defined ( ARDUINO_AVR_LEONARDO ) && not defined ( ARDUINO_AVR_LEONARDO_ETH ) && not defined ( ARDUINO_AVR_MICRO ) && not defined ( ARDUINO_AVR_ESPLORA ) && not defined ( ARDUINO_AVR_LILYPAD_USB ) && not defined ( ARDUINO_AVR_YUNMINI ) && not defined ( ARDUINO_AVR_INDUSTRIAL101 ) && not defined ( ARDUINO_AVR_LININO_ONE )
Serial.print( F( "Operating mode (heat/cool/auto/off)=" ) );
#else
Serial.print( F( "Operating mode (heat/cool/off)=" ) );
#endif
printThermoModeWord( factory_setting_thermostat_mode, true );
Serial.print( F( "Fan mode=" ) );
if( factory_setting_fan_mode == 'a' ) Serial.println( F( "auto" ) );
else if( factory_setting_fan_mode == 'o' ) Serial.println( F( "on" ) );
Serial.print( ( const __FlashStringHelper * )szHeatStartLowTemp );
Serial.print( '=' );
Serial.println( factory_setting_lower_heat_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szHeatStopHighTemp );
Serial.print( '=' );
Serial.println( factory_setting_upper_heat_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szCoolStopLowTemp );
Serial.print( '=' );
Serial.println( factory_setting_lower_cool_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szCoolStartHighTemp );
Serial.print( '=' );
Serial.println( factory_setting_upper_cool_temp_floated, 1 );
Serial.print( F( "Logging talkback o" ) );
if( bFactory_setting_logging_setting ) Serial.println( F( "n" ) );
else Serial.println( F( "ff" ) );
Serial.print( F( "Logging temp changes o" ) );
if( bFactory_setting_logging_temp_changes_setting ) Serial.println( F( "n" ) );
else Serial.println( F( "ff" ) );
Serial.println();
Serial.print( F( "primary_temp_sensor_pin=" ) );
Serial.println( factory_setting_primary_temp_sensor_pin & 0x7F );
Serial.print( F( "secondary_temp_sensor_pin=" ) );
Serial.println( factory_setting_secondary_temp_sensor_pin & 0x7F );
Serial.print( F( "outdoor_temp_sensor1_pin=" ) );
Serial.println( factory_setting_outdoor_temp_sensor1_pin & 0x7F );
Serial.print( F( "outdoor_temp_sensor_pin=" ) );
Serial.println( factory_setting_outdoor_temp_sensor2_pin & 0x7F );
Serial.print( F( "heat_pin=" ) );
Serial.println( factory_setting_heat_pin );
Serial.print( F( "furnace_blower_pin=" ) );
Serial.println( factory_setting_furnace_blower_pin );
Serial.print( F( "cool_pin=" ) );
Serial.println( factory_setting_cool_pin );
Serial.print( F( "automation system power_cycle_pin=" ) );
Serial.println( factory_setting_power_cycle_pin );
// Serial.print( F( "LED_BUILTIN pin=" ) );
// Serial.println( LED_BUILTIN );
}
void setup()
{
// analogReadResolution(10); //This is default and what this sketch algorithm assumes. 12 bits resolution aren't known to be necessary for a simple home thermostat, nor can the increased compiled sketch size from this command otherwise be justified
Serial.begin( BAUD_RATE );
Serial.setTimeout( 10 );
//read EEPROM addresses 0 (LSB)and 1 (MSB). This sketch expects MSB combined with LSB always contain ( NUM_DIGITAL_PINS + 1 ) * 3 for confidence in the remiander of the EEPROM's contents.
u16 tattoo = 0;
#if defined ( RESTORE_FACTORY_DEFAULTS ) && ( defined ( __LGT8FX8E__ ) || defined ( ARDUINO_AVR_YUN ) || defined ( ARDUINO_AVR_LEONARDO ) || defined ( ARDUINO_AVR_LEONARDO_ETH ) || defined ( ARDUINO_AVR_MICRO ) || defined ( ARDUINO_AVR_ESPLORA ) || defined ( ARDUINO_AVR_LILYPAD_USB ) || defined ( ARDUINO_AVR_YUNMINI ) || defined ( ARDUINO_AVR_INDUSTRIAL101 ) || defined ( ARDUINO_AVR_LININO_ONE ) )
#else
#ifndef __LGT8FX8E__
EEPROM.get( 0, tattoo );
#else
tattoo = EEPROM.read( 0 );
tattoo += ( u16 )( EEPROM.read( 1 ) << 8 );
#endif
#endif
#ifdef __LGT8FX8E__
delay( 10000 );//needed for TTGO XI Serial to initialize
if( ( tattoo != ( NUM_DIGITAL_PINS + 1 ) * 3 ) && ( tattoo != 45 ) ) // Check for tattoo, allow for the obsolete one also
#else
if( tattoo != ( NUM_DIGITAL_PINS + 1 ) * 3 ) // Check for tattoo
#endif
{
while ( !Serial ); // wait for serial port to connect. Needed for Leonardo's native USB
Serial.println();
#ifdef RESTORE_FACTORY_DEFAULTS
Serial.println( F( "Detected first time run so initializing to factory default pin names, power-up states and thermostat assignments, please wait..." ) );
restore_factory_defaults();
#else
while( true )
{
Serial.println( F( "For this board to be a thermostat, EEPROM needs to get set up. Because there is not" ) );
Serial.println( F( "enough room in some boards for the requisite code to be included by default, the" ) );
Serial.println( F( "EEPROM configuring code will need to be executed by the following process:" ) );
Serial.println( F( "1. Modify the soure code of the main .ino file so that the line defining" ) );
Serial.println( F( " RESTORE_FACTORY_DEFAULTS is uncommented back into active code" ) );
Serial.println( F( "2. Don't save file that way, just reload board with modified sketch," ) );
Serial.println( F( " let board execute that modification of the sketch" ) );
Serial.println( F( " once to set up the EEPROM" ) );
Serial.println( F( "3. Revert sketch, reload" ) ); //Some boards just aren't large enough for the verbosity of next lines
// Serial.println( F( " just as the source code was originally - and upload the sketch into the board." ) );
// Serial.println( F( " After restarting the board it will operate as a thermostat." ) );
Serial.println();
delay( 20000 );
}
#endif
}
else
{
assign_pins( NOT_RUNNING );
}
bLogging = ( boolean )EEPROM.read( logging_address );
bLogging_temp_changes = ( boolean )EEPROM.read( logging_temp_changes_address );
thermostat_mode = ( char )EEPROM.read( thermostat_mode_address );
short lower_heat_temp_shorted_times_ten = 0;
short upper_heat_temp_shorted_times_ten = 0;
short upper_cool_temp_shorted_times_ten = 0;
short lower_cool_temp_shorted_times_ten = 0;
#ifndef __LGT8FX8E__
EEPROM.get( lower_heat_temp_address, lower_heat_temp_shorted_times_ten );
#else
lower_heat_temp_shorted_times_ten = EEPROM.read( lower_heat_temp_address );
lower_heat_temp_shorted_times_ten += ( u16 )( EEPROM.read( lower_heat_temp_address + 1 ) << 8 );
#endif
lower_heat_temp_floated = ( float )( ( float )( lower_heat_temp_shorted_times_ten ) / 10 );
#ifndef __LGT8FX8E__
EEPROM.get( upper_heat_temp_address, upper_heat_temp_shorted_times_ten );
#else
upper_heat_temp_shorted_times_ten = EEPROM.read( upper_heat_temp_address );
upper_heat_temp_shorted_times_ten += ( u16 )( EEPROM.read( upper_heat_temp_address + 1 ) << 8 );
#endif
upper_heat_temp_floated = ( float )( ( float )( upper_heat_temp_shorted_times_ten ) / 10 );
delay( 3000 ); // The sensor needs time to initialize, if you have some code before this that make a delay, you can eliminate this delay
//( pin_specified*3 )and ( pin_specified*3 )+1 contains the EEPROM address where the pin's assigned name is stored. Pin 0 will always have its name stored at EEPROM address (NUM_DIGITAL_PINS+1 )*3, so address (NUM_DIGITAL_PINS+1 )*3 will always be stored in EEPROM addresses 0 and 1; 0 = ( pin_number*3 )and 1 = (( pin_number*3 )+1 ) )]
// That will be the way we determine if the EEPROM is configured already or not
// ( pin_specified*3 )+2 is EEPROM address where the pin's desired inital state is stored
#ifndef __LGT8FX8E__
EEPROM.get( upper_cool_temp_address, upper_cool_temp_shorted_times_ten );
#else
upper_cool_temp_shorted_times_ten = EEPROM.read( upper_cool_temp_address );
upper_cool_temp_shorted_times_ten += ( u16 )( EEPROM.read( upper_cool_temp_address + 1 ) << 8 );
#endif
upper_cool_temp_floated = ( float )( ( float )( upper_cool_temp_shorted_times_ten ) / 10 );
#ifndef __LGT8FX8E__
EEPROM.get( lower_cool_temp_address, lower_cool_temp_shorted_times_ten );
#else
lower_cool_temp_shorted_times_ten = EEPROM.read( lower_cool_temp_address );
lower_cool_temp_shorted_times_ten += ( u16 )( EEPROM.read( lower_cool_temp_address + 1 ) << 8 );
#endif
lower_cool_temp_floated = ( float )( ( float )( lower_cool_temp_shorted_times_ten ) / 10 );
bLogging = ( boolean )EEPROM.read( logging_address );
#ifdef PIN_A0
calibration_offset = EEPROM.read( calibration_offset_array_start_address_first_byte );
calibration_offset += ( ( u16 )EEPROM.read( calibration_offset_array_start_address_first_byte + 1 ) ) << 8;
#endif
}
void fixInputted( u8 functionDesired, const char *strToFind, const char *strToReplaceWith, u8 lengthGoal )
{
char *hit;
hit = strstr_P( szFull, strToFind );
if( hit )
{
if( functionDesired == REPLACE )
{
if( strlen_P( strToReplaceWith ) < strlen_P( strToFind ) )
{
functionDesired = SHORTEN;
lengthGoal = strlen_P( strToReplaceWith );
strcpy_P( hit, strToReplaceWith );
}
else
{
strncpy_P( hit, strToReplaceWith, strlen_P( strToReplaceWith ) + lengthGoal );
}
}
if( functionDesired == SHORTEN )
{
memmove( hit + lengthGoal, hit + strlen_P( strToFind ), strlen( hit + strlen_P( strToFind ) ) + 1 );
}
}
}
void printTooHighLow( u8 highOrLow )
{
Serial.print( F( " too " ) );
if( highOrLow == LOW )
Serial.print( F( "low for this value. Raise" ) );
else
Serial.print( F( "high for this value. Lower" ) );
Serial.println( F( " that before trying this value" ) );
}
void LimitSet( float *pSettingOfInterest, unsigned int settingOfInterestAddress, u8 AdjustOrSetfull )
{
if( AdjustOrSetfull == SETFULL )
*pSettingOfInterest = temp_specified_floated;
else
*pSettingOfInterest += temp_specified_floated;
timer_alert_furnace_sent = 0;
short temp_specified_shorted_times_ten = ( short )( *pSettingOfInterest * 10 );
#ifndef __LGT8FX8E__
EEPROM.put( settingOfInterestAddress, temp_specified_shorted_times_ten );
#else
EEPROMupdate( settingOfInterestAddress, ( u8 )temp_specified_shorted_times_ten );
EEPROMupdate( settingOfInterestAddress + 1, ( u8 )( temp_specified_shorted_times_ten >> 8 ) );
#endif
}
void showHeatSettings( void )
{
Serial.print( ( const __FlashStringHelper * )szHeatStartLowTemp );
Serial.print( F( " now " ) );
Serial.println( lower_heat_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szHeatStopHighTemp );
Serial.print( F( " now " ) );
Serial.println( upper_heat_temp_floated, 1 );
}
void showCoolSettings( void )
{
Serial.print( ( const __FlashStringHelper * )szCoolStopLowTemp );
Serial.print( F( " now " ) );
Serial.println( lower_cool_temp_floated, 1 );
Serial.print( ( const __FlashStringHelper * )szCoolStartHighTemp );
Serial.print( F( " now " ) );
Serial.println( upper_cool_temp_floated, 1 );
}
void print_the_pin_and_sensor_reading( u8 pin_specified, u8 KY013orRaw )
{
Serial.print( F( "Pin " ) );
Serial.print( pin_specified );
if( KY013orRaw == KY013 )
{
Serial.print( F( " sensor read: " ) );
DHTresult* noInterrupt_result = ( DHTresult* )FetchTemp( pin_specified, LIVE );
if( noInterrupt_result->ErrorCode == DEVICE_READ_SUCCESS || noInterrupt_result->Type == TYPE_ANALOG )
{
Serial.print( ( float )( ( float )( noInterrupt_result->TemperatureCelsius )/ 10 ), 1 );
Serial.print( F( " °C, " ) );
Serial.print( ( float )( ( float )noInterrupt_result->HumidityPercent / 10 ), 1 );
Serial.print( F( " %" ) );
}
else
{
Serial.print( F( "Error " ) );
Serial.print( noInterrupt_result->ErrorCode );
// Serial.print( F( ", Type " ) );
// Serial.print( noInterrupt_result->Type );//If no type is printed below it it > TYPE_ANALOG
}
if( noInterrupt_result->Type <= TYPE_ANALOG ) Serial.print( F( " TYPE " ) );
if( noInterrupt_result->Type == TYPE_KNOWN_DHT11 ) Serial.print( F( "DHT11" ) );
else if( noInterrupt_result->Type == TYPE_KNOWN_DHT22 ) Serial.print( F( "DHT22" ) );
else if( noInterrupt_result->Type == TYPE_LIKELY_DHT11 ) Serial.print( F( "DHT11?" ) );
else if( noInterrupt_result->Type == TYPE_LIKELY_DHT22 ) Serial.print( F( "DHT22?" ) );
else if( noInterrupt_result->Type == TYPE_ANALOG )
{
Serial.print( F( "assumed ANALOG" ) );
}
}
else
{
printAnalogLevel( pin_specified );
}
Serial.println();
}
void check_for_serial_input()
{
char nextChar;
nextChar = 0;
while( Serial.available() )
{
// pinMode( LED_BUILTIN, OUTPUT ); // These lines for blinking the LED are here if you want the LED to blink when data is rec'd
// digitalWrite( LED_BUILTIN, HIGH ); // These lines for blinking the LED are here if you want the LED to blink when data is rec'd
nextChar = (char)Serial.read();
if( nextChar != 0xD && nextChar != 0xA )
{
szFull[ strlen( szFull ) + 1 ] = 0;
szFull[ strlen( szFull ) ] = nextChar;
}
else
{
if( Serial.available() ) Serial.read();
nextChar = 0;
}
}
// digitalWrite( LED_BUILTIN, LOW ); // These lines for blinking the LED are here if you want the LED to blink when data is rec'd
if( szFull[ 0 ] == 0 || nextChar != 0 ) return; //The way this and while loop is set up allows reception of lines with no endings but at a timing cost of one loop()
char *hit;
fixInputted( REPLACE, szTalkback, szLogging, NOT_TO_END_OF_STRING );
fixInputted( REPLACE, szPin_set, szSet_pin, NOT_TO_END_OF_STRING );
fixInputted( REPLACE, szCycle_power, szPower_cycle, NOT_TO_END_OF_STRING );
fixInputted( REPLACE, szPins_read, szRead_pin_dot, TO_END_OF_STRING );
fixInputted( REPLACE, szRead_pins, szRead_pin_dot, TO_END_OF_STRING );
fixInputted( REPLACE, szSet_pin_to, szSet_pin, NOT_TO_END_OF_STRING );
fixInputted( SHORTEN, szView, 0, 2 );
fixInputted( SHORTEN, szSensor, 0, 4 );
fixInputted( SHORTEN, szPersistent_memory, 0, 4 );
fixInputted( SHORTEN, szChanges, 0, 2 );
fixInputted( SHORTEN, szChange, 0, 2 );
fixInputted( SHORTEN, szThermostat, 0, 4 );
fixInputted( SHORTEN, szFan_auto, 0, 5 );
fixInputted( SHORTEN, szFan_on, 0, 5 );
fixInputted( SHORTEN, szFactory, 0, 4 );
char *address_str;
char *data_str;
char *number_specified_str;
number_specified_str = strrchr( szFull, ' ' ) + 1;
Serial.println( szFull );
if( strstr_P( szFull, szPower_cycle ) )
{
digitalWrite( power_cycle_pin, HIGH );
if( bLogging )
{
Serial.print( F( "time_stamp_this powered off, power control pin " ) );
Serial.print( power_cycle_pin );
Serial.print( '=' );
Serial.print( digitalRead( power_cycle_pin ) );
}
delay( 1500 );
digitalWrite( power_cycle_pin, LOW );
if( bLogging )
{
Serial.print( F( ", powered back on, power control pin " ) );
Serial.print( power_cycle_pin );
Serial.print( '=' );
Serial.println( digitalRead( power_cycle_pin ) );
}
}
else if( strstr_P( szFull, szHeat_temps ) )
{
if( IsValidTemp( number_specified_str, NO_BOUNDS_CHECK ) != NO_TEMP_ENTERED )
{
if( ( bool )IsValidTemp( number_specified_str, HEAT_TEMP_BOUNDS_CHECK + ADJUST_START_AND_STOP_LIMITS_TOGETHER ) )
{
if( temp_specified_floated < 0 )
{
LimitSet( &lower_heat_temp_floated, lower_heat_temp_address, ADJUST );
}
LimitSet( &upper_heat_temp_floated, upper_heat_temp_address, ADJUST );
if( temp_specified_floated >= 0 )
{
LimitSet( &lower_heat_temp_floated, lower_heat_temp_address, ADJUST );
}
}
}
if( bLogging )
{
showHeatSettings();