# Detecting the light source location using multiple photoresistors

I am not an expert yet, so please forgive any possibly wrong terminology that I may have used.

I am planning to place 6 light sensors in 1 meter (or maybe more) distance from one another, like this (sorry about the bad schematic, I am not an expert, but I hope it makes sense):

simulate this circuit – Schematic created using CircuitLab

I am going to place them in a relatively dark room. My goal is, when I am pointing a flash light towards them to be able to tell which sensor is measuring the highest values (where the light is coming from), and for how long (in milliseconds).

I know how to do the connections and measure values. My questions are two:

1. Which is the most efficient way to detect which of the 4 sensors is recording the higher values? I am assuming I have to compare the values that they are recording. However, I think that if in each loop I sort the captured values, the process might become heavy. I guess I could minimise the comparisons, since for example if LDR1's value is higher than LDR2 and LDR3, I can assume that the light source is cannot be pointing towards LDR6 for example. However, I am thinking that maybe there could be an even more efficient way to detect that (not necessarily in software).

2. Since the room will not be completely dark, the analog pins will record some values even when I am not pointing the flash light towards them. Is there a way to filter that out? I have come across calibration (https://www.arduino.cc/en/tutorial/calibration), but I am not sure whether this is the optimal solution. Please take into account that the light source might not be very strong (but its light will definitely be brighter than the room light).

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) {
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) {
}
``````

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);
}
}
``````
• This will most likely do it! I really like the second solution. However, in order to calibrate it, is the method that I posted in my question likely to work? I need to measure for how long the flash light was on (in milliseconds), so I guess I will need either some calibration or a high pass filter. Apr 2, 2017 at 11:22
• @jackgu1988, see edit - although I guess I didn't address the ms on but instead fraction of on time in last .8 seconds (with constants shown) Apr 2, 2017 at 17:14
• This will probably do it! Thank you very much. For the moment I am waiting for a couple more sensors to be delivered. I'll report back when I test it. Apr 2, 2017 at 23:26

There's only one ADC (analog to digital converter) on the ATmega328P; the analog input pins are multiplexed so that you choose which particular pin you want to sample. (See Section 28 of the datasheet for details.)

Taking a sample takes time; how long depends on how you do it. The `analogRead()` function is documented to take about 100 microseconds. Reading your six sensors is thus going to take at least 600 microseconds.

The sample returned is a 10-bit integer and so easily fits into two bytes, or even one byte if you drop the low two bits (which is reasonable for some applications, and probably yours, because they're likely to be more sample error than data anyway). Whether you do that or not, however, you're left with a very tiny amount of data to sort from one set of reads: just six values.

The Arduino runs the ATmega328P at 16 MHz, and most instructions run in single clock, giving you performance approaching 16 instructions per microsecond.

We could work out, based on the type of sort you use, the number of comparisons and swaps you're likely to make in the average and worst cases, but there's no point to that on such a tiny data set because the total time your routine takes is almost certainly going to be dominated by other overhead. So I'm just going to say that the sort will be something on the order of a hundred clock cycles.

That means your sort would add around 6 or 7 microseconds to your 600 microsecond total read time you've got so far, or 1%; you don't have to worry about it at all.

You'll actually spend a lot more time doing other sorts of processing. But it's a pretty fast CPU, as we've seen above, so there's a good chance if you're careful that your loop will still be dominated by the sampling time of the ADC for each of the sensors.

My suggestion is to go ahead and write the thing but record actual timings in your loop for the sampling and the processing. After you've played around with it for a bit you'll have a better idea of the particular costs for each for your particular application and then you can worry about whether or not you need optimization. I suspect you won't.

Regarding the "filtering," that's going to be an integral part of your processing algorithm because the "room light" values can be vastly different from time to time depending on the current lighting in the room. I don't think you want to "filter" anything out, but instead compare the values of all of the photoresistors; at a first guess, similar values from all of them would mean that there's no flashlight and some giving you significantly higher values would indicate that there is one. But my second guess is that a) you'll want your sensors a lot further apart than 1 meter (perhaps in the corners of the room) and b) it's not going to be so simple; you're likely to see fairly different values from the sensors even in a quiescent room. This is where you get to play and experiment to find out how this really works!

It also occurs to me that putting the photoresistors several meters away from the ADC is going to involve a lot of wire, which adds resistance, capacitance and noise, all of which are going to interfere with your measurements. There's not a lot you can do about this; you can play a bit with measuring known-value resistors at the ends of the wires to see how bad the problem is, but if it's terrible you'd probably need to use a separate Arduino for each sensor and have them communicating back to a central place for analysis.

• Thank you! This information is very helpful. I was also thinking that maybe I can wire the resistors in a way that I can minimise the amount of checks. For example, having 3 instead of 6 analog inputs (each input representing 2 sensors) and depending on the returned values to be able to tell which sensor is receiving more light. Apr 2, 2017 at 10:39
• You could try to come up with clever ways to wire the photoresistors in serial and/or parallel to reduce the number of samples you need to take, but that's a pretty darn tricky thing to do. I would probably first start with adding more sensors and, if the loop is too slow due to the length of time it takes to sample all of them, figure out an algorithm to sample only some of the sensors and leave others alone based on what's been sampled recently. But first I'd get something really basic working.
– cjs
Apr 2, 2017 at 11:59