-
Notifications
You must be signed in to change notification settings - Fork 0
/
waterfall.ino
262 lines (191 loc) · 7.9 KB
/
waterfall.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
#include <LocoNet.h>
#include <avr/power.h>
#include "Animation.cpp"
CRGB strip[STRIP_AMOUNT][STRIP_LENGTH]; // declares array of LED strips
bool not_visible;
uint8_t dim_current;
uint8_t dim_target;
uint8_t animation_selector;
lnMsg *LnPacket;
LocoNetThrottleClass Throttle;
/*
* Animationa declaration and related colours go here.
* And yes, throughout this code I chose to use the Canadian spelling of colour. Sorry.
*
*/
// HSV values: H: Hue, S: Saturation (whiteness), V: Value (brighness)
Waterfall *waterfall;
const CHSV WATER(70, 180, 32);
const CHSV HIGHLIGHT(70, 90, 48);
const CHSV HIGHLIGHT_RATE(0, 15, 2);
const CHSV SHIMMER(20, 180, 32);
const CHSV SHIMMER_RATE(4, 0, 0);
OHML *ohml;
const CHSV ORANGE(150, 255, 255); // 255 (don't go below 32)
const CHSV WHITE(150, 64, 32); // 32
Waterfall *canadaDayFall;
const CHSV CD_WATER(130, 180, 32);
const CHSV CD_HIGHLIGHT(130, 90, 48);
const CHSV CD_HIGHLIGHT_RATE(0, 15, 2);
const CHSV CD_SHIMMER(130, 180, 32);
const CHSV CD_SHIMMER_RATE(4, 0, 0);
Leaf *leaf;
const CHSV RED(150, 255, 255);
/*
* HELPER METHODS
* Do not use or edit.
* ...or don't even worry about them.
*
*/
// method controlling brightness for day / night modes based off of pin input
void dim_on_pin() {
if (digitalRead(DAY_PIN) == HIGH && dim_current != 0) {
dim_current = (dim_current <= DIM_RATE) ? 0 : dim_current - DIM_RATE;
} else if (digitalRead(DAY_PIN) == LOW && dim_current != DIM_PERCENT) {
dim_current = (DIM_PERCENT - dim_current <= DIM_RATE) ? DIM_PERCENT : dim_current + DIM_RATE;
}
for (int strip_num = 0; dim_current != 0 && strip_num < STRIP_AMOUNT; ++strip_num) {
strip[strip_num][START].fadeLightBy(dim_current);
}
}
// method controlling brightness for day / night modes based odd of class variable controlled by LocoNet
void dim_with_fading() {
if (dim_current > dim_target) {
dim_current = (dim_current - dim_target <= DIM_RATE) ? dim_target : dim_current - DIM_RATE;
} else if (dim_current < dim_target){
dim_current = (dim_target - dim_current <= DIM_RATE) ? dim_target : dim_current + DIM_RATE;
}
if(dim_current != 0) {
for (int strip_num = 0; strip_num < STRIP_AMOUNT; ++strip_num) {
strip[strip_num][START].fadeLightBy(dim_current);
}
}
}
// simplified version of method above
void dim_quick() {
if(dim_target != 0) {
for (int strip_num = 0; strip_num < STRIP_AMOUNT; ++strip_num) {
strip[strip_num][START].fadeLightBy(dim_target);
}
}
}
// shifts every light down to create illusion of flow
void flow(int wait_time) {
// if the direction in the software is backwards, turn off the flow; the animations will continue running but they will not be visible
if (not_visible) {
for (int strip_num = 0; strip_num < STRIP_AMOUNT; ++strip_num) {
strip[strip_num][START] = 0;
}
}
dim_quick();
FastLED.show();
for (int strip_num = 0; strip_num < STRIP_AMOUNT; ++strip_num) {
for (int pos = STRIP_LENGTH - 1; pos > START; --pos) {
strip[strip_num][pos] = strip[strip_num][pos - 1];
}
}
delay(wait_time);
}
/*
* Scary LocoNet stuff.
*
*/
// user-defined callback functions
void notifyThrottleAddress(uint8_t UserData, TH_STATE State, uint16_t Address, uint8_t Slot) {Serial.print("Address: "); Serial.println(Address);}
void notifyThrottleSlotStatus(uint8_t UserData, uint8_t Status) {Serial.print("Slot Status: "); Serial.println(Status);}
void notifyThrottleError(uint8_t UserData, TH_ERROR Error) {Serial.print("Error: "); Serial.println(Throttle.getErrorStr(Error));}
void notifyThrottleState(uint8_t UserData, TH_STATE PrevState, TH_STATE State) {Serial.print("State: "); Serial.println(Throttle.getStateStr(State));}
void notifyThrottleSpeed(uint8_t UserData, TH_STATE State, uint8_t Speed) {Serial.print("Speed: "); Serial.println(Speed); dim_target = 2 * (127 - Speed);}
void notifyThrottleDirection(uint8_t UserData, TH_STATE State, uint8_t Direction) {Serial.print("Direction: "); Serial.println(not_visible = Direction);}
void notifyThrottleFunction(uint8_t UserData, uint8_t Function, uint8_t Value) {Serial.print("Function: "); Serial.println(Function);
animation_selector = (Function < NUM_ANIMATIONS) ? Function : animation_selector;}
// used for printing and processing LocoNet packets
void lnUpdate() {
LnPacket = LocoNet.receive();
if (LnPacket) {
// First print out the packet in HEX
Serial.print("RX: ");
uint8_t msgLen = getLnMsgSize(LnPacket);
for (uint8_t x = 0; x < msgLen; x++)
{
uint8_t val = LnPacket->data[x];
// Print a leading 0 if less than 16 to make 2 HEX digits
if (val < 16)
Serial.print('0');
Serial.print(val, HEX);
Serial.print(' ');
}
// If this packet was not a Switch or Sensor Message then print a new line, and process it as a Throttle Message
if (!LocoNet.processSwitchSensorMessage(LnPacket)) {
Serial.println();
Throttle.processMessage(LnPacket);
}
}
}
/*
* RUNNABLE FUNCTIONS
* Feel free to use, but do not edit.
*/
// runs an animation with a given delay; default is no delay
void animation_runner(Animation *a1, int wait_time = 0) {
CHSV *row = a1->getNext(animation_selector);
while(row != NULL) {
for (int strip_num = 0; strip_num < STRIP_AMOUNT; ++strip_num) {
strip[strip_num][START] = row[strip_num];
}
flow(wait_time);
row = a1->getNext(animation_selector);
lnUpdate();
}
}
/*
* DEFAULT ARDUINO METHODS
* Feel free to edit at will.
*
*/
// initial setup function
void setup() {
// initialization of hardware and background variables (try not to edit this)
Serial.begin(9600);
pinMode(DAY_PIN, INPUT);
if (0 < STRIP_AMOUNT) FastLED.addLeds<APA102, STRIP0_DATA_PIN, STRIP0_CLOCK_PIN>(strip[0], STRIP_LENGTH);
if (1 < STRIP_AMOUNT) FastLED.addLeds<APA102, STRIP1_DATA_PIN, STRIP1_CLOCK_PIN>(strip[1], STRIP_LENGTH);
if (2 < STRIP_AMOUNT) FastLED.addLeds<APA102, STRIP2_DATA_PIN, STRIP2_CLOCK_PIN>(strip[2], STRIP_LENGTH);
if (3 < STRIP_AMOUNT) FastLED.addLeds<APA102, STRIP3_DATA_PIN, STRIP3_CLOCK_PIN>(strip[3], STRIP_LENGTH);
if (4 < STRIP_AMOUNT) FastLED.addLeds<APA102, STRIP4_DATA_PIN, STRIP4_CLOCK_PIN>(strip[4], STRIP_LENGTH);
FastLED.show(); //display strips to clear previous run
Serial.println("Initialized Strips");
// initialize Animation objects here; see possible Animations and their constructors in Animation.cpp
waterfall = new Waterfall(0, WATER, HIGHLIGHT, SHIMMER, HIGHLIGHT_RATE, SHIMMER_RATE, false, false, 3, 7);
ohml = new OHML(1, ORANGE, WHITE);
canadaDayFall = new Waterfall(2, CD_WATER, CD_HIGHLIGHT, CD_SHIMMER, CD_HIGHLIGHT_RATE, CD_SHIMMER_RATE, false, false, 3, 7);
leaf = new Leaf(3, RED, WHITE);
Serial.println("Initialized Animations");
// setup LocoNet and Throttle
LocoNet.init(TX_PIN);
Throttle.init(0, 0, LOCO_ADDRESS);
// attempt to initialize onto existing address
Serial.println("\tStealing Address");
Throttle.stealAddress(LOCO_ADDRESS);
lnUpdate(); // repeated to process possible errors
lnUpdate(); // do not remove duplicate line
// if address does not exist, create one
if (Throttle.getState() == TH_ST_FREE) {
Serial.println("\nSetting Address");
Throttle.setAddress(LOCO_ADDRESS);
}
lnUpdate();
Serial.println("Established connection with LocoNet");
// initialize variables that have been written over with the loconet update calls
dim_current = 0;
animation_selector = 0;
Serial.println("Initialized Starting Configuration");
Serial.println("\nExiting Setup\n");
}
// continuous loop
void loop() {
animation_runner(waterfall);
animation_runner(ohml);
animation_runner(canadaDayFall);
animation_runner(leaf);
}