-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathsensorCAM.ino
More file actions
2843 lines (2670 loc) · 166 KB
/
sensorCAM.ino
File metadata and controls
2843 lines (2670 loc) · 166 KB
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
//sensorCAM Alpha release limit >|
#define BCDver 321 // maxSensors PROM bug fix & testing for IR bright4 vs. bright(16)
/* //v321 add STARTUP_DELAY. fix blank PROM maxSensors bug. Allow m0,98 (80)
* add focused bright option for bpd, specifically for IR sensor markers (for h5 && bsn>07)
//v320 add check byte[31] to i2c myWire.write
//v319 bulk removal pvtThresholds (t1,%%). Limit minSensors < maxSensors. <Nt ##> sends selected sensors.
//v318 now also Accepts CS style sensorCAM cmds (<N >), and more spaces in commands, from CAM USB monitor
//v317c set minSensors to skip scroll status & (working) sensors below minSensors, unless set to default 0
//v317 block LED handling re. latched & shared sensors ; convert EPvFlag to byte; enhanced EPminSensors
//v316 added ESP32_WROVER_CAM option (integral USB); made QLED configurable via NLED in config.h; fix <NM>
//v315 fix to '&' parzing, make 'v 0' from CS equivalent to '^'
//v314 fix 'v1' command bug introduced v310
//v313 corrected a line bug (dx) and suppressed some debug output
//v312 introduce int8_t deltaX[80] to allow finer line angles. dx -64 to 63. Saves to EPROM. Remved _SFs
//v311 improved setup for line sensor '\%%' Add linear markers to image 'Y' & correct f565to666() ptr
//v310 added parser and modified processCmd() Remove some redundancies
//v308 add tcounter for 't' argument of 2-30. Linear STEPX_SF & STEPR_SF. Only 'a%%,r,x' clears pvtThreshold
//v307 getting line sensors cmd '/' and '\' working optimally. 'v' for version, 'v1' or 'v2' for webcam
//v306 pvtThreshold for S00 & linear banks. 'a' now clears pvtThreshold. Create linear '/' & '\' cmds.
//v305 more minor code neatening & array initialisation. Correct Linear stepR calculation
//v304 move globals to static functions where possible
//v303 start localising variables. check r00 malfunction & adjust "i2c cmd:" display
//v302 add '+' alignment, & experiments with wire.write() strategies
//v301 adjust i2c 'p' & 'q' cmd. added Prof's startup delay(2000)
//v300 working mods to deal with new IO_EXSensorCAM.h driver(v300)
*/
#if __has_include ( "configCAM.h")
#include "configCAM.h"
#else
#warning configCAM.h not found. Using defaults from configCAM.example.h
#include "configCAM.example.h"
#endif
#define NUMdigPins 80 //CS can create fewer to save memory.
#define NUManalogPins 0 //must be 4 for UNO emulation
//#define SUPPLY 10 //see configCAM.h - set period to 10 half-cycles of mains (50Hz) (use 25 for 60Hz)
#define CYCLETIME 100000 //100mSec cycle time syncs with 5cycles of 50Hz and 6 cycles at 60 Hz
#ifndef BRIGHTSF
#define BRIGHTSF 3 //increases sensitivity. If 1, 20% change adds 3*SF to diff. 5% is ignored
#endif
#ifndef SEN_SIZE
#define SEN_SIZE 0 //sensor expansion - add SEN_SIZE rows and columns + through 4x4 sensor.
#endif
#ifndef STARTUP_DELAY
#define STARTUP_DELAY 2000
#endif
#define EXIOINIT 0xE0 // CS: CAM config request
#define EXIORDY 0xE1 //response OK & ready for next ioexpander cmd
#define EXIODPUP 0xE2 // CS: pullup settings (ack only)
#define EXIOVER 0xE3 // CS: CAM version request
#define EXIORDAN 0xE4 // CS: requests analogue data array (ignored or send digital array)
#define EXIOWRD 0xE5 // CS: CAM enable(a) or refresh(r) specified sensor (deprecate?)
#define EXIORDD 0xE6 // CS sensor status request
#define EXIOENAN 0xE7 // CS: CAM analogue pin enable (ack only)
#define EXIOINITA 0xE8 // CS: requests analog pin info (ignored)
#define EXIOPINS 0xE9 //response code
#define EXIOWRAN 0xEA //CS: can use to create a sensor position (r,x)
#define EXIOERR 0xEF //response - last command erroneous
#define CAMERR 0xFE //CAM cmd error header
#include "esp_camera.h" //Define i2c interface parameters--- Any difference between “Wire.h” and <Wire.h> ??
#include <Arduino.h>
#include <WiFi.h> //compiler still finding: Multiple libraries were found for "WiFi.h"
//Used: C:\Users\Barry\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.3-RC1\libraries\WiFi
//Not used: C:\Program Files (x86)\Arduino\libraries\WiFi
#include <Wire.h> //esp32 i2c Wire library
TwoWire MyWire = TwoWire(0); //Create second i2c interface (don't use Wire1 = )
// define appropriate board (only AI_THINKER tested so far
#ifndef CAMERA_MODEL_WROVER_KIT // define in configCAM.h if appropriate
#define CAMERA_MODEL_AI_THINKER // Select camera model for ESP32-CAM (deault: has OV2640 Camera module)
#endif
#include "camera_pins.h" //for thinker, includes #define XCLK_GPIO_NUM 0
#define I2C_FREQ 100000 //Master sets i2c clock frequency MEGA uses default clock (100kBits/sec)
//Define GPIO ports for I/O use
#ifdef CAMERA_MODEL_AI_THINKER
#define I2C_SDA2 15 //for MyWire (twowire) i2c data GPIO pin 15
#define I2C_SCL2 13 //for MyWire (twowire) i2c clock pin
#define WEBPIN 14 // if grounded on Reset, this pin invokes web server mode - uses default WiFi(1) ssid .
#define GPIO0 0 // Used as CSI MCLK output, so best to not ground while running UNLESS in reset mode.
#define FLASHLED 4 //GPIO4, when 4 HIGH turns on Flash LED. Use ~25uSec pulse to indicate new fb image.
#define BLK0LED 33 //tried GPIO12 - DANGEERUS 12 MUST BE LOW on reset to correctly reset intenals for RAM
#define BLK1LED 33 // N.B. tried: GPIO12 and stuffed a CAM -GPIO12 used to set flash programming voltage!
#define BLK2LED 33 //to use 33 (on-board LED) need to rework CAM to repurpose P_OUT pin(P1-4)(see Youtube)
#define BLK3LED 33 //consider using 4 if can disable flash (remove resistor from CAM?)(but flash useful!)
#define PLED 14 //assign a LED to be programmable. i.e. 'n' cmd assigns a bank status to the PLED
#define QLED 2 //configurable bank NLED output
//GPIO14 multipurposed - LED pulls GPI14 high. Need to Gnd GPIO14 to select web server.
//GPIO0 unavailable. Need to Gnd GPIO0 to program then needed for CAM CSI_MCLK
//GPIO1 & GPIO3 reserved for TX/RX USB comms.
//GPIO12 best left untouched. Used to set internal voltages for Embedded Flash?
// (tried to use, but rendered FLASH RAM unprogrammable!(stuffed one CAM)
#else //set options for ESP32_WROVER_KIT
#define I2C_SDA2 15 //for MyWire (twowire) i2c data GPIO pin 15
#define I2C_SCL2 13 //for MyWire (twowire) i2c clock pin
#define WEBPIN 14 // if grounded on Reset, this pin invokes web server mode - uses default WiFi(1) ssid .
#define GPIO0 0 // Used as CSI MCLK output, so best to not ground while running UNLESS in reset mode.
#define FLASHLED 2 //GPIO2, when 2 HIGH turns on Red LED. Use ~25mSec pulse to indicate new fb image.
#define BLK0LED 33 //tried GPIO12 - DANGEERUS 12 MUST BE LOW on reset to correctly reset intenals for RAM
#define BLK1LED 33 // N.B. tried: GPIO12 and stuffed a CAM -GPIO12 used to set flash programming voltage!
#define BLK2LED 33 //to use 33 (on-board LED) need to rework CAM to repurpose P_OUT pin(P1-4)(see Youtube)
#define BLK3LED 33 //consider using 4 if can disable flash (remove resistor from CAM?)(but flash useful!)
#define PLED 14 //assign a LED to be programmable. i.e. 'n' cmd assigns a bank status to the PLED
#define QLED 32 //assign a LED to configurable bank NLED output
#endif
#include <EEPROM.h>
#define EEPROM_SIZE 320+8+80+80+80 //long pointers to image +reboot flag(int)(+threshold,nLED,min2flip,maxSensors?)
#define EPvFlag EEPROM_SIZE-80-80-80-8 //vFlag stored as (1byte)integer, others as byte
//#define also stepr/x_SF??
#define EPminSensors EEPROM_SIZE-80-80-80-5 //beware of 0xFF
#define EPnLED EEPROM_SIZE-80-80-80-4 //NOTE: these cells have garbage(FF?) in unprogrammed CAM - needing init.
#define EPthreshold EEPROM_SIZE-80-80-80-3
#define EPmin2flip EEPROM_SIZE-80-80-80-2
#define EPmaxSensors EEPROM_SIZE-80-80-80-1 //use to limit printout of sensor states (if > 40, loop time may suffer)
#define EPSensorTwin EEPROM_SIZE-80-80-80 //save SensorTwin array
#define EPpvtThreshold EEPROM_SIZE-80-80 //Individual thresholds (use default threshold if =255) also lineardr
#define EPlineardX EEPROM_SIZE-80 //save xsize for linear sensors
//maxSensors may also be used to avoid other wasted processing time.
// WARNING!!! PSRAM IC required for UXGA resolution and high JPEG quality
#define RGB565p 2 // bytes per pixel in RGB565 image
#define FBPITCH (320*RGB565p) // bytes per row of FB (QVGA=320 pixels)
bool DEBUG[11]={false,false,false,false,false,false,false,false,false,false}; //show detail debug to USB output
#define IFsc if(scroll==true) //output progressive scroll data
#define Spr Serial.print //shortens long print statements.
#define IF0 if(DEBUG[0]) //Higher priority debug
#define IFT if(DEBUG[1]) //Debug control of timing output
#define IFD if(DEBUG[2]) //Lower priority Debug control
#define IFI2C if(DEBUG[3]) //output generated i2c packets
#define IFN if(DEBUG[4]) //output relating to pixel noise measurements
#define IF5 if(DEBUG[5]) //if bsn>=010 use 4pixel (IR) brightness only //show parz output
#define IF6 if(DEBUG[6]) //grab_ref message printed on full execution of S666init_SensorRefs(prefill)
#define IF7 if(DEBUG[7]) //stop/wait on a trip of bank # or sensor 16. only if LAST 'h' was h7 or h7#
#define IF8 if(DEBUG[8]) //write EX-CS cmd to USB e.g. EXIODPUP gives "E2", undefined cmds give "#EF" error
#define S666_pitch 48 //length of one sensor rgb666 data of 16 pixels 4x4x3bytes saved in Sensor666[]
#define S666_row 12 //length of row for one sensor (_pitch= s666_row x 4(rows) (for QVGA resolution) 4x3
#define AVCOUNT 32 //Nominate the number of frames to average for 'r' reference image
#define NUM2AVERAGE 32 //variables for auto references averaging & updating for unoccupied enabled sensors
#ifndef NLED //define NLED in config.h to select a BLK for QLED indicator pin
#define NLED 0 //default 0 maintains consistency with earlier versions
#endif
/* sensorCAM commands:
a%% Activate/enAble sensor[%%] refresh Sensor_ref[%%], cRatios etc. from S[%%] image (48 bytes) in latest frame.
a%%,rrr,xxx *Set coordinates of Sensor[%%] to row/col: rrr/xxx AND activate and auto ref. Verify with p$ cmd.
b%[#] bank % sensors. Show which sensors OCCUPIED(1) (in bits 7-0) [brightSF=# ('h'shows #)] <Request $+1 bytes>
c$$$$ *reCalibrate camera CCD occasionally and grab new references for all enabled sensors. (Beware of doing this
while any sensors are occupied! Obstructed sensors will later need an r%% Check all bank LEDS are off
AND check all sensors are unoccupied before recalibrate. Can set BRI CON SAT AWB through c$$$$ e.g. c0120
Can change default setting for AWBg,AEC,AECd,AEL,AGC,AGg with six extras: c$$$$1111119. c$$$$ resets them!
d%%[#] *Difference score in colour & brightness between Ref & actual image. Show # grabs. <Request 4 data bytes>
e *EPROM save of any new Sensor offset positions & 4 parameters. Warning: May only work once per reset
f%% *Frame buffer sample display. Prints latest binary image of SensorRef[%%] & Sensor[%%] <Requests 4x28 bytes>
g *Get Camera Status. Displays most current settings available in webcam window. (also works in video mode)
h$ Help(debug)output - h to turn OFF, h0 turns ON detailed USB output. h1:more; h2:timing; h3:i2c; h4:Noise
i%% *Individual sensor %% Information. Optional i%%,$$ sets Twin sensor[$$] for S%%. <i2c Request 10 data bytes>
j$# adJust camera setting $ to value # and display most settings (as for ‘g’) ‘j’ does NOT get new refs. - use r
k%%,rrr,xxx *Set coordinates of Sensor[%%] to row: rrr & xVal: xxx. Follow with r%%. Verify values with p$ cmd
l%% (Lima) force sensor %% to ON (1= occupied (LED lit) & also set SensorActive[%%] false to inactivate updating
m$[,%%] *Min. no.($) of sequential frames to trigger Occupied threshold for detection (def.=2)[%% sets maxSensors]
n$[,%%] *Number of bank($) assigned to the programmable nLED to show bank$ occupancy status. [%% sets minSensors]
o%% (Oscar)force sensor %% off (0=UN-occupied (LED off) & also set SensorActive[%%] false to inactivate updating
p$ *Position Pointer table for banks 0 to $ giving DEFINED sensor r/x positions. p%% shorter. <32 data bytes>
q$ *Query bank $, to show which sensors enabled (in bits 7-0). 1=enabled. q9 gives ALL <Req. $+2 data bytes>
r%%[*] Refresh average Sensor_Ref[%%] (Iff defined), enable & calc. cRatios etc. [r%%* refreshes bank up to S%%]
r00 Renew Average Refs etc. for ALL defined sensors. Ignores active[]. Sensor 00 reserved for brightness ref.
s%% *Scan for new location for sensor %% (00-97). If found, records location in Sensor[%%]. Further setup needed.
Scan looks for a bright LED on a dimmer background. The LED should be placed on the desired sensor pos'n.
If satisfied with the scan, the user should REMOVE the LED, set lighting, and do an r%% to set enabled AND
record a new reference image (also computes colour ratios & brightness) The location must be unoccupied!
t##[,%%] Threshold level being used. show/set for Sensors (## sets 32-98)[for S%%] t1 toggle scrolling <32 bytes>
u%% *Un-define/remove sensor %% by setting INACTIVE & Sensor[%%]=0. u99 erases ALL. Needs ‘e’ to erase from EPROM
v[1|2] *Video mode. Reboot CAM in webserver mode. [v2” will connect to 2nd (alt.) router ssid] ('v' gives version)
w *Wait for new command line (\n) before resuming loop(). (handy to stop display data scrolling)
x### Presets column for start of Processing image transfer (0-318)
y### Suspend imaging. Proceeds to write header & Zlength row### pixels to USB port for Processing4. 'yy' resumes
yy Used exclusively to end a series of 'y' commands (that suspended imaging) and returns CAM to run mode.
z### Presets Zlength for line length (even number of pixels) in image transfer (2-320) (see 'y')
F|R *Reset commands – will Reset CAM and initiate the Sensor mode. Both will Finish the WebServer (v) mode.
\%%,$,$ *Line sensor S%% uses a 4 step line with $,$(r,x) steps AND extend using rest of bank sensors.('/' for -x)
& *Diagnostic output of statistics. Gives histogram of No. of frames of ("noise-tripped") occupations.
@## Changes 'Occupied' indicator from 35('#') to any nominated ASCII char. from 1 to 127. @12 for BOLD 'spade'
+#,$ *Add to position - camera alignment adjustment. Move ALL sensors #(0-3) pixels in $ direction(0-7)(North-NW)
* These commands typically for diagnostic/setup use only. They wait for a line feed or command to resume.
*/
unsigned long int starttime=0; //counter giving each start time in uSec.
int E6counter=200; //E6 EXIOexpander cmd occurs at 100/sec. stop E4 debug output after #
byte analoguePinMap[] = {12,13,14,15,16,17}; //default pin map for uno & nano vpin=firstVpin+Map[i]?
byte EXCScmd0=0; //last EXCS cmd
bool EXCScmd=false; //true if last i2c cmd from EX
int firstVpin=0; //to be set by EX-CS EXIOINIT function
int Bri=0, Con=1, Sat=2, AWB=1, AWBg=1, AEC=1, AECd=1, AEL=1, AGC=1, AGg=9; //initial settings of 'c' variables
int AWB9=1, AGC9=1; //after prefillRef delay (9sec) change AWB and AGC if desired
// arguments for 'c' command; Bri Con Sat AWB AWBg AEC AECd AEL AGC AGg (NO SPACES!)
int dbug=10;
char OCc = '#'; //OCc character to indicate OCCUPIED in scrolling status line. @12 good for Arduino IDE
int emptyStateCtr[80]; //used to count No. of loops a sensor NOISE caused "OCCUPIED" status
int rbsn = 1; //bsn for current averaging If unoccupied, new 3.2sec ref regularly(~EVERY MIN.?)
bool autoRefSuspended = true; //use to stop auto referencing during conflicting commands e.g. 'r' and 'c' ("SUS")
int force_refs=0; //used to force block refs
// byte RingBuff[2*80*16*3]; //ring buffer to save grabs to average before trip
bool avOddFrame=true; //for av2Frames()
bool boxSensors=false;
int SensorHiCount[80]; //for '&' noise histogram command
int HistoLoops=0;
int SensorHisto[80*5];
//uint32_t i2cCtr = 0; //prepare for i2c
//bool CmdI2C=false;
int nLED=2; //Blk No assigned to programmable LED (PLED)- initially bank 2
int maxSensors=80; //use to limit USB PRINTOUT time & line length
unsigned int minSensors=000; //a lower limit for sensor data output
//used if using more sensors than can otherwise be processed in 100mSec.(42-> max bsNo=51 (5*8+1) ). It limits
//monitor output (if more than 40, sensors loop time may suffer because of printout time to monitor)
unsigned long Sensor[80]; //from EEPROM array of xy coordinates for 10 banks of 8 sensors.
//Sensor[] Offsets into FULL image buffer! xy stored in 4 bytes EPROM
byte Sensor666[80*16*3]; //buffer to hold decoded latest 4x4 sensor images from frame fb (pitch=S666_row=12)
byte Sensor_ref[80*4*4*3]; //array of 80 reference grabs (QVGA: 4x4x3RGB =48 bytes/sensor (total 3840 bytes
unsigned int SensorRefRatio[80*12]; //array of 3 x 4 quadrant colour ratios (r/g g/b b/r)x4
int Sen_Brightness_Ref[80]; //sum of pixels in all quadrant colours combined. max=3024 (4*4*3*63) for 666/pixel
int Sen_Brightness_Ref4[80]; //sum of rgb of centre 4 pixels (4x3=12 bytes)
byte SensorBlockStat[11]={0,0,0,0,0,0,0,0,0,0,0};//array of bits(8/bank) each high if corresponding sensor tripped
// If SensorBlockStat[b] ==0 then NO sensors in that block set are occupied (sensor[b0] is LSB in byte)
unsigned int quad[5]={0,0,0,0,0}; //a temporary place to hold current sensor image quadrant brightness values
unsigned int bright; // (4xquad[0-3] & total in quad[4] )
int brightSF=BRIGHTSF; //a scale factor to weight brightness variation when computing diff + bright*brightSF
bool SensorStat[80]; //state occupied/unoccupied true/false (can set using l%% or o%%. They set inactive)
bool SensorActive[80]; //if false (inactive) then don't update SensorStat[] (can set active with a%%)
byte SensorActiveBlk[10]; //each holds 8 sensor bits(8 bits/block) e.g. SensorActive[07-00] in SensorActiveBlk[0]
byte SensorTwin[80]; //if sensors in pairs use to identify secondary bsn. (should save into eprom!)
byte pvtThreshold[80];
int8_t lineardX[80]; //xlength of a single linear Sensor[%%] pixels (+/-63 max)
int min2flip = 2; //number of exceptional frames required before transitioning output tripped/untripped.
byte mask[8]= {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
int bsNo; //parameter from text commands
int bsn; //general use
int bsnUpdated=-1;
int dbsNo=0; //bsNo chosen with d%% cmd.
int i; //local index counter
int j; //local index counter
int b=0; //local (block) variable
int h7block=1; //h7 stop on block # trip
int aRefCtr=-1; //for 'a' cmd
int absn=0; //char absNo1='0', absNo2='0'; //save 'a' bsn for 'r'
char i2cCmd[64]={'0'}; //string from Master (onReceive)
byte dataPkt[128]={CAMERR}; // prepared packet(s) for i2c (max packet=32 bytes)
bool newi2cCmd=false; // flags the presence of a new i2c serial command string for processing
//int wifi=1; //identify default wifi router (1 or 2)
bool MyWebServer=true;
char Shedssid[]=WIFI_SSID;
char ShedWIFIpwd[]=WIFI_PWD;
char Altssid[]=ALTWIFI_SSID;
char AltWIFIpwd[]=ALTWIFI_PWD;
char* ssid = Shedssid; //must match local WiFi !
char* password=ShedWIFIpwd; //must match local WiFi !
char* ssid2 = Altssid; //alternate ssid use with "v2" cmd
char* password2=AltWIFIpwd;
int fbheight = 240; //number of rows in fb. default QVGA 320x240
int fbwide = 320; //number of pixel columns in fb
int pitch=640*3; //image pixels/row (RGB888 VGA 640x480)
char cmdString[64]; // holds a whole command line from input source
char cmdChar;
bool Yhold=false; //halt refreshing frames so multiple 'y's can see same fb
int Xcolumn=0; //default value for 'x' command
int Yrow=0; //default for 'y' cmd
int Zlength=320; //default full line for 'z' command
char Yheader[10]="y_x_z_ck:";
bool SendYpacket=false;
char i2cData[64]; //array of sensor status data to send on 't' request
bool scroll=true; //if false suppress scrolling output (use 't0')
int tcounter=0; //down counter for delayed scroll "off"
int i2cDatai=0; //array index
int threshold=42; //difference (max Xratio+brightnessRatio) threshold for occupancy
int maxDiff=0; //exact Xdiff match produces minimum maxDiff of 32
int dFlag=0; //flag set by d%% to print out (while>0) diff and sample image for bsNo.
int dMaxDiff=0; //saved maxDiff for 'd' cmd.
int dBright=0; //saved bright for 'd' cmd.
unsigned int releaseTime=0; //counter for loop cycle timing control (target 10Hz?)
unsigned int timer; //*code subsection time measurement ESP32() returns uint32_t NOT long(64)
unsigned int timerLoop=0; //*loop time measurement
unsigned int cFlag=0; //*flag to set off full Sensor_ref[] refresh of all active sensors (triggers c##
unsigned int cDelay=10000; //*cDelay mSec after c## before updating active ref's
int sScan=-1; //flag set by s%% to scan for, and set, new Sensor[sScan] position
int sScanCount=3; //get several frames before doing a scan to clear pipeline
int refBrightness=0; //reference brightness sum of 16 pixles of Sensor_ref[00]
int refActual=0; //actual Sensor[00] brightness on current frame
int prefillRef=90; //after first 9sec of fb_gets() (plus seconds from cFlag?) take 80 Sensor reference grabs
byte *imagePtr; //full image buffer pointer
byte *imageFB; //ditto used by 'y' processing
byte rgb666[3]={0,0,0}; //destination for decodeRGB565() (one pixel)
String cStr=" "; //string to hold latest auto adjust settings (AWB AEC AGC CBar)
int averageRbsn=-1; //a flag for main loop to do averaging
int averageRcounter = -1; //down counter for frame averaging (see 'r');
unsigned int stdCtimer=3600*1000*24; //*24hours minimum - use with 'c' comand (int=32 bit)
void startCameraServer(); //cameraInit() seems to initiate TwoWire(1) for CAM internal i2c
// *********************************************************************
void setup() {
Serial.begin(BAUD); //downloads with 115200+. Note: Mega still using 9600 for more reliable visual basic!
Serial.setDebugOutput(true);
Serial.println();
printf("\nHello\nBCD version No %d\n",BCDver); //my software version
printf("Total heap: %d\n", ESP.getHeapSize()); //356488 //BCD: added from esp32-how-to-use-psram
printf("Free heap: %d\n", ESP.getFreeHeap()); //331728 //set Arduino IDE Core Debug Level: "Verbose"
printf("Total PSRAM: %d\n", ESP.getPsramSize()); //4192139 //should output to SerialMonitor
printf("Free PSRAM: %d\n", ESP.getFreePsram()); //4.192139 for 4MB PSRAM
for (i=0;i<(80*16*3);i++){Sensor666[i]=10; Sensor_ref[i]=10;} //ensure none remain at 0 or risk /0 reboot
for (i=0;i<(80*12);i++) SensorRefRatio[i]=1;
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0; //AIthinker No
config.pin_d0 = Y2_GPIO_NUM; //5
config.pin_d1 = Y3_GPIO_NUM; //18
config.pin_d2 = Y4_GPIO_NUM; //19
config.pin_d3 = Y5_GPIO_NUM; //21
config.pin_d4 = Y6_GPIO_NUM; //36
config.pin_d5 = Y7_GPIO_NUM; //39
config.pin_d6 = Y8_GPIO_NUM; //34
config.pin_d7 = Y9_GPIO_NUM; //35
config.pin_xclk = XCLK_GPIO_NUM; // 0 *****!
config.pin_pclk = PCLK_GPIO_NUM; //22
config.pin_vsync = VSYNC_GPIO_NUM; //25
config.pin_href = HREF_GPIO_NUM; //23
config.pin_sccb_sda = SIOD_GPIO_NUM;//26
config.pin_sccb_scl = SIOC_GPIO_NUM;//27
config.pin_pwdn = PWDN_GPIO_NUM; //32
config.pin_reset = RESET_GPIO_NUM; //-1
config.xclk_freq_hz = 20000000; //20MHz
pinMode(WEBPIN, INPUT_PULLUP); //startup() uses GPIO(14?) (LOW) to trigger WebServer (jpg) mode
EEPROM.begin(EEPROM_SIZE); //will need to load 80 (long)Sensor[] pointers from EPROM
delay(500); //just copying an EPROM example (why?)
i=int(EEPROM.read(EPvFlag)); //if 1-2 initiate web server via wifi
if(i==2){ssid=ssid2; password=password2;}
printf("Video flag read as %d; Webpin %d\n",i,(1-digitalRead(WEBPIN)));
if(i==0){ //EPROM contains data so use stored parameters - restore threshold,nLED,min2flip,maxSensors
nLED=int(EEPROM.read(EPnLED));
if (nLED>9) nLED=2; //set a default if new EEPROM
minSensors=int(EEPROM.read(EPminSensors)); //use S00-S97(79) default: 0
if(minSensors>79) minSensors=0;
threshold=int(EEPROM.read(EPthreshold)); //use 32-254 default: 45?
if(threshold==255) threshold=45;
min2flip=int(EEPROM.read(EPmin2flip)); //default: 2
if(min2flip>9 || min2flip==0) min2flip=2; //checks for unprogrammed EPROM
maxSensors=int(EEPROM.read(EPmaxSensors)); //debug print limit
if(maxSensors>80) maxSensors=80;
printf("EEPROM set threshold= %d; nLED= %d; min2flip= %d; maxSensors= 0%o\n",\
threshold,nLED,min2flip,maxSensors);
}
for (j=0;j<80;j++) SensorTwin[j]=0; //initialise Twins, if none in eprom.
if((i==0) && (digitalRead(WEBPIN)==1)){ //if HIGH startup in RGB565 mode (else MyWebServer mode)
MyWebServer=false; //do not start MyWebServer
config.pixel_format = PIXFORMAT_RGB565; // YUV422|GRAYSCALE|RGB565|JPEG (see esp_camera.h)
// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(psramFound()){
config.frame_size = FRAMESIZE_QVGA; //QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
config.jpeg_quality = 10; //0-63 0 for highest quality?
config.fb_count = 2;
IFD Spr("fb_count set to 2 as PSRAM found\n");
} else { //setup webserver mode
config.frame_size = FRAMESIZE_QVGA; //select optimum size 640 x 480 for Sensor (speed v resolution)
config.jpeg_quality = 12; //4 better quality BUT slows any conversion to BMP by ~10%
config.fb_count = 1; //investigate speed gain using 327kB fast dynamic RAM. for a 154k fb
}
pinMode(FLASHLED,OUTPUT);digitalWrite(FLASHLED,LOW); //turn off flash LED
//pinMode(FLASHLED,OUTPUT); //to stop cycle flash set FLASHLED to another pin (33?)
pinMode(BLK0LED, OUTPUT);digitalWrite(BLK0LED,HIGH); //normal LED HIGH=off BUT (GPIO12 inverted. AVOID 12!)
pinMode(BLK1LED, OUTPUT);digitalWrite(BLK1LED,HIGH); //set up bank occupied indicators (GPIO 2,14,4,33)
pinMode(BLK2LED, OUTPUT);digitalWrite(BLK2LED,HIGH);
pinMode(BLK3LED, OUTPUT);digitalWrite(BLK3LED,HIGH); //consider logic reversal for all ext. LEDS
pinMode(PLED, OUTPUT);digitalWrite(PLED,HIGH);
pinMode(QLED, OUTPUT);digitalWrite(QLED,HIGH);
}else{ MyWebServer=true; //run CAM in webserver mode (no track Sensors)
pinMode(FLASHLED,OUTPUT);
flashLed(25); //pulse flash to show webserver mode initiation
//clear EEPROM Web flag and proceed to webserver
EEPROM.write(EPvFlag,0); //clear flag at end of EEPROM
EEPROM.commit();
EEPROM.end(); //burn eeprom
config.pixel_format = PIXFORMAT_JPEG;
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else { //could print error as must have psram to operate fully
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
} //end of else{ MyWebServer=true;
printf("Camera init attempt...\n");
esp_err_t err = esp_camera_init(&config); // camera init
if (err != ESP_OK) {
printf("Camera init failed with error 0x%x", err);
delay(10000);
ESP.restart();
}
delay(STARTUP_DELAY); //Prof's mod to help startup. default 2000
MyWire.onReceive(i2cReceive); //set up response to receive & request
MyWire.onRequest(i2cRequest); //links to void i2cRequest()
bool MyWireOK=MyWire.begin((uint8_t)I2C_DEV_ADDR,I2C_SDA2,I2C_SCL2,0); //Slave needs no FREQ - Master's choice
if(MyWireOK) printf("MyWire.begin()INITIAISED OK\n");
else printf("MyWire.begin()FAILED\n");
#if CONFIG_IDF_TARGET_ESP32 //ESP32 needs a call to slaveWrite for Arduino compatibility
uint32_t i2cCtr = 0; //prepare for i2c
IFT printf("Doing slaveWrite() for ESP32\n"); //ESP32-S2 and ESP32-C3 don't need slaveWrite()
char message[64];
snprintf(message, 64, "E%u Packets.", i2cCtr++);//snprintf() puts the text "1 Packets" into char message[64]
message[0]=CAMERR;
MyWire.slaveWrite((uint8_t *)message, strlen(message)); //preload onRequest buffer
IFT printf("Done slaveWrite\n");
#endif
delay(4000);
Spr("Camera initialised ");Serial.println(esp_err_to_name(err));
sensor_t * s = esp_camera_sensor_get();
// drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA); //THIS IS THE FINAL SETTING
#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
s->set_vflip(s, 1);
s->set_hmirror(s, 1);
#endif
/* //using CAMERA_FB_IN_DRAM got figures below CLEARLY ASSIGNING FB TO DRAM!
// printf("AFTER good camera_init and parameter setting & before WiFi.begin\n");
// printf("Total heap: %d\n", ESP.getHeapSize()); //356488->356212 BCD: added from esp32-how-to-use-psram
// printf("Free heap: %d\n", ESP.getFreeHeap()); //331492->258348 set Arduino IDE Core Debug Level: "Verbose"
// printf("Total PSRAM: %d\n", ESP.getPsramSize()); //4192139 unchanged for DRAM. -> 4192127
// printf("Free PSRAM: %d\n", ESP.getFreePsram()); //4.192139 MB!(DRAM) & 4153727 (PSRAM)(38400 for QQVGA)
*/
if(MyWebServer){ //set up WiFi connection
//if WEBPIN held LOW at startup, MyWebServer=true so startup in MyWebServer mode
//(if GPIO0 held low at startup then automatically goes into flash program mode)
ConnectWifi(ssid, password); //WiFibegin()
if (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.println("WiFi failed to connect. Try alt-wifi (v2)?");
ESP.restart();
}
Serial.println("");
Serial.println("WiFi connected");
startCameraServer();
Spr("Camera Ready! To connect, use 'http://");
Serial.println(WiFi.localIP());
flashLed(250); //long pulse flash to show MyWebServer mode established
}
else{ //PROCEED TO SETUP SENSORS using RGB565 & QVGA
for (i=0;i<80;i++){ //set all to UNDEFINED, Brightness Sensor(0/0) defaults to offset 000 if not from EPROM
Sensor[i]=0L; //in absence of EPROM data set all sensors to offset 0 (i.e. Sensor UNDEFINED)
SensorActive[i]=false; //can set undefined sensors active as ALL sensors scanned at present anyway
SensorActiveBlk[i>>3]=0; //clear all Blk bytes also
}
for (i=0;i<80;i++){ //will need to load 80 (long)Sensor[] pointers from EPROM and 80 byte Twin values
Sensor[i]=EEPROM.readLong(i*4); //get a long (4byte) pointer from EEPROM.
SensorTwin[i]=EEPROM.read(EPSensorTwin+i);
pvtThreshold[i]=EEPROM.read(EPpvtThreshold+i); //if =255 then use default threshold
lineardX[i]=EEPROM.read(EPlineardX+i);
if (Sensor[i]>=153600L) Sensor[i]=0; //eliminates wild values (all 1's) from a pristine EPROM
if (Sensor[0]==0) //153600L = size of image
Sensor[0]=(320*120+160)*2; //create first S00 as k00,120,160 (~centre)
if (SensorTwin[i]>79) SensorTwin[i]=0; // 153600L = size of image
if (Sensor[i]!=0){ IFT printf("define Sensor[0x%x]= %lu \n",i,Sensor[i]);
SensorActive[i]=true; //initially activate all sensors defined (i.e. >0) in EPROM
SensorActiveBlk[i>>3]=SensorActiveBlk[i>>3] | mask[i&0x07];
} //Never obstruct Sensor[00]. If brightness SensorStat[00] ever goes true, we have a lighting problem
} //Note: if sensor[0]=0 (top left cell undefined) cmd 'r' will ignore it!
delay(5000); //give the cam settings time to settle then renew
Spr("Turn AWB & AGC OFF(0) after 5 sec. Leave auto adjustments (AWBg,AEC,AECd,AEL) ON(1)\n");
//set AWB,AEC,AGC OFF, or just AWB & CB OFF?? NEEDS MORE EXPERIMENTING
new_camera_settings(Bri,Con,Sat,AWB,AWBg,AEC,AECd,AEL,AGC,AGg); //set Sat,AWB,AEC,AGC,.. to defaults
AWB=AWB9; AGC=AGC9; //Ready for Turn Off AFTER prefillRef delay, say 9000mSec.
// new_camera_settings(0,1,2,0,1,1,1,1,0,9); after prefill delay
printf("\n*** WILL WAIT >10 SECONDS THEN AUTOMATICALLY LOAD ACTIVE SENSOR IMAGE REFERENCES\n");
printf(" ASSUMING ALL SENSORS UNOCCUPIED ***\n");
cFlag=millis(); //record end of setup() time - used to determine when prefillRef happens
}
} //end setup()
// *************************************************************************************************************
void loop() {
//execte once per 100mSeconds (sample rate)
static int frameNo=0; //variables for NOISE measurement code
static int Sensor666Av[48];
static int rollAvFrameR[64]={0}, rollAvFrameG[64]={0}, rollAvFrameB[64]={0}; //rolling Av. values of Sensor[00]
static int rollNsFrameR[64]={0}, rollNsFrameG[64]={0}, rollNsFrameB[64]={0}; //rolling Noise values
static int rollAvR=0, rollAvG=0, rollAvB=0; //RGB components
static int r00Av=0;
static int rollNsR=0, rollNsG=0, rollNsB=0; //RGB components
static int r00NsR, r00NsG, r00NsB;
int cmdCount = 0; // number of characters in serial cmdString[]
int Noise=0; //difference between frame pixel and ref pixel
int nParam=0; //number of parameters parz() found in command line
int16_t p[5]={-1,-1,-1,-1,-1}; //repository for parz() command parameters
/***/ if(prefillRef>1) {Serial.print(prefillRef);Serial.print(" ");}
// ****IF MYWEBSERVER MODE, JUST MONITOR SERIAL PORTS FOR RESET (R or F)
while(MyWebServer) { //monitor for character to trigger reboot back to sensor mode
if (Serial.available() > 0) { //if MyWebServer mode, do nothing but serve web and monitor for Reset cmd.
cmdChar=Serial.read();
if((cmdChar=='R') || (cmdChar=='F')) { //'F' gives Bluetooth ability to "Finish with webserver"
Serial.println("Restarting in sensor mode\n");
WiFi.disconnect(true, true);
delay(3000);
ESP.restart(); //give up on wifi and return to sensor mode
}else Spr(cmdChar);
if((cmdChar=='g') || (i2cCmd[0]=='g')){ //get camera status
i2cCmd[0]='.';
getCAMstatus();
}
} //end if (Serial.available())
if ((i2cCmd[0]=='R') || (i2cCmd[0]=='F')) ESP.restart(); //exit back to sensor mode
} //end MyWebserver code
// ***CALCULATE AND PRINT LOOP TIME - & IF CALLED FOR, UPDATE NEW CAMERA SETTINGS
/***/ IFT Spr("\n******** ");
int loopTime=millis()-timerLoop;
if(loopTime==99) loopTime=100; //add 1 just to stop display jitter with 99
timerLoop=millis();
if(loopTime<100)Spr(' ');
IFsc if(tcounter>0){tcounter--; if(tcounter==1) scroll=false;}
IFsc{ Spr(loopTime); Spr("mS; "); }
HistoLoops++; //increment loop counter
if (stdCtimer < millis()) { //time to invoke new camera settings after 'c' std delay
stdCtimer=1000*3600*24*10; //set timer too large to ever trip again. Long 2^31 > 2,147,000,000,000
new_camera_settings(Bri,Con,Sat,AWB,AWBg,AEC,AECd,AEL,AGC,AGg); //set Sat,AWB,AEC,AGC,.. select AWB OFF
cFlag=millis(); //Time stamp Flag for loop() to reload all references after cDelay sec of flushing frames
}
// ***TAKE A FULL FRAME INTO fb IN RGB565 FORMAT
/***/ IFD Serial.println(" Try a get_picture "); // Get Picture from Camera. New image starts when fb released.
/***/ IFT timer=micros(); // Images are "pipelined" so images are 2 "gets" old. (160mSec)
camera_fb_t * fb = NULL; //transfer rgb565 image #1 into fb (taken
fb = esp_camera_fb_get(); //get most recent frame into fb (and return pointer?)
if(!fb) { Serial.println("Camera capture #1 failed\n"); return;} //no pic fb ? - restart program loop
/***/ IFT{ Spr(timer);timer=micros()-timer;//does camera_fb_get return error if .jpg not ready, or does it wait
/***/ Spr(" fb_get time:"); Serial.println(timer); //zStr);
/***/ }
fbheight=fb->height;
/***/ IF0 printf(" got RGB565 frame. fbheight %d fb->len %d\n",fbheight,fb->len);
// ****DECODE RGB565 FRAME INTO COMPACT SENSOR FRAME OF RGB666 FORMAT
/***/ IFT timer=micros();
int pitch=RGB565p*fb->width; //set pitch for RGB565 image format
imagePtr=fb->buf;
bool converted = f565to666(imagePtr,pitch,Sensor,Sensor666,SensorActive);//decode rgb565 to rgb666 in Sensor666
/***/ IFT{ timer=micros()-timer; Spr("Decode (uS):"); Serial.println(timer);}
if (!converted) {Spr("Conversion to rgb666 failed\n"); return;}
/***/ IFD printf("should now be rgb666 in Sensor666[]\n");
// ****SEE IF IMAGE DUMP (TO PROCESSING4)CALLED FOR & DUMP AS REQUESTED BEFORE NEW FRAME INITIATED
if(Yhold) cmdCount=0; //reset command string index & prepare for a new command following recent y#
while(Yhold){ //do not proceed with loop() until Yhold cleared by "yy\n" command
if(SendYpacket){
imageFB = fb->buf; //set a ptr to start of image buf
if(boxSensors){ //put a box around active sensors in image
for (int sen=0; sen<maxSensors; sen++) { //limit to first maxSensors
if (SensorActive[sen]) boxIt(Sensor[sen],sen); //box 4x4 sensor at Sensor[] location
}
boxSensors=false; //don't do again - only on first 'y' command of series
}
j = Xcolumn*2 + Yrow*320*2; //starting point in data //then send an ASCII header
/***/ Spr('\n');Spr('y');Spr(Yrow);Spr('x');Spr(Xcolumn);Spr('z');Spr(Zlength);Spr(";\n"); //printf() too slow
Yheader[1]=byte(Yrow); Yheader[3]=byte(Xcolumn/2); Yheader[5]=byte(Zlength/2);
uint32_t Ycksum=0; //Calculate checksum
for (i=0;i<6;i++) Ycksum += int(Yheader[i]); //does not include chsum or ':' in summation
for (i=j;i<(j+Zlength*2);i+=2) {
if(!imageFB[i]) imageFB[i] = 0x20; //tweak to data so no NUL bytes by adding LSB to even green
if(!imageFB[i+1]) imageFB[i+1]= 0x08; //ditto red (Note: 0x08 = BS backspace)
Ycksum += imageFB[i]; Ycksum += imageFB[i+1];
}
Yheader[6]=byte(Ycksum & 0x00FF); Yheader[7]=byte(Ycksum >>8);
i=Serial.write(Yheader,9); //write 9 header bytes
if(i != 9) printf("Y header write error %d\n",i);
i=Serial.write(&imageFB[j],Zlength*2); //write data
if(i != Zlength*2) printf("Y data write error %d\n",i);
SendYpacket=false;
}
while (Serial.available() > 0) { //each command is followed by a fresh loop to "get_image"
cmdChar=Serial.read();
cmdString[cmdCount]=cmdChar; //add it to cmd string
cmdCount+=1;
cmdCount=constrain(cmdCount,0,20); //increment & limit string length
if (cmdChar == '\n'){ //don't process cmdString until get LF (\n)
nParam=parz(cmdString,p);
/***/ IF5 printf(("parz %d parameters; p[0] %c %d %d %d %d \n"),nParam, byte(p[0]),p[1],p[2],p[3],p[4]);
processCmd(imagePtr,pitch,Sensor,nParam,p); //process command line
cmdCount=0; //reset command string index
}
}
} //end of while(Yhold)
// ****BEFORE DISCARDING RGB565 fb FRAME, CHECK IF NEEDED TO PROCESS A sCAN or cALIBRATE COMMAND REQUEST
long bright_spot=0; //pointer to the brightest spot in BMP/RGB666 image
if (sScan>=0){ // scan full frame for a bright new Sensor[sScan] offset position
sScanCount -=1; // decrement frame count to flush pipeline
if(sScanCount==0){
bright_spot = scan( imagePtr, pitch,fb->width, fbheight, RGB565p); //check imageData for a bright spot
Sensor[sScan] = bright_spot - long(2 * RGB565p + 2 * pitch); //point to first byte top left corner
printf("Scan: New Sensor %d/%d(%d) bright pixel position: row=%d column(x)=%d offset %lu\n",
sScan>>3,sScan&7,sScan,int(bright_spot/pitch),int(bright_spot%pitch)/2,bright_spot);
printf(" YOU MUST DO an r%d%d TO RECORD NEW IMAGE REFERENCE DATA *AFTER* removing brightspot and "
"restoring light level.\n",sScan/8,sScan%8);
printf(" Press Enter for fresh frames (DO NOT DO r%%%% here)\n");
sScan=-1; // clear request. //NOTE: printed pixel pos'n is the bright_spot NOT the Sensor corner pos'n
wait(); // (sensor corner reduced by 2*pitch+4)
}
}
// ****DO A STEP TOWARDS AVERAGING Sensor[bsNo] IF COUNTER SET.
refRefresh(autoRefSuspended); //try to auto refresh a bsNo reference (if not tripped)
if (averageRcounter>0) averageRcalculation(averageRbsn,AVCOUNT); //update average ref[bsn] process ('r')
// ****CALCULATE AVERAGE FOR Sensor[00] AND AVERAGE NOISE (& PEAK NOISE?)
frameNo += 1; frameNo &= 0x3F; //inc. frame counter reset to 0 after 0x3F(63)
if(frameNo==1) { //every 64th frame update Sensor_ref[00] & print 4x4 new av & current red(noisiest?) values
/***/ IFN Spr("av&cur Red ref[0] "); //NOTE THIS DOES NOT UPDATE BRIGHT OR COLOUR RATIOS!
for (i=0;i<48;i++) { //48 = 4x4pixels*3colours
Sensor_ref[i]=Sensor666Av[i]>>6; //update Sensor_ref[0/0] every 6.4 seconds (64 frames) (av = sum/64)
/***/ IFN{ Spr('&');Spr(Sensor_ref[i]); } //print whole ref (DEC)
/***/ IFN if(i%3==0) {Spr(" [");Spr(i);Spr("]:");Spr(Sensor_ref[i]);Spr(' ');\
/***/ Spr(Sensor666[i]);} //print (DEC) one (red) colour of new sensor_ref[00] & actual[00]
/***/ Sensor666Av[i]=0; //initialise for next 64 frames
/***/ }
/***/ IFN Spr("\n......."); //try to keep next line aligned.
}
//compute new cratios etc for Sensor[00] latest av. reference UPDATE sensor_ref(00) every 6.4sec to new average
if(frameNo==2) { //grab_ref() includes calculation of new Cratios & brightness
grab_ref(0,Sensor_ref,S666_pitch,0L,&Sensor_ref[00],&Sen_Brightness_Ref[00],&SensorRefRatio[00],&Sen_Brightness_Ref4[00]);
Sen_Brightness_Ref[00]=r00Av; //Sum of (grab_ref)rounded averages is < Average of sum, use r00Av instead.
/***/ IFN {Spr("adjusted _Brightness_ref[00] to Average "); Serial.println(Sen_Brightness_Ref[00]);}
}
int sumR=0; int sumG=0; int sumB=0; //calculate new bright(00) ( <= 48*63 (3024max))
for (i=0;i<48;i+=3){ //calculate colour averages (NB. not colour ratios!)
sumR=sumR+Sensor666[i]; sumG=sumG+Sensor666[i+1]; sumB=sumB+Sensor666[i+2]; //calculate rolling averages
Sensor666Av[i] += Sensor666[i]; //calculate individual pixel colour averages for _ref
Sensor666Av[i+1]+= Sensor666[i+1];
Sensor666Av[i+2]+= Sensor666[i+2];
}
//calculate whole sensor rolling averages (sumR already contains sum of R in current frame)
rollAvR=rollAvR+sumR-rollAvFrameR[frameNo]; rollAvFrameR[frameNo]=sumR;//r00AvR=rollAvR>>6;//av.of Sensor[00]
rollAvG=rollAvG+sumG-rollAvFrameG[frameNo]; rollAvFrameG[frameNo]=sumG; //r00AvG=rollAvG>>6;
rollAvB=rollAvB+sumB-rollAvFrameB[frameNo]; rollAvFrameB[frameNo]=sumB; //r00AvB=rollAvB>>6;
r00Av = (rollAvR+rollAvG+rollAvB)>>6; //div by 64 frames to get 0-3024 (48*0x3F) av. s00 frame sum (bright)
sumR=0; sumG=0; sumB=0;
for (i=0;i<48;i+=3){ //calculate av noise
//calculate sum of noise squared for CURRENT 4x4 frameNo. aiming for RMS equivalent
Noise = Sensor666[i] - Sensor_ref[i]; sumR=sumR+Noise*Noise;
Noise = Sensor666[i+1] - Sensor_ref[i+1]; sumG=sumG+Noise*Noise;
Noise = Sensor666[i+2] - Sensor_ref[i+2]; sumB=sumB+Noise*Noise;
} //sumR is now sum from 16 pixels in one frame
rollNsR=rollNsR+sumR-rollNsFrameR[frameNo]; rollNsFrameR[frameNo]=sumR; r00NsR=rollNsR>>6; //calculate rolling average noise on Sensor[00]
rollNsG=rollNsG+sumG-rollNsFrameG[frameNo]; rollNsFrameG[frameNo]=sumG; r00NsG=rollNsG>>6; //divide by 64.
rollNsB=rollNsB+sumB-rollNsFrameB[frameNo]; rollNsFrameB[frameNo]=sumB; r00NsB=rollNsB>>6; //16 for av/pixel
IFN printf("s00 roll'n brAv=%d, noisAv=%d %d %d s",r00Av,r00NsR,r00NsG,r00NsB); //SHOULD THESE BE /16 as 16 pixels in every r00Ns%
// ****FOR FLUORESCENT LIGHTING TRY TO SYNCHRONISE release/start image WITH 50Hz MAINS USING INTERNAL CLOCK
// ****DELAY TO REDUCE FRAMES PER SECOND FROM one/80mS to one/100mSec FOR PSRAM 25% IDLE (DE-STRESS) TIME.
while ((micros()-starttime)>CYCLETIME) {starttime += CYCLETIME;} //bring it back into sync if loop is late
while ((micros()-starttime)<CYCLETIME) {;} //kill any remaining spare time before 50/60Hz sync.
starttime += CYCLETIME;
// above should sync with mains just before releasing fb to be refilled.
// this is based on the premise that the mains frequency is very accurate, stable & doesn't drift noticeably!
// also assumes ov2640 is restrained to run at 10Hz (This may not be true - suspect free runs at ~13Hz!)
/***/ IFT {Spr(starttime);Spr("uSec ");}
/***/ IFT timer=micros();
esp_camera_fb_return(fb); //release jpg camera image frame buffer - starts new frame capture?
//sensor images now in 3 byte format in array Sensor666[]
/***/ IFT{ Spr("50Hz fb return(uS) "); Serial.println(timer);}
// ****IF CALLED FOR BY c####, OR STARTUP(), DO A FULL REFERENCE UPDATE FOR ALL ACTIVE SENSORS
//if cFlag not 0 then all defined sensors should be prefilled from good stable unoccupied image, i.e.AFTER
// CAM has run for > prefillRef frames.
if (cFlag!=0) prefillRef = S666init_SensorRefs(prefillRef); //Sensor_refs[],related brightness,cRatios etc.
/***/ //Print fb data sample //NOTE this is AFTER release of fb so use saved decoded Sensor666[] images!!!
/***/ IFD write_img_sample(Sensor666,S666_row,0x08*48,24);//write current image of sensor[00] AND Sensor[bsn(1/0)]
// ****NOW WANT TO CLEAR CAMERA PIPELINE & START NEW IMAGE CAPTURE FOR NEXT LOOP
/***/IFT timer=micros();
// WITH FAST rgb565 decode above, it isn't worthwhile trying to clear out pipeline AS WAS NEEDED WITH jpg TO bmp
// ****DO COMPARE OF ALL SENSORS WITH THEIR REF'S, DECIDE IF OCCUPIED & IF SO SET STATUS.
/***/IFT timer=micros();
//at start of program preload sensor_ref[] automatically after (prefillRef) images
// LOOP UNTIL prefillRef frames acquired at which time S666init_SensorRefs(prefillRef) will load ref. images.
if(prefillRef>0){ //at start of program there is time to kill before ref.images are auto grabbed
/***/ if (prefillRef>1) { //during initial countdown, output some debug images to see changes (if obvious)
/***/ IF0 for (i=0;i<3;i++){ printf("Sensor_ref[]:"); //print out first 3 SensorRefs
/***/ for (int j=0;j<48;j++) printf(" %x",Sensor_ref[i*48+j]); printf("\n");
/***/ }
/***/ IF0 printf("Quad: %u %u %u %u quad[4]:%u\n",quad[0],quad[1],quad[2],quad[3],quad[4]);
/***/ }
/***/ IFD for (int i=0;i<3;i++){ //print Cratios for first 3 sensors
/***/ printf("\nbsNo %d SensorRatios: ",i); for (int j=0;j<4;j++)printf(" %3d %3d %3d :",\
/***/ SensorRefRatio[i*12+j*3],SensorRefRatio[i*12+j*3+1],SensorRefRatio[i*12+j*3+2] );
/***/ } printf("\n");
}else{ // DO NORMAL LOOP COMPARISON
i2cDatai=1; //index for i2c message data entry - leave [0] for 't'
avOddFrame=!avOddFrame; //flip flag for av2frames() execution
for(bsn=0;bsn<80;bsn++){ if(bsn==1)timer=micros(); //leave bsn==0 out of timing to see effect.
if (SensorActive[bsn]){ //skip compare if Sensor NOT active! (inactive sensors can't
// auto update/track brightness drift (& saves time))
// ****USE 2 image average for compare if bsNo < TWOIMAGE_MAXBS as a test for improved algorithm
if (bsn < TWOIMAGE_MAXBS) av2frames(bsn); //do 2xframe av. if active & bsNo < 2/0 & compare current S666 image
maxDiff= compare( Sensor666, S666_row, long(bsn*S666_pitch), &SensorRefRatio[bsn*12], bsn, RGB565p);
processDiff(bsn); //make a decision on whether to set occupied or not and set status[bsn] accordingly
//processDiff recomputes bright & sets/resets all sensor status flags
if (bsn==dbsNo) {dMaxDiff=maxDiff; dBright=bright*brightSF;} //save for delivery on dflag or i2c request
}
if(bsn==0){ //special treatment for brightness reference sensor[00]
/***/ IFD {Spr("bsNo 0/0 maxDiff: ");Serial.println(maxDiff);}
refBrightness=Sen_Brightness_Ref[0]; //a refBrightness from last time ALL references updated (startup, 'r00' or 'c' only)
refActual=quad[4]; //save refActual in case use later
/***/ IFT { Spr(refBrightness);Spr(" refBrightness > actual ");Serial.println(refActual);} //sensor[00]
else IFsc if(minSensors==0){ Spr(" T");Spr(threshold);Spr(" N");Spr(nLED);Spr(" R");
Spr(refBrightness);Spr(" A");Spr(refActual);
Spr(" M");Spr(min2flip);Spr(" B");Spr(brightSF);Spr("\t"); //short & no \n
}
// bsn=minSensors; //then skips active/enabled sensors up to minSensors!
} //end if(bsn==0)
} //end for(bsn=0->80)
i2cData[1]=byte(threshold); //replace first i2c byte (bsn=0?) with threshold (don't want NULL bsn at start!
i2cData[i2cDatai]=0x50; //put block "80." to flag end of i2cdata[] (max valid bsn=79.)
i2cData[i2cDatai+1]=0x00; //put a NULL on end of data message to end transmission
timer=micros()-timer;
/***/ IFT {Spr("Compare(uS): "); Spr(timer,DEC);}
if(bsnUpdated>=0) { IFsc{ Spr("Ref 0");Spr(bsnUpdated,OCT);} bsnUpdated=-1;}
IFsc Serial.println(""); //was consistently getting same No. from (timer,DEC) & (zStr) (until neg nos)
// ****CHECK DFLAG AND OUTPUT REQUESTED INFO. (BUG - doesn't look like it prints requested dbsNo DIFFERENCES only LAST active sensor leftover?)
//if dFlag==0 just output diffs. if dFlag >0 output image table and repeat dFlag times
if(dFlag>=0) {
printf("Diff: bsNo %d/%d diff %d bright %d diff+bright %d\n",dbsNo>>3,dbsNo&7,dMaxDiff,dBright,dMaxDiff+dBright);
// also need to send something to i2c - do this in i2cRequest() if last i2cReceive was 'd'
dFlag-=1; //decrement towards -1
if(dFlag>=0){ //d%%n sets dFlag so can output debug sample images for bsNo '%%' on each of 'n' loops
write_img_sample(Sensor666,S666_row,long(dbsNo*48),24); //write for visual check what is being compared.
wait(); //give user chance to read
}
} //end if(dFlag) code
} //end (normal loop comparisons) (prefillRef==0)
// ****FLASH A LED ON EACH LOOP. Use FLASHLED pin
if (FLASHLED==4) flashLed(20); //flash White led on IO4 briefly (~25uSec for ESP32-CAM)
else { digitalWrite(FLASHLED, HIGH); delay(5); digitalWrite(FLASHLED, LOW); } //wastes 5% of loop time!*****
setLED(nLED,true); // set the PLED and QLED block status indicators also
// ****CHECK FOR USB COMMAND INPUT - PROCESS ANY COMMAND
/***/ IF7 if (SensorBlockStat[h7block] != 0){Spr("debug-trip in Block:");Serial.println(h7block);wait();}
//wait if Block # occupied
if(aRefCtr>0){ aRefCtr--; //do we need to do a ref for new sensor yet?
if(aRefCtr==1){
nParam=2;
p[0]='r'; //byte('r');
p[1]= absn;
processCmd(imagePtr,pitch,Sensor,nParam,p); //process 'r' command
cmdCount=0; //reset command string index
}
}
if(newi2cCmd==true){
/***/ IFI2C {Serial.print("I2C:");Spr(char(i2cCmd[0]));}
for (i=0;i<64;i++)cmdString[i] = i2cCmd[i]; //copy i2cCmd to cmdString
/***/ IFT {Spr("****************i2cCmd: ");Spr(i2cCmd);}
if(cmdString[0]=='w') newi2cCmd=false; //this ensures 'w' actually waits - other i2c commands DON'T WAIT
nParam=parz(cmdString,p); // parse ascii cmd
/***/ // printf(("parz - %d parameters; p[0-4]: %c %d %d %d %d bsN(p[1]): 0%o \n"),nParam, p[0],p[1],p[2],p[3],p[4],bsN(p[1]) );
processCmd(imagePtr,pitch,Sensor,nParam,p); //process command line
if(cmdString[0]!='w') newi2cCmd=false; //if() lets newi2cCmd cancel 'w' without being discarded itself.
}
else while (Serial.available() > 0) { //each command is followed by a fresh loop to "get_image"
cmdChar=Serial.read();
cmdString[cmdCount]=cmdChar; //add it to cmd string
if(cmdChar != '\n') Spr(cmdChar); //immediate echo back to monitor
cmdCount+=1;
cmdCount=constrain(cmdCount,0,20); //increment & limit string length
if (cmdChar == '\n'){ //don't process cmdString until get LF (\n)
Serial.println(" ******"); // return a new line to monitor
nParam=parz(cmdString,p);
// parse ascii cmd
/****/ //IF5 printf(("parz: %d parameters; p[0-4]: %c %d %d %d %d bsN(p[1]): 0%o \n"),nParam,p[0],p[1],p[2],p[3],p[4],bsN(p[1]));
processCmd(imagePtr,pitch,Sensor,nParam,p); //process command line
cmdCount=0; //reset command string index
}
}
/***///output reference images sensor_Ref[00] & sensor_Ref[02] for diagnostics purposes captured Sensor666 array
/***/ IF0 disp_sample_pixels( Sensor666, S666_row, 4 * (01) , 1*16, 24); //dispX/16 will move multiples of bsNo
/***/ IF0 delay(1000); //delay a second for serial.print debug to catch up?
} //end of main loop
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// FUNCTION TO EXECUTE A REFRESH OF SENSORREF[00] r00
void DOr00() {
for (int i=0;i<80;i++){
if (Sensor[i]>0) //DEFINED so grab_ref(); Gets an image from Sensor666[] and computes new cRatios and brightness (*NO AVERAGING!*)
grab_ref(bsn,Sensor666,S666_pitch,long(i*S666_pitch),&Sensor_ref[i*S666_pitch],&Sen_Brightness_Ref[i],&SensorRefRatio[i*12],&Sen_Brightness_Ref4[i]); //includes new Cratios & brightness
averageRbsn=0; //set a flag for main loop to compute a multi-second average Reference and update _ref[0-79]
averageRcounter=AVCOUNT; //initiate down counter. (10 per second)
} //end of r00
printf("Ref: r00 refreshes ALL defined sensor references with new Cratios & brightness\n");
refBrightness=0; //recalculate a refBrightness from Sensor_ref - perhaps should ONLY do when r00 is executed not whenever ref(00) updated
for(int i=0;i<S666_pitch;i++) refBrightness+=int(Sensor_ref[i]); //sum S666_pitch pixlets for 6x6 Sensor[0] max 6804r02
} // *****************************
// PROCESS A COMMAND STRING FROM USER
void processCmd(byte *imagePtr,int pitch,unsigned long *Sensor,int nParam,int16_t *p){ //process parz command in p[]
//imagePtr: is a pointer to the location of image Data NOTE no longer used by processCmd !
//pitch: is the number of bytes in a full line of fb imagePtr
//Sensor: is a pointer to the Sensor array of pointers into the image Data
//nParam: the number of parameters in p[]
//p: is pointer to array of command parameters
int b=0; //local (bank) variable
int c=0;
int i, j; //j: sensor index
int param2; // second cmd argument
int bsn=0;
if(nParam > 0)
switch(p[0]) {
case 'a':{ //a%%; enAble Sensor[bsn] and get fresh reference (only do if UNOCCUPIED!)
absn = p[1]; //save decimal straight from parz() for later use
bsn = bsN(p[1]); // (can't produce out of range bsn - defaults to 00)
if(nParam==1) { printf("(a%%%%[,rrr,xxx]) enAble Sensor(%%%%)[& sets new coordinates for it]\n"); break; }
if(nParam==4) {
int rowVal=p[2];
if(rowVal<0 || rowVal>235) {printf("invalid rowValue\n"); break;}
int xVal=p[3];
if(xVal<0 || xVal>319) {printf("invalid xValue\n"); break;}
pvtThreshold[bsn]=255; //new coordinates so cancel pvtThreshold. ('k' doesn't clear it)
Sensor[bsn]=pitch*rowVal + 2*xVal;
printf("(a%%%%[,rrr,xxx]) new coordinates. Sensor[%d/%d] to r:%d x:%d\n",bsn>>3,bsn&7,rowVal,xVal);
aRefCtr=4; //get # fresh frames then take ref for sensor[aRef]
wait();
}
if (bsn>=0) { printf("(a%%%%) enAble: sensor %d/%d with fresh reference - bsNo 0%o\n",bsn>>3,bsn&7,bsn);
SensorActive[bsn]=true; //set active and grab new ref
SensorActiveBlk[bsn>>3] |= mask[bsn&0x07];
grab_ref(bsn,Sensor666,S666_pitch,long(bsn*S666_pitch),&Sensor_ref[bsn*S666_pitch],
&Sen_Brightness_Ref[bsn],&SensorRefRatio[bsn*12],&Sen_Brightness_Ref4[bsn]);
//includes new Cratios & brightness
//NOTE WELL: without new frames, any new reference won't be much use with new coordinates!
/***/ printf("updated Sensor_ref[] \n"); //HEX bytes(0-5)(BGRBGR): %X %X %X %X %X %X \n",
} break;
}
case 'b':{ //b$; Block status byte output to host on USB (& i2c) //b$# use # to change brightSF from 1 to 4
if (nParam<2){
Serial.print("(b$#) Bank/BrightSF: parameters not found - brightSF(#): "); Serial.println(brightSF);
}
else {
if(nParam==3){ brightSF=p[2];
Spr("(b$#) changing brightSF sensitivity to "); Serial.println(brightSF);
}
b=p[1]%10; //ignore any further text after b$
if(!newi2cCmd){
Spr("(b$#)bank: ");Spr(b);Spr(" occupancy status byte: 0x");
Spr(SensorBlockStat[b],HEX);Spr(" (sensors 7-0) ");
for (i=7;i>=0;i--) Spr((SensorBlockStat[b]>>i) & 0x01); Serial.println(' ');
}
else IFI2C {for (i=7;i>=0;i--) Spr((SensorBlockStat[b]>>i) & 0x01); Serial.println(' '); }
}
break;
} //c$$$$#####; set $=1 to leave AWB AEC AGC & CB respectively.
case 'c':{ //c$$$$; Calibrate: turn on/off Auto adjustments for 10seconds and reload all references!
//6x % parameters are 1/0 for AWBg AEC AECd AEL AGC AGg respectively ( default to off(0) )
//full list: arguments for 'c' command; Bri Con Sat AWB AWBg AEC AECd AEL AGC AGg (NO SPACES!)
printf("(c$$$####$##) dangerous cam cmd! - USE CMD 'r00' after 15ses or just use 'j' ( $= _|-|0|1|2 )\n");
Bri=0; Con=1; Sat=2; AWB=1; AWBg=1; AEC=0; AECd=0; AEL=1; AGC=1; AGg=9; //reset defaults
if(cmdString[1]<0x20){
// printf("CALIBRATE CAM - syntax: c AWB AEC AGC CBar Bri Con Sat AGCgain AWBg e.g. reset defaults: c01200129\n");
printf("Calibrate CAM syntax: c Bri Con Sat AWB AWBg AEC AECd AEL AGC AGgain defaults: c0121100119\n");
wait();
break;
}
autoRefSuspended = true; //Suspend auto refRefresh() during 'c'
cStr=" ";
if (isDigit(cmdString[1])) Bri=cmdString[1]-0x30; //options: use 0,1,2,'-' for -1 and '_' for -2
else {if (cmdString[1]=='-') Bri=-1; if (cmdString[1]=='_') Bri=-2;}
Spr(" Bri=");Spr(Bri); cStr=cStr+cmdString[1];
if (isDigit(cmdString[2])) Con=cmdString[2]-0x30;
else {if (cmdString[2]=='-') Con=-1; if (cmdString[2]=='_') Con=-2;}
Spr(" Con=");Spr(Con); cStr=cStr+cmdString[2];
if (isDigit(cmdString[3])) Sat=cmdString[3]-0x30;
else {if (cmdString[3]=='-') Sat=-1; if (cmdString[3]=='_') Sat=-2;}
Spr(" Sat=");Spr(Sat); cStr=cStr+cmdString[3];
if (cmdString[4]=='1') {AWB=1; Spr(" AWB "); cStr=cStr+" AWB ";} else AWB=0;
if (cmdString[5]!='\n') {
if(cmdString[11]!='\n'){Spr(" missing arguments\n"); break;}
if (isDigit(cmdString[5])){AWBg=cmdString[5]-0x30; Spr(" AWBg=");Spr(AWBg); cStr=cStr+cmdString[5];}
if (cmdString[6]=='1') {AEC =1; Spr(" AEC "); cStr+=" AEC ";}else {AEC=0; cStr+=" ";}
if (cmdString[7]=='1') {AECd=1; Spr(" AECd ");cStr+=" AECd ";}else AECd=0;
if (isDigit(cmdString[8])) AEL=cmdString[8]-0x30; //options: use 0,1,2,'-' for -1 and '_' for -2
else {if (cmdString[8]=='-') AEL=-1; if (cmdString[8]>'_') AEL=-2;}
Spr(" AEL=");Spr(AEL); cStr=cStr+' '+cmdString[8];
if (cmdString[9]=='1') {AGC =1; Spr(" AGC "); cStr+=" AGC ";}else {AGC=0; cStr+=" ";}
if (isDigit(cmdString[10])) {AGg=cmdString[10]-0x30;Spr(" AGCgain=");Spr(AGg); cStr=cStr+cmdString[10];}
}
Serial.println(" ");
new_camera_settings(Bri,Con,Sat,AWB,AWBg,AEC,AECd,AEL,AGC,AGg); //set AWB,AEC,AGC select Auto ON initially
// loop will do following call after 10 seconds and delay a further 10 before doing ref updates
// new_camera_settings(Bri,Con,Sat,AWB,AWBg,AEC,AECd,AEL,AGC,AGg); //set AWB,AEC,AGC,ColourBar select AWB OFF
Spr("Calibrate: run 10 seconds for auto calibrate before turning selected 'Auto' options off ");Serial.println(cStr);
prefillRef=1; //set to 1 should suppress normal loop data stream until end of stdCtimer
getCAMstatus(); //also print in 'g' format
Serial.println(" NOTE Calibration operation 'c' has NOT FULLY FOLLOWED THROUGH - assumes SensorStat[] & SensorBlockStat[] will automatically reset");
printf("After a new line, calibrate will take ~20 seconds to execute and refresh references\n");
wait();
stdCtimer=millis()+10000; //start a timer - wait 10sec for adjustments to settle automatically then turn off AGC say
break; //leaves flags set for main loop() to calibrate from fresh images.
}
case 'd':{ //d%%,#; Differences between Sensor_ref[bsNo] and imagePtr. (optional n=No. of repetitions of image) (& i2c)
if(nParam==1) {
printf("(d%%%%,#) Diff score for S%%%%. Optional repeat # times including frame samples \n ");
wait();
break;
}
dFlag=0; //set a (+ve) flag for loop() to print out data after it gets a fresh image repeating if desired.
if(nParam==3) dFlag=p[2];
Spr("(d%%#) Diff: ");
dbsNo = bsN(p[1]);
if (dbsNo>=0) {
printf(" difference score for Sensor[0%o], use %d consecutive images (remember pipelining!)\n",dbsNo,dFlag);
printf("will print data AFTER new line - follow with 'w' command to pause to read\n");
} else dFlag=-1;
wait();
break; //leaves flag for loop() to complete print output.
}
case 'e':{ //e; EPROM burn any changes to Sensor[] & some other parameters
printf("(e) EPROM: Save latest threshold(%d), min2flip(%d), nLED(%d), minSensors(0%o), maxSensors(0%o), Sensor[] SensorTwin[] & pvtThreshold[] to EPROM\n"\
,threshold,min2flip,nLED,minSensors,maxSensors);
EEPROM.write(EPminSensors,byte(minSensors));
EEPROM.write(EPnLED,byte(nLED));
EEPROM.write(EPthreshold,byte(threshold));
EEPROM.write(EPmin2flip,byte(min2flip));
EEPROM.write(EPmaxSensors,byte(maxSensors));
printf("EPROM: Saving latest sensor pointers to EPROM with thresholds, nLED, min2flip, min & maxSensors\n");
for (i=0;i<80;i++) { //put changed values for SensorTwin[] & Sensor[] in EEPROM
EEPROM.writeLong(i*4,Sensor[i]);
EEPROM.write(EPSensorTwin+i,SensorTwin[i]);
EEPROM.write(EPpvtThreshold+i,pvtThreshold[i]);
EEPROM.write(EPlineardX+i,lineardX[i]);
delay(10); //added to avoid 'e' resets of Alive082 hw.
}
EEPROM.commit(); //ESP32 method