-
Notifications
You must be signed in to change notification settings - Fork 1
/
Hannah_Rev2_Device.nut
2008 lines (1736 loc) · 66.8 KB
/
Hannah_Rev2_Device.nut
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
// Electric Imp - Hannah Dev Board
//TODO: Remove large blocks of commented and unused code
//TODO: Use Callibration capabilities of devices to allow setting correct temp, acceleration, color sensor, etc.
//TODO: Implement Color Sensor
//TODO: Figure out why continuous conversion doesn't work on the Temp sensor
//TODO: Do something with the accelerometer and light sensor to make them useful
//TODO: Test the Servo code is somewhat working - make it a more useful and friendly library
//TODO: Is there anything else available with the hardware that would be useful?
//TODO: Implement a useful agent with all the latest bells and whistles I can think of - (serving up a local control bootstrap page is one good thought, create Agent end points for all of the available actions, create a RESTFUL JSON API)
//TODO: Create an onchange callback method for the potentiometer (that is able to ignore noise/drift) for agents and to be used as an irq
//TODO: Ensure all class variables start with underscores
//TODO: Ensure all Camelcaseing of functions, member vars, etc. are consistent
//TODO: Ensure putting semicolons is consistent
//TODO: Implement all of the functionality in the servo class (minpulse, maxpulse, etc.)
//TODO: Implement all the Release-23 goodies here
function getConnectionReason(reason){
if(reason == SERVER_CONNECTED)
return "The server is connected";
else if(reason == NO_WIFI)
return "No Wifi";
else if(reason == NO_IP_ADDRESS)
return "Failed to get IP Address";
else if(reason == NO_SERVER)
return "Failed to connect to EI server";
else if(reason == NOT_RESOLVED)
return "Failed to resolve EI server";
return ""
}
server.setsendtimeoutpolicy(RETURN_ON_ERROR, WAIT_TIL_SENT, 10.0);
//server.disconnect();
//imp.wakeup(10.0, server.connect(function(status){
// server.log(getConnectionReason(status))
//}, 10.0));
onBoot <- true;
agent.send("Device_Variables", {
onBoot = true,
impeeID=hardware.getimpeeid(),
MACaddr=imp.getmacaddress(),
});
/*******************************************************************************
HARDWARE ASSIGNMENTS/CONSTANTS
*******************************************************************************/
//Imp Hardware Constants
IOEXPANDER_INTERRUPT_PIN <- hardware.pin1;
POTENTIOMETER_PIN <- hardware.pin2;
SERVO1_PIN <- hardware.pin5;
SERVO2_PIN <- hardware.pin7;
I2C_PORT <- hardware.i2c89;
//I2C HW Constants
IOEXPANDER_I2C_ADDRESS <- 0x3E;
ACCELEROMETER_I2C_ADDRESS <- 0x1C;
RGB_LIGHT_SENSOR_I2C_ADDRESS <- 0x74;
TEMP_SENSOR_I2C_ADDRESS <- 0x4C;
//IO Expander peripherals
IO_BUTTON1_PIN <- 0;
IO_BUTTON2_PIN <- 1;
IO_HALL_SWITCH_PIN <- 2;
IO_ACCELEROMETER_INTERRUPT_PIN <- 3;
IO_TEMP_SENSOR_INTERRUPT_PIN <- 4;
IO_LED_G_PIN <- 5;
IO_LED_B_PIN <- 6;
IO_LED_R_PIN <- 7;
IO_POTENTIOMETER_ENABLE_PIN <- 8;
IO_RGB_LIGHT_SENSOR_SLEEP_PIN <- 9;
IO_SERVO_POWEREN_PIN <- 10;
/**------------------ END HARDWARE ASSIGNMENTS/CONSTANTS ------------------- **/
/*******************************************************************************
FIRMWARE CONSTANTS
*******************************************************************************/
/**------------------------ END FIRMWARE CONSTANTS ------------------------- **/
/*******************************************************************************
HELPER FUNCTIONS
*******************************************************************************/
function arrayString(arr){
local str = ""
for(local i = 0; i < arr.len(); i++){
if(arr[i] == null) break;
str = str + format("%.2X ", arr[i]);
}
return str;
}
function byteString(data, bytes){
local str = ""
for(local z=(1<<(bytes*8-1)); z>0; z = z>>1){
if((data & z) == z) str = str + "1";
else str = str + "0"
}
return str;
}
function byte(byteStr){
local retVal = 0
for(local i=0; i<byteStr.len(); i++){
retVal = retVal || byteStr[i].tointeger() << i
}
return retVal;
}
function bitRead(value, bit) {
return (((value) >> (bit)) & 0x01);
}
function Max(...) {
local maximum = vargv[0]
for(local i = 1; i< vargv.len(); i++) {
if (vargv[i] > maximum) {
maximum = vargv[i];
}
}
return maximum;
}
function Min(...) {
local minimum = vargv[0]
for(local i = 1; i< vargv.len(); i++) {
if (vargv[i] < minimum) {
minimum = vargv[i];
}
}
return minimum;
}
function round(val, decimal){
if (decimal)
return math.floor((val * math.pow(10,decimal)) + 0.5) / math.pow(10,decimal)
else
return math.floor(val+0.5)
}
function hexToInteger(hex) {
local result = 0;
local shift = hex.len() * 4;
// For each digit..
for(local d=0; d<hex.len(); d++) {
local digit;
// Convert from ASCII Hex to integer
if(hex[d] >= 0x61) {
digit = hex[d] - 0x57;
}
else if(hex[d] >= 0x41) {
digit = hex[d] - 0x37;
}
else {
digit = hex[d] - 0x30;
}
// Accumulate digit
shift -= 4;
result += digit << shift;
}
return result;
}
/* Converts an HSV color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number v The value
* @return Array The RGB representation
*/
//Adapted from http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c which has several other examples
function hsvToRgb(h, s, v){
local r, g, b;
local i = math.floor(h * 6);
local f = h * 6 - i;
local p = v * (1 - s);
local q = v * (1 - f * s);
local t = v * (1 - (1 - f) * s);
switch(i % 6){
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
//math.floor(x+.5) is the same as rounding (which squirrel doesn't have)
//math.abs() gives us an integer instead of a float which math.floor oddly returns
return [math.abs(math.floor(r * 255 + 0.5)), math.abs(math.floor(g * 255 + 0.5)), math.abs(math.floor(b * 255 + 0.5))];
}
/* Converts an RGB color value to HSV. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSV_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSV representation
*/
function rgbToHsv(r, g, b){
r = r/255.0
g = g/255.0
b = b/255.0;
local max = Max(r, g, b)
local min = Min(r, g, b);
local h;
local s;
local v = max.tofloat();
local d = (max - min);
s = max == 0.0 ? 0.0 : d / max;
if(max == min){
h = 0.0; // achromatic
} else {
if(max == r){
h = (g - b) / d + (g < b ? 6 : 0);
} else if(max == g){
h = (b - r) / d + 2;
} else if(max == b){
h = (r - g) / d + 4;
} else{
server.log("Error in rgbToHsl")
}
h = h/6.0
}
return [h, s, v];
}
/** --------------------------------- END HELPER FUNCTIONS --------------------------------------- **/
/******************************************************************************************************
DATE TIME FUNCTIONS
******************************************************************************************************/
//month <- ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
//weekday <- ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
/*function TimeStamp(arg = null){ //Time Format for SEG = "2010-01-10T23:22:12"
local currentTime = null;
local timeStr = null;
if(arg == null)
currentTime = date(time(), 'u');
else
currentTime = date(arg, 'u');
timeStr = format("%.4d-%.2d-%.2dT%.2d:%.2d:%.2d", currentTime.year, currentTime.month+1, currentTime.day, currentTime.hour, currentTime.min, currentTime.sec);
//TODO: Figure out how to incorporate milliseconds and microseconds into this in a consistent way? (May be difficult since they reset on boot, but since the boot takes more than a second does it really matter?)
return timeStr
}*/
function calcElapsedtime(lastTimeStamp, lastTimeStampMS=null, lastTimeStampUS=null, newTimeStamp=null, newTimeStampMS=null, newTimeStampUS=null){
if(newTimeStampUS == null) newTimeStampUS=hardware.micros();
if(newTimeStampMS == null) newTimeStampMS=hardware.millis();
if(newTimeStamp == null) newTimeStamp=time();
if(lastTimeStamp == null) lastTimeStamp = newTimeStamp;
if(lastTimeStampMS == 0.0 && lastTimeStampUS == 0.0){ //We don't have any microsecond or millisecond information
lastTimeStampMS = newTimeStampMS;
lastTimeStampUS = newTimeStampUS;
}
if(lastTimeStampMS == null) lastTimeStampMS = newTimeStampMS;
if(lastTimeStampUS == null) lastTimeStampUS = newTimeStampUS;
local seconds = newTimeStamp - lastTimeStamp;
if(seconds < 0){
server.log(format("ERROR: Time ran backwards - newTimestamp=%s, oldTimeStamp=%s", TimeStamp(newTimeStamp), TimeStamp(m_lastTimeStamp)));
}
if(seconds > 0 && seconds < 2100 && newTimeStampUS != lastTimeStampUS) {//microsecond counter rolls over after 2^31 microseconds = 2147.483648 seconds
seconds = (newTimeStampUS - lastTimeStampUS)/1000000.0;
} else if(seconds < 0 && seconds > -2100 && newTimeStampUS != lastTimeStampUS) {//microsecond counter rolls over after 2^31 microseconds = 2147.483648 seconds
seconds = (lastTimeStampUS - newTimeStampUS)/1000000.0;
} else if(seconds > 0 && seconds < 2147400 && newTimeStampMS != lastTimeStampMS) { //milliseconds roll over after 2147483.648 seconds or 24.855 Days
seconds = (newTimeStampMS - lastTimeStampMS)/1000.0;
} else if(seconds < 0 && seconds > -2147400 && newTimeStampMS != lastTimeStampMS) { //milliseconds roll over after 2147483.648 seconds or 24.855 Days
seconds = (lastTimeStampMS - newTimeStampMS)/1000.0;
} else if (seconds < 0){ //Have to rely on seconds only
seconds = -seconds
} //else{ //Have to rely on seconds only
//seconds = seconds
//}
return seconds;
}
function calcElapsedTimeString(lastTimeStamp, lastTimeStampMS=null, lastTimeStampUS=null, newTimeStamp=null, newTimeStampMS=null, newTimeStampUS=null){
local secs = calcElapsedtime(lastTimeStamp, lastTimeStampMS, lastTimeStampUS, newTimeStamp, newTimeStampMS, newTimeStampUS);
return (secs%3600) + ":" + ((secs/3600)%60) + ":" + ((secs/3600)/60.0)
}
/** --------------------------------- END DATE TIME FUNCTIONS --------------------------------------- **/
/******************************************************************************************************
PERMANENT AND NONVOLATILE STORAGE
******************************************************************************************************/
//TODO: need to ensure that all the server.permanent and nonvolatile code is properly working
//server.setpermanentvalues() sets the key value pairs that are saved by the server and set whenever the imp is rebooted. This allows values to be saved and recovered across power cycles.
//TODO: In Wi-Fi less power-up will these values be available???
serverPermanentUpdateNeeded <- false; //This flag is used with imp.wakeup to ensure we only have one one serverpermanent update being sent to the server at a time
function updateServerPermanentTable(){ //We use the nv table as local storage and update the permanent table when enough memory is available
if(imp.getmemoryfree() > 5000){
serverPermanentUpdateNeeded = false;
server.setpermanentvalues(server.permanent);
} else {
imp.wakeup(0.25, updateServerPermanentTable);
}
}
function serverPermanentUpdate(){
if(serverPermanentUpdateNeeded == false){
serverPermanentUpdateNeeded = true
imp.wakeup(0.25, updateServerPermanentTable);
}
}
/** ------------------------- END PERMANENT AND NONVOLATILE STORAGE --------------------------- **/
/*******************************************************************************
AGENT HANDLER CODE
*******************************************************************************/
function getRunningStatisticsTable(){
return {
FreeMemory=imp.getmemoryfree(),
SignalStrength=imp.rssi(),
impeeID=hardware.getimpeeid(),
MACaddr=imp.getmacaddress(),
impOS=imp.getsoftwareversion(),
impEnvironment=imp.environment(),
wakeReason=hardware.wakereason(),
Version="Hannah V1.0.0",
BSSID=imp.getbssid(),
bootNumber = server.permanent.BootNumber,
onBoot = onBoot,
}
}
function getUIDataTable(){
// Start a single shot conversion
TemperatureSensor.start(false);
// Wait for conversion to complete - takes approximately 38ms according to the data sheet
while(!TemperatureSensor.isReady()) {
imp.sleep(0.04);
}
local temp = TemperatureSensor.getFTemperature();
local voltage = hardware.voltage();
local rotaryPot = Potentiometer.readSmooth();
local accelX = Accelerometer.getX();
local accelY = Accelerometer.getY();
local accelZ = Accelerometer.getZ();
local rgbLEDColor = rgbLED.getLEDRGBColor();
local servo1Angle = servo1.readPercent();
local servo2Angle = servo2.readPercent();
local button1State = pushButton1.read();
local button2State = pushButton2.read();
local hallSwitchState = hallSwitch.read();
server.show(format("%3.2f Degrees F, %1.10fV, %f%%, %d Bytes", temp, voltage, rotaryPot, imp.getmemoryfree()));
//server.log(format("%3.2f Degrees F, %1.10fV, %f%%, %d Bytes", temp, voltage, rotaryPot, imp.getmemoryfree()));
//server.log(accelX + " " + accelY + " " + accelZ)
return {
"Temperature": temp,
"VoltageSense": voltage,
"Potentiometer": rotaryPot,
"Accelerometer-X": accelX,
"Accelerometer-Y": accelY,
"Accelerometer-Z": accelZ,
"LED-R":rgbLEDColor[0],
"LED-G":rgbLEDColor[1],
"LED-B":rgbLEDColor[2],
"Servo-1": servo1Angle,
"Servo-2": servo2Angle,
[pushButton1._name] = button1State,
[pushButton2._name] = button2State,
[hallSwitch._name] = hallSwitchState
"DeviceFreeMemory": imp.getmemoryfree(),
"WIFIRSSI": imp.rssi()
}
}
function initAgentCallbacks(){
agent.on("GET_RUNNING_STATISTICS",function(value){
agent.send("GET_RUNNING_STATISTICS", [getRunningStatisticsTable(), value]);
});
agent.on("GetUIData", function(value){
agent.send("GET_UI_DATA", [getUIDataTable(), value]);
})
}
/** ------------------------- END AGENT HANDLER CODE --------------------------- **/
//All singletons are created as tables to optimize memory usage as mentioned at http://devwiki.electricimp.com/doku.php?id=writingefficientsquirrel
/*******************************************************************************
// IO Expander Class for SX1509
*******************************************************************************/
IOExpander <- {}
IOExpander.I2CPort <- null;
IOExpander.I2CAddress <- null;
IOExpander.IRQPin <- null;
IOExpander.IRQ_Callbacks <- array(16); //Array of functions to be called whenever an IO Expander interrupt is triggered
//TODO: Implement binding of native methods to improve performance
IOExpander.Init <- function(port, address, irqPin) {
I2CPort = port;
I2CAddress = address << 1;
IRQPin = irqPin
I2CPort.configure(CLOCK_SPEED_400_KHZ);
IRQPin.configure(DIGITAL_IN, getIRQSources.bindenv(this));
}
// Read a byte
IOExpander.read <- function(register) {
// Read and return data if successful
local data = I2CPort.read(I2CAddress, format("%c", register), 1);
if (data != null) return data[0];
// Error, return -1
server.log("I2C Read Failed");
return -1;
}
// Write a byte
IOExpander.write <- function(register, data) {
I2CPort.write(I2CAddress, format("%c%c", register, data));
}
// Write a bit to a register
IOExpander.writeBit <- function(register, bitn, level) {
local value = read(register);
value = (level == 0)?(value & ~(1<<bitn)):(value | (1<<bitn));
write(register, value);
}
// Write a masked bit pattern
IOExpander.writeMasked <- function(register, data, mask) {
local value = read (register);
value = (value & ~mask) | (data & mask);
write (register, value);
}
// Set a GPIO direction
IOExpander.setDir <- function(gpio, output){
writeBit (gpio>=8?0x0e:0x0f, gpio&7, output?0:1);
}
// Set a GPIO level
IOExpander.setPin <- function(gpio, level){
writeBit (gpio>=8?0x10:0x11, gpio&7, level?1:0);
}
// Enable/disable a GPIO internal pull-up resistor
IOExpander.setPullUp <- function(gpio, enable) {
writeBit (gpio>=8?0x06:0x07, gpio&7, enable);
}
// Set GPIO interrupt mask
IOExpander.setIRQMask <- function(gpio, enable) {
writeBit (gpio>=8?0x12:0x13, gpio&7, enable);
}
// Set GPIO interrupt edges
IOExpander.setIRQEdges <- function(gpio, rising, falling) {
local addr = 0x17 - (gpio>>2);
local mask = 0x03 << ((gpio&3)<<1);
local data = (2*falling + rising) << ((gpio&3)<<1);
writeMasked (addr, data, mask);
}
// Clear an interrupt
IOExpander.clearIRQ <- function(gpio) {
writeBit (gpio>=8?0x18:0x19, gpio&7, 1);
}
IOExpander.setIRQCallBack <- function(pin, func){
IRQ_Callbacks[pin] = func;
}
IOExpander.clearIRQCallBack <- function(pin){
IRQ_Callbacks[pin] = null;
}
IOExpander.getIRQSources <- function(){
if(IRQPin.read() == 0){
return; //No need to run the code - the IRQ line has simply gone low
}
//0x18=RegInterruptSourceB (Pins 15->8), 1 is an interrupt and we write a 1 to clear the interrupt
//0x19=RegInterruptSourceA (Pins 7->0), 1 is an interrupt and we write a 1 to clear the interrupt
local sourceB = read(0x18);
local sourceA = read(0x19);
//server.log(format("Interrupt Source B = %s", byteString(sourceB, 1)));
//server.log(format("Interrupt Source A = %s", byteString(sourceA, 1)));
local irqSources = array(16);
local pin = 0;
for(local z=1; z < 256; z = z<<1){
irqSources[pin] = ((sourceA & z) == z);
irqSources[pin+8] = ((sourceB & z) == z);
if(irqSources[pin]){
IRQ_Callbacks[pin]();
clearIRQ(pin);
}
if(irqSources[pin+8]){
IRQ_Callbacks[pin+8]();
clearIRQ(pin+8);
}
pin++;
}
return irqSources; //Array of the IO pins and who has active interrupts
}
// Get a GPIO input pin level
IOExpander.getPin <- function(gpio) {
//If gpio pin is greater than or equal to 8 then its staus is in the 0x10 register, else its in the 0x11 register. Then left shift to create a mask for the particular pin and return true or false based on its value
return (read(gpio>=8?0x10:0x11)&(1<<(gpio&7))) ? 1 : 0;
}
/** ------------------------- END IO EXPANDER CODE --------------------------- **/
/*******************************************************************************
Temperature Sensor Class for SA56004X
*******************************************************************************/
TemperatureSensor <- {}
TemperatureSensor.i2cPort <- null;
TemperatureSensor.i2cAddress <- null;
TemperatureSensor.irqPin <- null; //TODO: Need to fully implement the TemperatureSensor Interrupt/Callback system
TemperatureSensor.callBack <- null;
TemperatureSensor.conversionRate <- 0x04;
TemperatureSensor.Init <- function(port, address, irq, call = null) {
//server.log("Contructing TemperatureSensor")
i2cPort = port;
i2cAddress = address << 1;
irqPin = irq;
callBack = call;
// Configure device for single shot, no alarms
write(0x09, 0xD5);
// Set default conversion rate (1Hz)
setRate(conversionRate);
// Set event handler for irq
IOExpander.setIRQCallBack(irqPin, irqHandler.bindenv(this))
// Configure pin as input, irq on both edges
IOExpander.setDir(irqPin, 0);
IOExpander.setPullUp(irqPin, 1);
IOExpander.setIRQMask(irqPin, 0);
IOExpander.setIRQEdges(irqPin, 1, 1);
//server.log("TemperatureSensor Constructed")
}
TemperatureSensor.irqHandler <- function() {
server.log("Temperature Sensor irqHandler Called");
//TODO: Get what caused interrupt
if (callBack != null) callBack()
// Clear the interrupt
//IOExpander.clearIRQ(pin); //Taken care of by the IOExpander Class
}
// Read a byte
TemperatureSensor.read <- function(register) {
local data = i2cPort.read(i2cAddress, format("%c", register), 1);
if(data == null) {
server.log("I2C Read Failure");
return -1;
}
return data[0];
}
// Write a byte
TemperatureSensor.write <- function(register, data) {
i2cPort.write(i2cAddress, format("%c%c", register, data));
}
// Set continuous conversion rate, 0 = 0.06Hz, 4 = 1Hz, 9 = 32Hz
TemperatureSensor.setRate <- function(rate){
if(rate >= 0 && rate <= 9) {
write(0x0a, rate);
conversionRate = rate;
}
else {
write(0x0a, 0x04);
conversionRate = 0x04;
server.log("Invalid conversion rate, using default 1Hz");
}
}
// Stop continuous conversion
TemperatureSensor.stop <- function() {
write(0x09, 0xD5);
}
// Start conversion, continuous or single shot
TemperatureSensor.start <- function(continuous = false) {
if(continuous == true) {
write(0x09, 0x55);
}
else {
write(0x0f, 0x00);
}
}
// Check if conversion is completed
TemperatureSensor.isReady <- function() {
return (read(0x02) & 0x80)?false:true;
}
// Retrieve temperature (from local sensor) in deg C
TemperatureSensor.getTemperature <- function() {
// Get 11-bit signed temperature value in 0.125C steps
local temp = (read(0x00) << 3) | (read(0x22) >> 5);
if(temp & 0x400) {
// Negative two's complement value
return -((~temp & 0x7FF) + 1) / 8.0;
}
else {
// Positive value
return temp / 8.0;
}
}
TemperatureSensor.getFTemperature <- function(){
local Ctemp = this.getTemperature();
return Ctemp*1.8+32;
}
/** ------------------------- END TEMP SENSOR CODE --------------------------- **/
/*******************************************************************************
ACCELEROMETER CODE (LIS331DLTR)
*******************************************************************************/
Accelerometer <- {}
Accelerometer.i2cPort <- null;
Accelerometer.i2cAddress <- null;
Accelerometer.irqPin <- null;
Accelerometer.callBack <- null;
Accelerometer.FullScaleEnabled <- 0;
Accelerometer.Init <- function(port, address, irq, call = null){
i2cPort = port;
i2cAddress = address << 1;
irqPin = irq;
callBack = call;
// Set event handler for irq
IOExpander.setIRQCallBack(irqPin, irqHandler.bindenv(this))
//TODO: Need to get all of our edges configured right so that we can clear the interrupts and not miss anything.
// Configure pin as input, irq on rising edges
IOExpander.setDir(irqPin, 0);
IOExpander.setPullUp(irqPin, 1);
IOExpander.setIRQMask(irqPin, 0);
IOExpander.setIRQEdges(irqPin, 1, 0);
//Reset Accelerometer
write(0x21, 0x40); // CTRL_REG2 (21h) 0x40 = B01000000
//SPI 4/3 wire
//1=ReBoot - reset chip defaults
//n/a
//filter off/on
//filter for freefall 2
//filter for freefall 1
//filter freq MSB
//filter freq LSB - Hipass filter (at 400hz) 00=8hz, 01=4hz, 10=2hz, 11=1hz (lower by 4x if sample rate is 100hz)
//Enable Accelerometer to sample 2g @ 400Hz on all 3 axis's
write(0x20, 0xC7); // CTRL_REG1 (20h) 0xC7 = B11000111
//sample rate 100/400hz
//power off/on
//2g/8g
//self test
//self test
//z enable
//y enable
//x enable
//Enable single and double tap interrupts
write(0x3B, 0x33); //CLICK_THSY_X (3Bh) THSy3 | THSy2 | THSy1 | THSy0 | THSx3 | THSx2 | THSx1 | THSx0 //From 0.5 g (0001) to 7.5 g (1111) with step of 0.5 g
write(0x03, 0x03); //CLCIK_THZ (3Ch) -- -- -- -- | THSz3 | THSz2 | THSz1 | THSz0
write(0x3D, 0xFF); //CLICK_Timelimit (3Dh) From 0 to 127.5 msec with step of 0.5 msec
write(0x3E, 0x00); //CLICK_Latency (3Eh) From 0 to 255 msec with step of 1 msec
write(0x3F, 0xFF); //CLICK_Window (3Fh) From 0 to 255 msec with step of 1 msec
write(0x38, 0xFF); //CLICK_CFG(38h) //3F - | Latch Interrupt Request (LIR) | Double_Z | Single_Z | Double_Y | Single_Y | Double_X | Single_X
write(0x22, 0x07) //CTRL_REG3 [interrupt register] (22h) IHL | PP_OD | I2_CFG2 | I2_CFG1 | I2_CFG0 | I1_CFG2 | I1_CFG1 | I1_CFG0
read(0x39); //Clear the interrupt
}
//TODO: there is a memory leak somewhere in this code...
Accelerometer.irqHandler <- function(clickSrc = null) {
if(clickSrc == null) //else we were called recursively to clear the queue of things on the Accelerometer IRQ
clickSrc = read(0x39); //CLICK_SRC (39h) -- | Interrupt Active (IA) | Double_Z | Single_Z | Double_Y | Single_Y | Double_X | Single_X
//this read should clear the interrupt register on the accelerometer
if (callBack == null){
return;
}
//server.log("Accelerometer = " + byteString(clickSrc, 1))
if(bitRead(clickSrc, 6)){ //Interrupt Active
if(bitRead(clickSrc, 0)){
callBack(1, "X") ;
} else if(bitRead(clickSrc, 1)){
callBack(2, "X") ;
} else if(bitRead(clickSrc, 2)){
callBack(1, "Y") ;
} else if(bitRead(clickSrc, 3)){
callBack(2, "Y") ;
} else if(bitRead(clickSrc, 4)){
callBack(1, "Z") ;
} else if(bitRead(clickSrc, 5)){
callBack(2, "Z") ;
}
} else{
server.log("ACCEL IRQ Called for no reason")
}
clickSrc = read(0x39) //If the events happen quickly, sometimes the accelerometer will set its IRQ Pin low but the register will be queued with the next event, effectively blocking new ones. This additional reading of the IRQ Register and recursive calling fixes that
if(clickSrc != 0) this.irqHandler(clickSrc)
else {
imp.wakeup(0.10, function(accel = this){
while(accel.read(0x39) != 0){}
IOExpander.clearIRQ(accel.irqPin); //The accelerometer can flood the IOExpander IRQ Pin and things can get out of sync - this tries to fix that
});
}
}
// Read a byte
Accelerometer.read <- function(register) {
local data = i2cPort.read(i2cAddress, format("%c", register), 1);
if(data == null) {
server.log("I2C Read Failure");
return -1;
}
return data[0];
}
// Write a byte
Accelerometer.write <- function(register, data) {
i2cPort.write(i2cAddress, format("%c%c", register, data));
}
//TODO: We could create an I2C singleton that takes an i2cAddress and performs all these functions instead of repeating them in each class... Not sure which design pattern is better..
// Write a masked bit pattern
Accelerometer.writeMasked <- function(register, data, mask) {
local value = read (register);
value = (value & ~mask) | (data & mask);
write (register, value);
}
Accelerometer.EnableFullScale <- function() { //Sets Sensitivity to 72mg/digit and range to +-9.2g's
writeMasked(0x20, 0xFF, 0x20);
FullScaleEnabled = 1;
}
Accelerometer.DisableFullScale <- function() { //Sets Sensitivity to 18mg/digit and range to +-2.3g's
writeMasked(0x20, 0x00, 0x20);
FullScaleEnabled = 0;
}
Accelerometer.twosComplement <- function(val) { //Eight bit two's compliment function. In order to change to other word lengths, edit the two hex values
if(val & 0x80) {
val = -((~val & 0xFF) + 1); // Negative two's complement value
}
else {
val = val; // Positive value
}
return val;
}
Accelerometer.reading2Gs <- function(val) {
val = twosComplement(val)
if (FullScaleEnabled == 0){ //High Sensitivity, Low Measurement Range
val = val*0.018;
}
else if (FullScaleEnabled == 1){//Low Sensitivity, High Measurement Range
val = val*0.072;
}
return val;
}
//Functions return acceleration in their axes in g's
Accelerometer.getZ <- function() {
return reading2Gs(read(0x2d)); //
}
Accelerometer.getY <- function() {
return reading2Gs(read(0x2b));
}
Accelerometer.getX <- function() {
return reading2Gs(read(0x29));
}
//
//// User set points. Adjust these to your washing machine
//local timeout = 3 // the duration in minutes
//local POLL_INTERVAL = 0.5 // interval is seconds between polling the acellerometer
//local ACCEL_THRESHOLD = 50; // threshold signal for teh accelerometer
//local INTERNAL_TIMEOUT = timeout*60/POLL_INTERVAL // scale by poll interval
//
//server.log(format("Poll Interval: %f seconds", POLL_INTERVAL));
//server.log(format("Acceleration Threshold: %d", ACCEL_THRESHOLD));
//server.log(format("Timeout: %d minutes", timeout));
//server.log(format("Internal Timeout: %d", INTERNAL_TIMEOUT));
//
//// Change to a random color every 500ms
//function poll() {
// imp.wakeup(POLL_INTERVAL, poll)
//
// if (running == 1) {
//
// local xtest = accelerometer.getX()
// local ytest = accelerometer.getY()
// local ztest = accelerometer.getZ()
//
//
// local x = ((xtest + 128) % 256) - 128.0
// local y = ((ytest + 128) % 256) - 128.0
// local z = ((ztest + 128) % 256) - 128.0
//
// averageX = averageX * exponentialFactor + x * (1-exponentialFactor)
// averageY = averageY * exponentialFactor + y * (1-exponentialFactor)
// averageZ = averageZ * exponentialFactor + z * (1-exponentialFactor)
//
// local motion = math.abs(x-averageX) + math.abs(y-averageY) + math.abs(z-averageZ)
// //server.log(format("current motion:! %d", motion));
//
// if(motion > ACCEL_THRESHOLD && warmup > 3/(1-exponentialFactor)) {
// server.log(format("triggered! %d", motion))
// timer = 0
// startTimer = 1
// } else {
// warmup += 1
// if (timer > INTERNAL_TIMEOUT){
// server.log("send sms")
// out.set (1)
// server.show("washing machine is done")
// server.log("turning off timer")
// timer = 0
// startTimer = 0
// running = 0
// led.setLevels(100, 1, 1);
// }
// }
//
// if (startTimer) {
// timer++
// server.log(format("timer count:! %d", timer));
// }
// }
//}
/** ------------------------- END ACCELEROMETER CODE --------------------------- **/
/*******************************************************************************
ARGB LIGHT SENSOR CODE (ADJD-S311-CR999)
*******************************************************************************/
/*
#define CTRL 0x00
#define CONFIG 0x01
#define CAP_RED 0x06
#define CAP_GREEN 0x07
#define CAP_BLUE 0x08
#define CAP_CLEAR 0x09
#define INT_RED_LO 0xA
#define INT_RED_HI 0xB
#define INT_GREEN_LO 0xC
#define INT_GREEN_HI 0xD
#define INT_BLUE_LO 0xE
#define INT_BLUE_HI 0xF
#define INT_CLEAR_LO 0x10
#define INT_CLEAR_HI 0x11
#define DATA_RED_LO 0x40
#define DATA_RED_HI 0x41
#define DATA_GREEN_LO 0x42
#define DATA_GREEN_HI 0x43
#define DATA_BLUE_LO 0x44
#define DATA_BLUE_HI 0x45
#define DATA_CLEAR_LO 0x46
#define DATA_CLEAR_HI 0x47
#define OFFSET_RED 0x48
#define OFFSET_GREEN 0x49
#define OFFSET_BLUE 0x4A
#define OFFSET_CLEAR 0x4B
*/
//
//#include <Arduino.h>
//#include <ADJDS311.h>
//
//ADJDS311::ADJDS311(int led_pin) {
// _led_pin = led_pin;
//}
//
//
//
//void ADJDS311::calibrate(){
// delay(2); //wait for LED to come full brintness if used
// calibrateColor(); // This calibrates R, G, and B int registers
// calibrateClear(); // This calibrates the C int registers
// calibrateCapacitors(); // This calibrates the RGB, and C cap registers
//}
//
//void ADJDS311::ledOn(){
// digitalWrite(_led_pin, HIGH);
//}
//
//void ADJDS311::ledOff(){
// digitalWrite(_led_pin, LOW);
//}
//
//
//void ADJDS311::init(){
// Serial.begin(9600);
// Serial.print("init");
//
// pinMode(_led_pin, OUTPUT); // Set the sensor's LED as output
//
// Wire.begin();
// delay(1); // Wait for ADJD reset sequence
//
//
// colorCap[0] = 9;
// colorCap[1] = 9;
// colorCap[2] = 2;
// colorCap[3] = 5;
// // values must be between 0 and 15
//
//
// colorInt[0] = 2048;
// colorInt[1] = 2048;
// colorInt[2] = 2048;
// colorInt[3] = 2048;
// // max value for these is 4095
//
//
// // sensor gain registers, CAP_... to select number of capacitors.
// // value must be <= 15 */
// writeRegister(colorCap[RED] & 0xF, CAP_RED);
// writeRegister(colorCap[GREEN] & 0xF, CAP_GREEN);
// writeRegister(colorCap[BLUE] & 0xF, CAP_BLUE);
// writeRegister(colorCap[CLEAR] & 0xF, CAP_CLEAR);
//
// //Write sensor gain registers INT_...
// //to select integration time value must be <= 4096
// writeRegister((unsigned char)colorInt[RED], INT_RED_LO);
// writeRegister((unsigned char)((colorInt[RED] & 0x1FFF) >> 8), INT_RED_HI);
// writeRegister((unsigned char)colorInt[BLUE], INT_BLUE_LO);
// writeRegister((unsigned char)((colorInt[BLUE] & 0x1FFF) >> 8), INT_BLUE_HI);
// writeRegister((unsigned char)colorInt[GREEN], INT_GREEN_LO);
// writeRegister((unsigned char)((colorInt[GREEN] & 0x1FFF) >> 8), INT_GREEN_HI);