-
Notifications
You must be signed in to change notification settings - Fork 0
/
prohex.ino
347 lines (300 loc) · 8.55 KB
/
prohex.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
/*
ProHex firmware for HexBright FLEX
v0.1 Jan 09, 2012
Website: http://justinmrkva.github.com/prohex/
Please consider donating!
This firmware is provided for free and no warranty is supplied or implied.
You may modify for your own use without restrictions.
You may distribute copies, original or modified, ONLY if you include this original header.
If you distribute any modified versions, please list your modifications below.
Above all, have fun hacking your HexBright!
ACKNOWLEDGEMENTS
Some of this code, especially temperature, charging, and setup blocks, are sourced from the
HexBright example libraries at http://github.com/hexbright - without them, this project may
not have been possible. Special thanks to http://www.hexbright.com for producing the light
in the first place!
*/
// USER PREFERENCES
#define USR_LOW_POWER 0.2
#define USR_MED_POWER 0.6
#define USR_HIGH_POWER 1.0
#define USR_BLINK_POWER 0.6
#define USR_BLINK_TIME 10
#define USR_BLINK_WAVELENGTH 750
// Advanced Settings
#define OVERTEMP 340
#define CRIT_TEMP 350
#define ABSOLUTE_MIN 8
#define FADE_INCREMENT 0.001
#define BTN_CLICK_SENSE 20
#define BTN_HOLD_SENSE 500
#define BTN_LONG_HOLD_SENSE 3000
// Pins
#define DPIN_RLED_SW 2
#define DPIN_GLED 5
#define DPIN_PWR 8
#define DPIN_DRV_MODE 9
#define DPIN_DRV_EN 10
#define APIN_TEMP 0
#define APIN_CHARGE 3
// Modes
#define MODE_OFF 0
#define MODE_LOW 1
#define MODE_MED 2
#define MODE_HIGH 3
#define MODE_BLINKING 4
// Includes
#include <math.h>
#include <Wire.h>
// State Information
byte mode = MODE_OFF;
float brightness = 0.0;
// Button Tracking (internal for button functions)
unsigned long btnDnTime = 0;
unsigned long btnUpTime = 0;
boolean btnDn = false;
boolean btnUp = false;
boolean btnAck = false;
byte btnDownMode = MODE_OFF;
// Button status
boolean btnIsBeingHeld = false;
boolean btnIsBeingLongHeld = false;
boolean btnWasClicked = false;
boolean btnWasHeld = false;
boolean btnWasLongHeld = false;
void setup()
{
// We just powered on! That means either we got plugged
// into USB, or the user is pressing the power button.
pinMode(DPIN_PWR, INPUT);
digitalWrite(DPIN_PWR, LOW);
// Initialize GPIO
pinMode(DPIN_RLED_SW, INPUT);
pinMode(DPIN_GLED, OUTPUT);
pinMode(DPIN_DRV_MODE, OUTPUT);
pinMode(DPIN_DRV_EN, OUTPUT);
digitalWrite(DPIN_DRV_MODE, LOW);
digitalWrite(DPIN_DRV_EN, LOW);
// Initialize serial busses
//Serial.begin(9600); // for debugging only
Wire.begin();
// redo this just in case
resetBtn();
mode = MODE_OFF;
}
void loop()
{
// IMPORTANT
// Time each operation; during fades, etc. cancel or minimize unnecessary operations
// standard variables used
unsigned long time = millis(); // rollover time is long, don't have to concern ourselves
// Check the state of the charge controller
int chargeState = analogRead(APIN_CHARGE);
if (chargeState < 128) {
// Low - charging
digitalWrite(DPIN_GLED, (time&0x0100)?LOW:HIGH);
} else if (chargeState > 768) {
// High - charged
digitalWrite(DPIN_GLED, HIGH);
} else {
// Hi-Z - shutdown
digitalWrite(DPIN_GLED, LOW);
}
// check temp sensor
// Test efficiency of both checking every time and of doing every second
// Temporary values, use true mode changing
int temperature = analogRead(APIN_TEMP);
if (temperature > CRIT_TEMP) {
pinMode(DPIN_PWR, OUTPUT);
digitalWrite(DPIN_PWR, LOW);
digitalWrite(DPIN_DRV_MODE, LOW);
digitalWrite(DPIN_DRV_EN, LOW);
} else if (temperature > OVERTEMP && (mode != MODE_LOW && mode != MODE_OFF)) {
// disable MOSFET
digitalWrite(DPIN_DRV_MODE, LOW);
// flash to warn, then switch to low
for (int i = 0; i < 6; i++)
{
digitalWrite(DPIN_DRV_EN, LOW);
delay(100);
digitalWrite(DPIN_DRV_EN, HIGH);
delay(100);
}
digitalWrite(DPIN_DRV_EN, LOW);
brightness = 0;
mode = MODE_LOW;
}
// periodically pull down the button's pin
// in certain hardware revisions it can float
pinMode(DPIN_RLED_SW, OUTPUT);
pinMode(DPIN_RLED_SW, INPUT);
// check button state
senseBtn();
// determine transition for button/mode
byte newMode = mode;
switch (mode) {
case MODE_OFF:
if (btnWasClicked) {
latchPower();
newMode = MODE_LOW;
ackBtn();
} else if (btnIsBeingHeld) {
latchPower();
newMode = MODE_BLINKING;
} else if (btnWasHeld) {
latchPower();
newMode = MODE_BLINKING;
ackBtn();
}
break;
case MODE_LOW:
if (btnWasClicked) {
newMode = MODE_MED;
ackBtn();
} else if (btnIsBeingHeld || btnWasHeld) {
newMode = MODE_OFF;
ackBtn();
}
break;
case MODE_MED:
if (btnWasClicked) {
newMode = MODE_HIGH;
ackBtn();
} else if (btnIsBeingHeld || btnWasHeld) {
newMode = MODE_OFF;
ackBtn();
}
break;
case MODE_HIGH:
if (btnWasClicked) {
newMode = MODE_LOW;
ackBtn();
} else if (btnIsBeingHeld || btnWasHeld) {
newMode = MODE_OFF;
ackBtn();
}
break;
case MODE_BLINKING:
if (btnWasClicked) {
newMode = MODE_OFF;
ackBtn();
} else if (btnIsBeingLongHeld || btnWasLongHeld) {
newMode = MODE_OFF;
ackBtn();
} else if (btnDownMode == MODE_BLINKING && btnWasHeld) {
newMode = MODE_OFF;
ackBtn();
}
break;
}
// Do the mode
// Remember to ack button once no more input is expected
switch (newMode) {
case MODE_OFF:
fadeTo(0);
if (brightness == 0) {
pinMode(DPIN_PWR, OUTPUT);
digitalWrite(DPIN_PWR, LOW);
}
break;
case MODE_LOW:
fadeTo(USR_LOW_POWER);
break;
case MODE_MED:
fadeTo(USR_MED_POWER);
break;
case MODE_HIGH:
fadeTo(USR_HIGH_POWER);
break;
case MODE_BLINKING:
blinkTo(((time % USR_BLINK_WAVELENGTH) < USR_BLINK_TIME) ? USR_BLINK_POWER : 0);
break;
}
mode = newMode;
}
// LIBRARY
void latchPower() {
pinMode(DPIN_PWR, OUTPUT);
digitalWrite(DPIN_PWR, HIGH);
}
void fadeTo(float val)
{
// map values here
float target = brightness;
if (target > val) {
target -= FADE_INCREMENT;
if (target < val) target = val;
} else if (target < val) {
target += FADE_INCREMENT;
if (target > val) target = val;
}
if (target != brightness) blinkTo(target);
}
void blinkTo(float val)
{
if (val == 0) {
digitalWrite(DPIN_DRV_MODE,LOW);
analogWrite(DPIN_DRV_EN,0);
} else {
float strength = pow(val, 2);
unsigned char setValue = (unsigned char)(strength * (255 - ABSOLUTE_MIN)) + ABSOLUTE_MIN;
analogWrite(DPIN_DRV_EN, setValue);
digitalWrite(DPIN_DRV_MODE, HIGH);
}
brightness = val;
}
void ackBtn() {
btnAck = true;
resetBtn();
}
void resetBtn()
{
btnDn = false;
btnUp = false;
btnIsBeingHeld = false;
btnIsBeingLongHeld = false;
btnWasClicked = false;
btnWasHeld = false;
btnWasLongHeld = false;
}
// TIME this function's execution time, try to optimize?
boolean senseBtn()
{
pinMode(DPIN_RLED_SW,INPUT);
boolean buttonIsDepressed = digitalRead(DPIN_RLED_SW);
if (btnAck && buttonIsDepressed) return false;
if (btnAck && !buttonIsDepressed) btnAck = false;
if (buttonIsDepressed && btnUp) resetBtn();
if (!btnDn && buttonIsDepressed) { // mark the down point
btnDn = true;
btnDnTime = millis();
btnDownMode = mode;
} else if (btnDn && !btnUp && !buttonIsDepressed) { // mark the up point
btnUp = true;
btnUpTime = millis();
}
btnIsBeingHeld = false;
btnIsBeingLongHeld = false;
btnWasClicked = false;
btnWasHeld = false;
btnWasLongHeld = false;
if (btnDn && !btnUp) {
unsigned long timeDiff = millis() - btnDnTime;
if (timeDiff >= BTN_HOLD_SENSE && timeDiff < BTN_LONG_HOLD_SENSE) {
btnIsBeingHeld = true;
} else if (timeDiff >= BTN_LONG_HOLD_SENSE) {
btnIsBeingLongHeld = true;
}
}
if (btnDn && btnUp) {
unsigned long timeDiff = btnUpTime - btnDnTime;
if (timeDiff >= BTN_CLICK_SENSE && timeDiff < BTN_HOLD_SENSE) {
btnWasClicked = true;
} else if (timeDiff >= BTN_HOLD_SENSE && timeDiff < BTN_LONG_HOLD_SENSE) {
btnWasHeld = true;
} else if (timeDiff >= BTN_LONG_HOLD_SENSE) {
btnWasLongHeld = true;
}
}
return buttonIsDepressed;
}