The answer from Curt Sampson provides several good design considerations, plus the quite-reasonable suggestion that you run some experiments to characterize your LDRs and their placements.
Note that if you want to know which sensor has the highest reading, a simple find-maximum-value loop will do that, without any sorting:
int maxR = reading[1], maxS=1;
for (byte i=2; i <= nSensors; ++i) {
if (reading[i] > maxR) {
maxR = reading[i];
maxS = i;
}
}
// Now we know sensor maxS has max reading, maxR
The code above follows the 1–6 numbering scheme given in the question, and assumes that reading[i]
stands for the reading from sensor i
.
Code below, however, numbers the sensors and their readings from 0 to 5 instead of from 1 to 6, to allow simpler conditions in for
loops. From a programming standpoint, it makes sense to call the first sensor 0 if its data is stored in cell 0.
Finding x, y direction of the light
Assumptions behind the following code are that higher readings indicate more light, and that if necessary some background-lighting calibration has been done, and that background lighting has already been subtracted from readings data. We also assume that maximum readings are less than (2¹⁵)/N, where N is number of sensors.
The point of the following code is to set two variables, x
and y
, that represent the relative location of a light source. For example, if x
and y
are both positive, the light is above and to the right of the top right LDR. If x
is negative and y
is near zero, the light is to the left of the middle-left LDR. If knowing the signs of x
and y
is adequate for your purpose, then there's no need to do more arithmetic than shown. On the other hand, if you want to compute some sort of angle, you can use atan2(y, x)
to compute the direction of the light, in radians.
int reading[nSensors];
const int xdir[] = { -1, 1, -1, 1, -1, 1}; // x-direction sensor weights
const int ydir[] = { 1, 1, 0, 0, -1, -1}; // y-direction sensor weights
int x=0, y=0;
for (byte i=0; i < nSensors; ++i) {
x += xdir[i] * reading[i];
y += ydir[i] * reading[i];
}
Edit 1: Tracking the time a light is on
Issues with light-timing include what levels to start or stop timing at; necessary resolution (milliseconds? minutes?); necessary range (eg, is what happened a minute ago relevant); and when to reset accumulated time data. It may be simplest to track the on times of all the sensors at the same time, then select one of the times or combine some times. Do you want the largest time, the newest time, the newest large time, or the time corresponding to the largest light?
For example, the code below tracks light levels that remain high long enough to register, in smoothed averages, as above some threshold. At intervals, current indications are rotated into bit maps that track the last 16 on-off levels for each LDR, and counts of number-of-ons are updated too.
// Initialization ...
uint16_t expAvg[nSensors]={0}; // Exponential averages over time
uint16_t bitMaps[nSensors]={0}; // Track each sensor's last 16 on-off levels
byte onCounts[nSensors]={0}; // Number of recent times sensor avg > threshold
unsigned long prevMilli=millis();
const int markInterval=50; // ms interval for recording light levels
const int onThreshold=547*16; // Some light-is-on threshold
// In each pass of loop() ...
for (byte i=0; i < nSensors; ++i)
expAvg[i] = reading[i] + 15*expAvg[i]; // exponential averaging with 1:16 weight
// In loop(), at intervals ...
if (millis()-prevMilli >= markInterval) {
prevMilli = millis();
for (byte i=0; i < nSensors; ++i) {
onCounts[i] += (expAvg[i] > onThreshold) - (!(bitMaps[i] & 0x8000));
bitMaps[i] = (bitMaps[i]<<1) | (expAvg[i] > onThreshold);
}
}