-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLEDProgrammer_V2-32.ino
903 lines (831 loc) · 34.6 KB
/
LEDProgrammer_V2-32.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
/*
@author: Justin D Vrana
@version v2.32
@since 2014-04-03
What is this?
The basic code needed to control a multi-output, programmable LED light device for optogenetic applications.
The the code allows for user-defined parameters to alter duration and intensity of light pulses at regular intervals.
Central to the device is an inexpensive microcontroller and LCD keypad to display and read user input to control
the duration and intensity of LED light pulses at user-defined intervals. Up to three LED outputs can be independently
programmed and different LED modules can be interchanged.
Usage:
Upload to Arduino Uno via USB. If uploaded properly, "Starting" should display on the LCD display screen and then the main page should display.
If there are issue uploading to the Arduino Uno, consult the Arduino website for help.
To turn an output ON or OFF: From the main page on the LCD display, use the UP and DOWN buttons to select an output (indicated by a cursor)
to edit. Move the cursor to the right using the RIGHT button and use either the UP or DOWN button to turn the output program ON
or OFF. A countdown timer which counts down untill the next time the LED is to flash will display when the output program is running.
To change an output's scheduled flashing: To change an output program, push SELECT to select the output highlighted by the cursor.
Use the UP and DOWN buttons to select and attribute to edit. To edit and attribute, move the cursor to the right using the RIGHT
button and then hold the UP or DOWN button to increase or decease a value. Move the cursor back to the left using the LEFT button
to change attributes to edit. Push the SELECT button at any time to return to the main page.
Sources:
- The original "LiquidCrystal" 8-bit library and tutorial
http://www.arduino.cc/en/uploads/Tutorial/LiquidCrystal.zip
http://www.arduino.cc/en/Tutorial/LCDLibrary
Tested only with a Arduino Uno (http://arduino.cc/) and DFRobot Keypad Shield (http://dfrobot.com)
*****Important*****:
Check and change "User Defined Paramters" directly below if needed. LCD pins, output pins, and anaolog voltages
must be changed if using an LCD Keypad Shield other than the DFRobot Keypad Shield or SainSmart Keypad Shield.
Refer to the datasheet of the LCD Keypad Shield to determine correct parameter values.
Button values can be reset for using the "Button Reset Mode"
Button Reset Mode:
If using a different LCD Keypad shield, button values may need to be reset. To initiate button reset mode, during startup hold
down any button and continue holding button for at least three seconds when prompted. Follow the on screen instructions.
The screen will request you press and hold each button while it records the voltage values associated with that button.
Once completed, voltage values will be stored to the Arduino's EEPROM memory. Upon each start-up, these voltage values
will be loaded and used. To reset button values on the Arduino, simply re-upload this program using the "UPLOAD" button.
V 2.0 - New Version with new interface
V 2.1 - Re-vamped interface
V 2.2 - Expanded to three outputs
V2.3 - Created button reset mode
V2.32 (4/10/14) - Corrected coding error, cast timers to int instead of long
*/
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////////// User Defined Parameters ////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/*/////////////////// User Defined LCD Display Parameters ////////////////////
// Check LCD Keypad Shield datasheet to determine which Arduino Uno pins correspond
// to the RS, Enable, D4, D5, D6, and D7 pins on the LCD Keypad Shield. Change the
// values below. For example, DFRobot Keypad Shield for Arduino designates:
// Digital Pin 9 on the Arduino to the reset pin (RS) on the LCD Keypad
// Digital Pin 8 on the Arduino Uno to the Enable pin on the LCD keypad
// Digital Pin 4 on the Arduino Uno to the D4 pin on the LCD keypad shield
// Digital Pin 5 on the Arduino Uno to the D5 pin on the LCD keypad shield
// Digital Pin 6 on the Arduino Uno to the D6 pin on the LCD keypad shield
// Digital Pin 7 on the Arduino Uno to the D7 pin on the LCD keypad shield
// Analog Pin 0 on the Arduino Uno to the Button select pin on the LCD keypad shield
///////////////////////////////////////////////////////////////////////////////*/
int RS_pin = 8;
int enable_pin = 9;
int D4_pin = 4;
int D5_pin = 5;
int D6_pin = 6;
int D7_pin = 7;
int button_select_pin = 0;
/*//////////////////// User Defined LCD Display Parameters ////////////////////
// Check LCD Keypad Shield datasheet to determine which Arduino Uno pins are
// available for use as output pins. Pins designated to the RS, Enable, D4, D5,
// D6, D7, and Button select pins (see above) on the LCD Keypad shield are
// unavailable for use as LED output pins. For example, DFRobot Keypad Shield
// has pins 12, 11, and 3 pins free.
///////////////////////////////////////////////////////////////////////////////*/
int LED_output_pin_1 = 12;
int LED_output_pin_2 = 11;
int LED_output_pin_3 = 3;
/*//////////////////// User Defined Analog Button Parameters ////////////////////
// Buttons on the LCD Keypad Shield change the voltage to the button_select_pin.
// Different values being recorded correspond to which button is being pressed
// Depending on LCD Keypad shield, the following values may be different. Button
// values can be reset live if desired by pressing and holding any button for
// three seconds during startup.
///////////////////////////////////////////////////////////////////////////////*/
//DFRobot LCD Keypad Shield Values
int btnNONE_upper_voltage = 1000;
int btnRIGHT_upper_voltage = 0;
int btnUP_upper_voltage = 150;
int btnDOWN_upper_voltage = 330;
int btnLEFT_upper_voltage = 500;
int btnSELECT_upper_voltage = 750;
int button_threshold = 50;
/*
//SainSmart LCD Keypad Shield Values
int btnNONE_upper_voltage = 1023;
int btnRIGHT_upper_voltage = 0;
int btnUP_upper_voltage = 144;
int btnDOWN_upper_voltage = 329;
int btnLEFT_upper_voltage = 505;
int btnSELECT_upper_voltage = 742;
*/
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/////////////// END User Defined Parameters ////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
///////////////////// VARIABLES ////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
#include <LiquidCrystal.h>
#include <EEPROM.h>
LiquidCrystal lcd(RS_pin, enable_pin, D4_pin, D5_pin, D6_pin, D7_pin); // Used to display information to LCD Display.
///////////////////// struct output ////////////////////
// Defines LED output parameters
// Name: name of output
// pin: which pin controls this output
// runs_status: is this output program currently running?
// run_state: is the LED currently powered on?
// attributes: defines duration, interval, and intensity for this input
// attribute_labels: labels for interval, interval, and intensity
// interval_timer: counter used to determine when to power ON led (change state = true)
// duration_timer: counter used to determine when to power OFF led (change state = false)
///////////////////// END struct output ////////////////////
struct output{
String name;
int pin;
boolean run_status;
boolean run_state;
int attributes[3];
String attribute_labels[3];
long interval_timer;
long duration_timer;
} out_1, out_2, out_3; // define outputs 1, 2, and 3
int LED_output_pins[3] = {LED_output_pin_1, LED_output_pin_2, LED_output_pin_3}; // Designates output pins on Arduino used to control LED outputs
output LED_outputs[3] = {out_1, out_2, out_3}; // Array for each output. Index is used later in program to grab corresponding output.
int selected_item = 0; // Used to designate item highlighted on LCD Display.
int current_page = 0; // Current page beging viewed (0 = top, 1 = edit).
int selected_output = 0; // Designate last selected output. Index to determine which output structure to retrieve from "LED_outputs" array.
int top_item_displayed = 0; // Which item is displayed at top of page.
int cursorPositionY = 0; // Designates where the cursor should be (0=top row, 1=bottom row).
int cursorPositionX = 0; // Designates where the cursor should be (0=1st character, 1=12th character).
int lcd_key = 0; // Analog input key pressed. Example: btnRIGHT = 0
int prev_lcd_key = 0; // Previous analog input key pressed.
// long button_hold_interval_counter: Used to increase or decrease a value while holding the UP or DOWN buttons.
// Designates total time since value was increased while holding button.
long button_hold_interval_counter = 0;
// long button_hold_start_counter: Used to increase or decrease a value while holding the UP or DOWN buttons.
// Designates total time button is held.
long button_hold_start_counter = 0;
int button_values[6] = {btnRIGHT_upper_voltage, btnUP_upper_voltage, btnDOWN_upper_voltage,
btnLEFT_upper_voltage, btnSELECT_upper_voltage, btnNONE_upper_voltage}; //Contains voltage values for each button
String button_names[6] = {"RIGHT", "UP", "DOWN", "LEFT", "SELECT", "NONE"}; // Contains names for each button
int button_addresses[6] = {101, 102, 103, 104, 105, 106}; // Contains EEPROM address for each button value
byte button_override_true = 111; // Contains value to override button values (after button reset mode)
int button_override_address = 111; // Address of whether to overrid buttons
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
///////////////////// DEFINITIONS ////////////////////////////
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Definitions used to determine pages.
// Definitions used to determine button pushed on lcd keypad
#define EDITPAGE 1
#define TOPPAGE 0
#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5
#define btnOverride 111
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/////////////////// MAIN METHODS (setup and loop //////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////// method: setup() ////////////////////
// Method initializes upon Arduino Uno startup and
// initialized variables for LED_outputs and LCD display
// Returns: void
///////////////////// END Setup ////////////////////
void setup() {
Serial.begin(9600);
// Initialize output values for each LED output
for (int i = 0; i < 3; i++) {
LED_outputs[i].name = "Output ";
LED_outputs[i].name = LED_outputs[i].name + (i + 1); // Name of this output to be displayed on the LCD display
LED_outputs[i].pin = LED_output_pins[i]; // Designates this output to a pin on the Arduino Uno
LED_outputs[i].run_status = false; // Determines if the output program is currently running (true) or not running (false)
LED_outputs[i].run_state = false; // Determines if the LED is powered (true) or not powered (false)
LED_outputs[i].attributes[0] = 1; // Value defines the duration of the LED light pulse
LED_outputs[i].attributes[1] = 10; // Value defines the interval duration of the LED light pulse
LED_outputs[i].attributes[2] = 100; // Value defines the intensity of the LED light pulse (0%-100%)
LED_outputs[i].attribute_labels[0] = "Dura(s)"; // Corresponding label for duration to be displayed on the LCD display
LED_outputs[i].attribute_labels[1] = "Int(s)"; // Corresponding label for interval to be displayed on the LCD display
LED_outputs[i].attribute_labels[2] = "Intensity(%)"; // Corresponding label for intensity to be displayed on the LCD display
}
lcd.begin(16,2);
lcd.print("Starting");
delay(300);
lcd.clear();
lcd.print("Starting.");
delay(300);
lcd.clear();
lcd.print("Starting..");
delay(300);
lcd.clear();
lcd.print("Starting...");
Serial.print("Checking for button reset mode\n");
buttonReset(); // Check for button and activate button reset if needed.
Serial.print("Loading values from memory\n");
loadButtonValuesFromMemory(); // Only loads if button reset has been previously activated.
delay(300);
lcd.clear();
lcd.blink(); // Sets cursor blinker ON
lcd.cursor(); // Sets underline cursor ON
printScreen();
button_hold_interval_counter = millis();
}
///////////////////// method: loop() ////////////////////
// Method is automatically looped by the Arduino Uno microcontroller continuously
// returns: void
///////////////////// END loop() ////////////////////
void loop() {
flash();
printScreen();
displayCursor();
delay(50);
lcd_key = read_LCD_buttons(); //Read analog input from buttons
if (lcd_key != prev_lcd_key) { //New key is pressed
button_hold_start_counter = millis(); // Timer for how long button is held
switch (lcd_key) { // depending on which button was pushed, we perform an action
case btnRIGHT:{
if (cursorPositionX == 0) { cursorPositionX = 1; }
delay(50);
break;
}
case btnLEFT:{
if (cursorPositionX == 1) {
cursorPositionX = 0;
}
break;
}
case btnUP:{
if (cursorPositionX == 0) {
lcd.clear();
prevItem();
}
else if (cursorPositionX == 1 && current_page == TOPPAGE) {
if (LED_outputs[selected_item].run_status == false) {
turnOnOutput(selected_item);
} else {
turnOffOutput(selected_item);
}
}
lcd.clear();
printScreen();
break;
}
case btnDOWN:{
if (cursorPositionX == 0) {
lcd.clear();
nextItem();
}
else if (cursorPositionX == 1 && current_page == TOPPAGE) {
if (LED_outputs[selected_item].run_status == false) {
turnOnOutput(selected_item);
} else {
turnOffOutput(selected_item);
}
}
lcd.clear();
printScreen();
break;
}
case btnSELECT:{
if (current_page == TOPPAGE) {
selected_output = selected_item;
toEditPage();
}
else if (current_page == EDITPAGE) {
toTopPage();
}
break;
}
case btnNONE:{
break;
}
}
}
// Else if on edit page, with cursor fartherest right and the UP or DOWN button is pressed
else if (cursorPositionX == 1 && current_page == EDITPAGE && (lcd_key == btnDOWN || lcd_key == btnUP)) {
int * value;
value = &LED_outputs[selected_output].attributes[selected_item];
increaseValueWhileHoldingButton(value); // Either increase or decease value of attribute while holding the UP or DOWN button
lcd.clear(); // Clear screen
printScreen(); // Print to screen
delay(50);
}
prev_lcd_key = lcd_key; // Save previous key pressed
}
void turnOffOutput(int which_output) {
LED_outputs[which_output].run_status = false;
LED_outputs[which_output].run_state = false;
LED_outputs[which_output].interval_timer = millis();
LED_outputs[which_output].duration_timer = millis();
}
void turnOnOutput(int which_output) {
LED_outputs[which_output].run_status = true;
LED_outputs[which_output].run_state = true;
LED_outputs[which_output].interval_timer = millis();
LED_outputs[which_output].duration_timer = millis();
}
///////////////////// method: flash() ////////////////////
// Uses interval_timer and duration_timer of each output to determine when to
// power LEDs ON or OFF. Duration and interval attributes (attributes[0] and attributes[1] respectively)
// are used to determine the length of the duration and interval. Intensity (attribute[2]) is used to
// determine the how much power goes to the LEDs.
// returns: void
///////////////////// END flash() ////////////////////
void flash() {
for (int i = 0; i < 3; i++) {
unsigned long current_time = millis();
/* //Serial output
if (i == i) {
Serial.print("Output: ");
Serial.print(i);
Serial.print( " Run Status: ");
Serial.print(LED_outputs[i].run_status);
Serial.print( " LED States: ");
Serial.print(LED_outputs[i].run_state);
Serial.print(" Current time: ");
Serial.print(millis()/1000);
Serial.print( " Interval Timer: ");
Serial.print(LED_outputs[i].interval_timer/1000);
Serial.print( " Duration Timer: ");
Serial.print(LED_outputs[i].duration_timer/1000);
Serial.print( " Interval: ");
Serial.print(LED_outputs[i].attributes[1]);
Serial.print( " Duration: ");
Serial.print(LED_outputs[i].attributes[0]);
Serial.print(" ");
Serial.print(current_time - LED_outputs[i].interval_timer);
Serial.print( " > " );
unsigned long this_interval = LED_outputs[i].attributes[1] * 1000.0;
Serial.print(this_interval);
Serial.print("\n");
}
/**/
if (LED_outputs[i].run_status) {
boolean state = LED_outputs[i].run_state;
if (current_time - LED_outputs[i].interval_timer > LED_outputs[i].attributes[1]*1000.0 && !state) {
LED_outputs[i].run_state = true;
LED_outputs[i].interval_timer = current_time; // reset interval timer
LED_outputs[i].duration_timer = current_time; // reset duration timer
}
if (millis() - LED_outputs[i].duration_timer > LED_outputs[i].attributes[0] * 1000.0 && state) {
LED_outputs[i].run_state = false;
LED_outputs[i].duration_timer = current_time; // reset duration timer
}
if (LED_outputs[i].run_state) {
analogWrite(LED_outputs[i].pin, map(LED_outputs[i].attributes[2], 0, 100, 0, 255));
}
else {
digitalWrite(LED_outputs[i].pin, LOW);
}
}
else {
LED_outputs[i].interval_timer = current_time;
LED_outputs[i].duration_timer = current_time;
digitalWrite(LED_outputs[i].pin, LOW);
}
}
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/////////////////// METHODS FOR USER DISPLAY AND INTERACTION //////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////// method: toTopPage() ////////////////////
// Simply returns to the first page to display the outputs.
// Text is scrolled left and cursor position is reset.
// returns: void
///////////////////// END toTopPage() ////////////////////
void toTopPage() {
scrollText(false, 50, 16);
lcd.clear();
cursorPositionY = 0;
cursorPositionX = 0;
current_page = TOPPAGE;
selected_item = 0;
top_item_displayed = 0;
printScreen();
}
///////////////////// method: toEditPage() ////////////////////
// Simply returns to the edit page to display the output attributes
// Text is scrolled right and cursor position is reset.
// returns: void
///////////////////// END toEditPage() ////////////////////
void toEditPage() {
scrollText(true, 50, 16);
lcd.clear();
cursorPositionY = 0;
cursorPositionX = 0;
current_page = EDITPAGE;
selected_item = 0;
top_item_displayed = 0;
printScreen();
}
///////////////////// method: display() ////////////////////
// Displays cursor at position as determined by cursorPositionX
// and cursorPositionY
// returns: void
///////////////////// END displayCursor() ////////////////////
void displayCursor() {
if (cursorPositionY == 0 && cursorPositionX == 0) {
lcd.setCursor(0,0);
}
else if (cursorPositionY == 1 && cursorPositionX == 0) {
lcd.setCursor(0,1);
}
else if (cursorPositionY == 0 && cursorPositionX == 1) {
lcd.setCursor(13,0);
}
else if (cursorPositionY == 1 && cursorPositionX == 1) {
lcd.setCursor(13,1);
}
}
///////////////////// method: printScreen() ////////////////////
// Prints to LCD screen. Output depends on which page and which
// item is selected.
// returns: void
///////////////////// END printScreen() ////////////////////
void printScreen() {
if (current_page == EDITPAGE) {
lcd.setCursor(0,0);
lcd.print(LED_outputs[selected_output].attribute_labels[top_item_displayed]);
lcd.setCursor(9,0);
lcd.print(convertSeconds(LED_outputs[selected_output].attributes[top_item_displayed]));
lcd.setCursor(0,1);
lcd.print(LED_outputs[selected_output].attribute_labels[top_item_displayed+1]);
if (top_item_displayed + 1 == 2) {
lcd.setCursor(13,1);
lcd.print(LED_outputs[selected_output].attributes[top_item_displayed+1]);
} else {
lcd.setCursor(9,1);
lcd.print(convertSeconds(LED_outputs[selected_output].attributes[top_item_displayed+1]));
}
} else if (current_page == TOPPAGE) {
lcd.setCursor(0,0);
lcd.print(LED_outputs[top_item_displayed].name);
if (LED_outputs[top_item_displayed].run_state) {
lcd.print("*");
}
else {
lcd.print(" ");
}
lcd.setCursor(13,0);
int out_status = LED_outputs[top_item_displayed].run_status;
if (out_status == 1) {
lcd.setCursor(10,0);
lcd.print(convertSeconds(LED_outputs[top_item_displayed].attributes[1] - (millis() - LED_outputs[top_item_displayed].interval_timer)/1000));
lcd.print(" ");
}
else {
lcd.print("OFF");
}
lcd.setCursor(0,1);
lcd.print(LED_outputs[top_item_displayed+1].name);
if (LED_outputs[top_item_displayed+1].run_state) {
lcd.print("*");
}
else {
lcd.print(" ");
}
lcd.setCursor(13,1);
out_status = LED_outputs[top_item_displayed+1].run_status;
if (out_status == 1) {
lcd.setCursor(10,1);
lcd.print(convertSeconds(LED_outputs[top_item_displayed+1].attributes[1] - (millis() - LED_outputs[top_item_displayed+1].interval_timer)/1000));
lcd.print(" ");
}
else {
lcd.print("OFF");
}
}
}
///////////////////// method: convertSeconds() ////////////////////
// Converts an integer to a 00:00 format
// seconds: integer of how many seconds to convert
// returns: String time in format of 00:00
///////////////////// END convertSeconds() ////////////////////
String convertSeconds(int seconds) {
int m = seconds/60;
int s = seconds - m * 60.0;
String time = "";
if (m < 10) {
time = time + "0" + m;
}
else {
time = time + m;
}
time = time + ":";
if (s < 10) {
time = time + "0" + s;
} else {
time = time + s;
}
return time;
}
///////////////////// method: nextItem() ////////////////////
// Moves to next item. Moves cursor down. If cursor is down, scroll
// page down.
// returns: void
///////////////////// END nextItem() ////////////////////
void nextItem() {
int pageLength = 3;
if (cursorPositionY == 1) {
top_item_displayed = top_item_displayed + 1;
if (top_item_displayed > pageLength - 2) {
top_item_displayed = pageLength - 2;
}
else {
cursorPositionY = 0;
}
}
else {
cursorPositionY = 1;
}
selected_item = cursorPositionY + top_item_displayed;
printScreen();
}
///////////////////// method: prevItem() ////////////////////
// Moves to previous item. Moves cursor up. If cursor is up, scroll
// page up.
// returns: void
///////////////////// END prevItem() ////////////////////
void prevItem() {
if (cursorPositionY == 0) {
top_item_displayed = top_item_displayed - 1;
if (top_item_displayed < 0) {
top_item_displayed = 0;
}
else {
cursorPositionY = 1;
}
}
else {
cursorPositionY = 0;
}
selected_item = cursorPositionY + top_item_displayed;
printScreen();
}
///////////////////// method: increaseValueWhileHoldingButton() ////////////////////
// If button is being held, increase or decrease value designated by *value depending
// on how long the UP or DOWN button is held. The longer the button is held, the faster
// the value is changed.
// int *value: pointer to value to decrease or increase
// returns: void
///////////////////// END increaseValueWhileHoldingButton() ////////////////////
void increaseValueWhileHoldingButton(int *value) {
if (millis()-button_hold_interval_counter > 100) {
int increment = 0;
if (millis() - button_hold_start_counter < 2000 || *value % 10 > 0) {
increment = 1;
}
else if (millis() - button_hold_start_counter > 2000 && millis() - button_hold_start_counter < 5000) {
increment = 10;
}
else if (millis() - button_hold_start_counter > 5000 && millis() - button_hold_start_counter < 10000) {
increment = 30;
}
else if (millis() - button_hold_start_counter > 10000) {
increment = 60;
}
if (lcd_key == btnDOWN) {
increment = increment * -1;
}
*value = *value + increment;
if (*value < 0) {
*value = 0;
}
button_hold_interval_counter = millis();
}
}
///////////////////// method: scrollText() ////////////////////
// Scrolls the current text left or right
// boolean scrollLeft: which direction to scroll text
// int scrollDelay: how quickly to scroll text (how many ms to move to next position)
// int number: how far to scroll the text
// returns: void
///////////////////// END scrollText() ////////////////////
void scrollText(boolean scrollLeft, int scrollDelay, int number) {
for (int i = 0; i < number; i++) {
// wait a bit:
delay(scrollDelay);
if (!scrollLeft) {
lcd.scrollDisplayRight();
}
else {
lcd.scrollDisplayLeft();
}
}
lcd.clear();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/////////////////// METHODS FOR ANALOG INPUT AND BUTTON RESET MODE/////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////// method: read_LCD_buttons() ////////////////////
// Returns which button was pressed.
// returns: int which button was pushed
///////////////////// END read_LCD_buttons() ////////////////////
int read_LCD_buttons() { // read the buttons
button_select_pin = analogRead(0); // read the value from the sensor
if (button_select_pin > btnNONE_upper_voltage) return btnNONE;
for (int i = 0; i < 5; i++) {
if (button_select_pin < button_values[i] + button_threshold &&
button_select_pin > button_values[i] - button_threshold) {
return i;
}
}
return btnNONE; // when all others fail, return this.
}
///////////////////// method: buttonReset() ////////////////////
// Used during Button Reset Mode.
// If button is pressed and held for 3 seconds, initial button reset mode
// For each button, promt a request for voltage value of each button.
// Save values to EEPROM memory
// returns: void
///////////////////// END buttonReset() ////////////////////
void buttonReset() {
Serial.print("Button Reset mode activated\n");
button_select_pin = analogRead(0);
if (button_select_pin < btnNONE_upper_voltage) { //If button is being pressed
int countdownTimer = 5;
lcd.clear();
while (button_select_pin < btnNONE_upper_voltage && countdownTimer >= 1) {
lcd.setCursor(0,0);
lcd.print("Btn Rst in ");
lcd.print(countdownTimer);
delay(1000);
countdownTimer = countdownTimer - 1;
lcd_key = read_LCD_buttons();
}
if (countdownTimer <= 1) {
//Btn Rst activated
lcd.clear();
lcd.print("Btn Rst");
lcd.setCursor(0,1);
lcd.print("activated");
delay(1000);
for (int i = 0; i < 5; i++) {
waitForButtonRelease();
delay(500);
button_values[i] = retrieveAverageButtonValue(button_names[i]);
delay(500);
}
lcd.clear();
lcd.print("Btn Rst");
lcd.setCursor(0,1);
lcd.print("Completed");
saveButtonValuesToMemory();
delay(2000);
}
}
}
///////////////////// method: waitForButtonRelease() ////////////////////
// Used during Button Reset Mode.
// Requests button release for program continues
// returns: void
///////////////////// END waitForButtonRelease() ////////////////////
void waitForButtonRelease() {
lcd.clear();
button_select_pin = analogRead(0);
while (button_select_pin < btnNONE_upper_voltage) {
lcd.setCursor(0,0);
lcd.print("Release");
lcd.setCursor(0,1);
lcd.print("all buttons");
lcd_key = read_LCD_buttons();
delay(50);
}
lcd.clear();
lcd.print("Btn released");
}
///////////////////// method: retrieveAverageValue() ////////////////////
// Used during Button Reset Mode.
// Demands a button to be pressed.
// Records up to 50 values and averages value.
// param: name of button requested
// returns: average voltage value of button
///////////////////// END retrieveAverageValue() ////////////////////
int retrieveAverageButtonValue(String whichButton) {
long total = 0;
int count = 1;
int numValues = 50;
int average_value = 0;
waitForButtonRelease();
delay(500);
demandButtonPress(whichButton);
button_select_pin = analogRead(0);
lcd.clear();
while (button_select_pin < btnNONE_upper_voltage && count <= numValues) {
button_select_pin = analogRead(0);
lcd.setCursor(0,0);
lcd.print("Hold btn ");
lcd.print(count);
lcd.print("/");
lcd.print(numValues);
lcd.print(" ");
total = total + button_select_pin;
average_value = total / count;
lcd.setCursor(0,1);
lcd.print("Avg: ");
lcd.print(average_value);
lcd.print(" ");
count = count + 1;
delay(50);
}
lcd.clear();
lcd.print("button set:");
lcd.setCursor(0,1);
lcd.print(average_value);
return average_value;
}
///////////////////// method: demandButtonPress() ////////////////////
// Used during Button Reset Mode.
// Demands a button to be pressed.
// param: name of button requested
// returns: voltage value of button
///////////////////// END demandButtonPress() ////////////////////
int demandButtonPress(String whichButton) {
lcd.clear();
button_select_pin = analogRead(0);
while (button_select_pin > btnNONE_upper_voltage) {
lcd.setCursor(0,0);
lcd.print("Press and hold .[");
lcd.setCursor(0,1);
lcd.print(whichButton);
lcd_key = read_LCD_buttons();
delay(50);
}
lcd.clear();
lcd.print("btn pressed");
lcd.setCursor(0,1);
lcd.print(button_select_pin);
return button_select_pin;
}
///////////////////// method: loadButtonValuesFromMemory() ////////////////////
// Used during setup()
// Load button voltage values from EEPROM memory. Finds an appropriate threshold
// for those values. Displays those values on screen.
// returns: void
///////////////////// END loadButtonValuesFromMemory() ////////////////////
void loadButtonValuesFromMemory() {
byte overrideButtons = EEPROM.read(button_override_address);
if (overrideButtons == button_override_true) {
for (int i = 0; i < 6; i++) {
button_values[i] = EEPROM.read(button_addresses[i]) * 10; // Multiple by 10 since EEPROM can only store values up to 255
}
// Copy button_values and sort values.
int copy_array[5] = {0,0,0,0,0}; // a copy array of button values to be sorted
for (int i = 0; i < 5; i++) {
copy_array[i] = button_values[i];
}
for (int nStartIndex = 0; nStartIndex < 5; nStartIndex++) {
int nSmallestIndex = nStartIndex;
for (int nCurrentIndex = nStartIndex + 1; nCurrentIndex < 5; nCurrentIndex++) {
if (copy_array[nCurrentIndex] < copy_array[nSmallestIndex]) {
nSmallestIndex = nCurrentIndex;
}
}
int temp_value = copy_array[nStartIndex];
copy_array[nStartIndex] = copy_array[nSmallestIndex];
copy_array[nSmallestIndex] = temp_value;
}
// Calculate average distance between values. Set threshold to 1/3 of that distance.
int total_distance = 0;
for (int i = 0; i < 4; i++) {
total_distance = total_distance + (copy_array[i+1] - copy_array[i])/3;
}
button_threshold = total_distance/3;
lcd.clear();
lcd.print("Button values");
lcd.setCursor(0,1);
lcd.print("loaded");
delay(1000);
lcd.clear();
lcd.print("Btn thrshld set");
lcd.setCursor(0,1);
lcd.print(button_threshold);
delay(500);
lcd.clear();
for (int i = 0; i < 3; i++) {
lcd.print(button_values[i]);
lcd.print(" ");
}
lcd.setCursor(0,1);
for (int i = 3; i < 6; i++) {
lcd.print(button_values[i]);
lcd.print(" ");
}
delay(500);
}
// Else use default values
}
///////////////////// method: saveButtonValuesToMemory() ////////////////////
// Used during Button Reset Mode.
// Saves each button value to designate button address on EEPROM memory.
// Saves that these values were altered (button_override_true) so next start-up
// will load EEPROM button values.
// returns: void
///////////////////// END saveButtonValuesToMemory() ////////////////////
void saveButtonValuesToMemory() {
for (int i = 0; i < 6; i++) {
EEPROM.write(button_addresses[i], button_values[i]/10); //Divide by 10 since EEPROM can only store values up to 255
}
EEPROM.write(button_override_address, button_override_true); //From now on, program will use stored EPPROM values
}