-
Notifications
You must be signed in to change notification settings - Fork 0
/
ebcb-general-controller.ino
357 lines (294 loc) · 9.72 KB
/
ebcb-general-controller.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
// ebcb-general-controller.ino Andrew Ward 6/23/2017
//
// A General purpose controller for stuff @ EBCB... Starting with Stained Glass Windows
//
// Two active LED channels are controlled.... Interfaced to Blynk apps, and IFTTT control.
// updates:
// 2018-03-26 Added 3rd LED channel (for side light control)
#include <blynk.h> // automatically added by the Particle IDE
#include <MDNS.h> // automatically added by the Particle IDE.
#include <Debounce.h> // automatically added by the Particle IDE.
#include <Adafruit_PCA9685.h> // automatically added by the Particle IDE.
SYSTEM_THREAD(ENABLED); // new beta feature as of 4.0.7 firmware: allows the code to run while the firmware is trying to connect to wifi
char auth[] = "c4f66cc955a141cf951ca9bb8225ddce";
char my_device_name[] = "EBCBcontrol";
//Adafruit_PCA9685 pwm = Adafruit_PCA9685(0x40, true); // Use the default address, but also turn on debugging
Adafruit_PCA9685 pwm = Adafruit_PCA9685(0x40);
Debounce debouncer = Debounce();
MDNS mdns;
unsigned long millis_next_off = 0l;
unsigned long next_status_print = 0l;
bool current_ps_state;
// -- Photon Hardware Assigmments
int boardLed = D7; // This is the LED that is already on your device.
int PIR0 = A0;
// channel assignemnts in 9685 controller
#define CH_8_PS_CTRL 8
#define CH_0_SG_BKGND 0
#define CH_1_SG_BIBLE 1
#define CH_2_SG_SIDES 2
#define NUM_CHANNELS 16
#define PWM_FREQ_DEFAULT 400
#define PWM_VALUE_DEFAULT 128
#define SG_BKGND_CHANNEL 0
#define SG_BKGND_PWM_DEFAULT 4095
#define SG_BIBLE_CHANNEL 1
#define SG_BIBLE_PWM_DEFAULT 1000
#define SG_SIDES_CHANNEL 2
#define SG_SIDES_PWM_DEFAULT 4095
#define SG_POWER_SUPPLY_CHANNEL 7
#define OFF_AFTER_MINS 10
#define STATUS_INTERVAL_MINS 2
void setup() {
unsigned long end_of_ser_conn_wait = millis() + 6000; // 3 seconds
int chan_ctr=0, pwm_value=PWM_VALUE_DEFAULT;
IPAddress ip;
pinMode(boardLed,OUTPUT); // Our on-board LED is output as well
debouncer.attach(PIR0, INPUT_PULLUP);
debouncer.interval(3000); // interval in ms
// Serial.begin();
USBSerial1.begin();
setupCloudFunctions();
digitalWrite(boardLed,HIGH);
while(millis() < end_of_ser_conn_wait ){
if (!(millis() % 500)) digitalWrite(boardLed,!digitalRead(boardLed));
// let it run out: if (USBSerial1.isConnected()) break;
Particle.process();
if (System.buttonPushed()) {
}
}
digitalWrite(boardLed, LOW);
USBSerial1.println("HELLO USB Andrew!");
Blynk.begin(auth);
pwm.begin();
pwm.setPWMFreq(PWM_FREQ_DEFAULT); // This is the maximum PWM frequency
for(;chan_ctr < NUM_CHANNELS; chan_ctr++) {
pwm.setVal(chan_ctr, pwm_value);
}
// Set to On value by default. TBD - use nvram to remember last state.
Ctrl_PS_On();
USBSerial1.println("All DONE with Setup");
delay(2000);
ip = WiFi.resolve("blynk-cloud.com");
Particle.publish("BlynkCloud ", String(ip), PRIVATE);
if (mdns.setHostname(my_device_name)) {
mdns.begin();
}
}
void loop() {
int val;
debouncer.update();
Blynk.run();
mdns.processQueries();
if (millis_next_off) { // only do this if the timer is being used (non-zero)
if (millis() > millis_next_off) {
millis_next_off = 0;
Ctrl_PS_Off();
debug("Auto OFF\n");
}
}
//debug("Blot");
if (millis() > next_status_print) {
// mdnsAdvertiser(1, my_device_name, strlen(my_device_name));
if (next_status_print == 0) {
debug("INITIAL STARTUP: Status: PS is %d\n", current_ps_state);
} else {
// debug("Periodic Status: PS is %d, debouncer rd %d\n",
// current_ps_state, debouncer.read());
//debug("Periodic Status: PS is %d\n",
// current_ps_state);
}
next_status_print = millis_mins_from_now(STATUS_INTERVAL_MINS);
// delay(2);
// debug("Status: automatic_timer value is %ld\n", millis_next_off);
// delay(2);
// debug("and next_status_print = %ld", next_status_print);
// delay(2);
// debug("and millis now %ld", millis());
// delay(1);
}
if (debouncer.fell()) {
//debug("Blot B");
// read the input pin
if ( millis_next_off) { // non-zero indicates timed-mode is active
millis_next_off = millis_mins_from_now(OFF_AFTER_MINS);
debug("MOTION! (ACTIVELY used!)");
} else {
debug("MOTION! (but inactive/disabled)");
}
}
// debug("end Loop");
}
// -------------------- Particle Cloud Functions (supports IFTTT) -----------------------
void setupCloudFunctions()
{
Particle.function("DevControl", DevControl);
}
// this function will be published to the cloud, allowing the user to control the fan remotely
int DevControl(String cmd)
{
if (cmd == "on") {
debug("cmd 'on': Set Power Supply 'ON'\n");
millis_next_off = 0;
if (!current_ps_state){
return(Ctrl_PS_On());
}
return 0;
} else if (cmd == "on_timed") {
debug("cmd 'on_timed': Set Power Supply On for %d mins\n", OFF_AFTER_MINS);
millis_next_off = millis_mins_from_now(OFF_AFTER_MINS);
if (!current_ps_state){
return(Ctrl_PS_On());
}
} else if (cmd == "off") {
debug("cmd 'off': Set Power Supply 'OFF'\n");
millis_next_off = 0;
return(Ctrl_PS_Off());
} else if (cmd == "set_default") {
debug("cmd 'set_default'\n");
return(Ctrl_Set_Defaults());
} else if (cmd == "1") {
debug("cmd '1': TBD\n");
}
return 1;
}
// -------------------- BLYNK Functions -----------------------
BLYNK_WRITE(V0) {
// int chan_ctr=0;
USBSerial1.print("(V0) Set PWM ");
USBSerial1.println(param.asInt());
debug("Blynk (V0) Set SG Bkgnd PWM to %d\n", param.asInt());
Ctrl_PWM_Bkgnd(param.asInt());
}
BLYNK_WRITE(V1) {
// int chan_ctr=0;
USBSerial1.print("(V1) Set PWM ");
USBSerial1.println(param.asInt());
debug("Blynk (V1) Set SG Bible PWM to %d\n", param.asInt());
Ctrl_PWM_Bible(param.asInt());
}
BLYNK_WRITE(V2) {
// int chan_ctr=0;
USBSerial1.print("(V2) Set PWM ");
USBSerial1.println(param.asInt());
debug("Blynk (V1) Set SG Sides PWM to %d\n", param.asInt());
Ctrl_PWM_Sides(param.asInt());
}
BLYNK_WRITE(V17) {
USBSerial1.print("(V17) Set PWM Frequency to ");
USBSerial1.println(param.asInt());
debug("Blynk (V17) Set FREQUENCY to %d\n", param.asInt());
Ctrl_Set_Freq(param.asInt());
}
BLYNK_WRITE(V18) {
if (param.asInt() == 0) { // only do on button RELEASE
debug("Blynk (V18) Restore Defaults\n");
Ctrl_Set_Defaults();
}
}
BLYNK_WRITE(V19) {
if (param.asInt() == 0) { // only do on button RELEASE
debug("Blynk (V18) Timed On: Set Power Supply On for %d mins\n", OFF_AFTER_MINS);
millis_next_off = millis_mins_from_now(OFF_AFTER_MINS);
if (!current_ps_state){
Ctrl_PS_On();
}
}
}
BLYNK_WRITE(V8) {
if (param.asInt() > 0) {
USBSerial1.print("(V8) Set Chan 8 (Relay) ON ");
debug("Blynk (V8) Set PS ON\n");
millis_next_off = 0;
Ctrl_PS_On();
} else {
USBSerial1.print("(V8) Set Chan 8 (Relay) OFF ");
debug("Blynk (V8) Set PS OFF\n");
Ctrl_PS_Off();
}
}
BLYNK_READ(V20) // Get and store push button status
{
Blynk.virtualWrite(V20, millis_next_off);
}
// This is called for all virtual pins, that don't have BLYNK_WRITE handler
BLYNK_WRITE_DEFAULT() {
USBSerial1.print("input V");
USBSerial1.print(request.pin);
USBSerial1.println(":");
// Print all parameter values
for (auto i = param.begin(); i < param.end(); ++i) {
USBSerial1.print("* ");
USBSerial1.println(i.asString());
}
}
// This is called for all virtual pins, that don't have BLYNK_READ handler
BLYNK_READ_DEFAULT() {
// Generate random response
int val = random(0, 100);
USBSerial1.print("output V");
USBSerial1.print(request.pin);
USBSerial1.print(": ");
USBSerial1.println(val);
Blynk.virtualWrite(request.pin, val);
}
// -------------------- HW Action Functions -----------------------
int Ctrl_Set_Freq(uint16_t val) {
pwm.setPWMFreq(val);
return 0;
}
int Ctrl_PS_Off(void) {
pwm.setVal(CH_8_PS_CTRL, 0);
current_ps_state = 0;
return 0;
}
int Ctrl_PS_On(void) {
pwm.setVal(CH_8_PS_CTRL, 4095);
current_ps_state = 1;
return 0;
}
int Ctrl_PWM_Bkgnd(uint16_t val) {
pwm.setVal(CH_0_SG_BKGND, val);
return 0;
}
int Ctrl_PWM_Bible(uint16_t val) {
pwm.setVal(CH_1_SG_BIBLE, val);
return 0;
}
int Ctrl_PWM_Sides(uint16_t val) {
pwm.setVal(CH_2_SG_SIDES, val);
return 0;
}
int Ctrl_Set_Defaults(void) {
pwm.setPWMFreq(PWM_FREQ_DEFAULT);
pwm.setVal(CH_0_SG_BKGND, SG_BKGND_PWM_DEFAULT);
pwm.setVal(CH_1_SG_BIBLE, SG_BIBLE_PWM_DEFAULT);
pwm.setVal(CH_2_SG_SIDES, SG_SIDES_PWM_DEFAULT);
millis_next_off = millis_mins_from_now(OFF_AFTER_MINS);
return 0;
}
uint16_t Ctrl_get_PWM_Bkgnd(void) {
return pwm.readPWMOn(CH_0_SG_BKGND);
}
uint16_t Ctrl_get_PWM_Bible(void) {
return pwm.readPWMOn(CH_1_SG_BIBLE);
}
uint16_t Ctrl_get_PWM_Sides(void) {
return pwm.readPWMOn(CH_2_SG_SIDES);
}
// -------------------- Utility Functions -----------------------
// Log message to cloud, message is a printf-formatted string
void debug(String message, int value ) {
char msg [50];
sprintf(msg, message.c_str(), value);
Particle.publish("STAT", msg);
}
// Log message to cloud, message is a printf-formatted string
void debug(String message) {
char msg [50];
sprintf(msg, message.c_str());
Particle.publish("STAT", msg);
}
unsigned long millis_mins_from_now(uint32_t mins) {
return (millis() + (mins * 60 * 100));
}