Self Regulating Loop (LED light and Photocell)

Hey everyone,

I want to make a self regulating loop where I have an photocell that measures light intensity and an LED that’s creating light and I want to keep the photocell in a certain range by changing the LED’s value.

Ie: if I move the photocell away from the LED, the LED should increase intensity.

I’m getting a bit stuck on the code and hope one of you could help me out. Here’s the code I have so far.

int ledPin = 3; 
int lightPin = A0;
int lightReading;
int ledValue;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  ledValue = 125;
}

void loop() {
//Reading from photocell
  lightReading = analogRead(lightPin); 
  if (lightReading >= 400)
  {
    ledValue = ledValue - 1;
    analogWrite(ledPin, ledValue);
  }
  else if (lightReading <= 350){
    ledValue = ledValue + 1;
    analogWrite(ledPin, ledValue); 
  }
  else{
    ledValue = ledValue;
    analogWrite(ledPin, ledValue);
  }
  delay(10);
  Serial.print(ledValue);
  Serial.print(",");
  Serial.println(lightReading);
}

The code properly measures the light intensity, however, I can see from the Serial Plotter that the LED continually increases to numbers way above 255, but I see that the LED is cycling from 0-255. Attached is a Serial Plotter example that shows this.

geraldfunk: I can see from the Serial Plotter that the LED continually increases to numbers way above 255, but I see that the LED is cycling from 0-255.

Sorry that makes no sense to me. LED goes way above 255 but also only goes from 0-255? Are you talking about variable 'ledValue' or something else? What actually happens?

Does the lightValue change as the analogWrite values change and if so in what way?

Steve

What do you mean by

the LED continually increases to numbers way above 255, but I see that the LED is cycling from 0-255

and why would that be weird since analogWrite takes as second parameter a value between 0 and 255 (so would only take the LSB of whatever you give as parameter)

the fact that it overshoots is that your LED is probably never bright enough to change anything but is capped at 255 but not the integer which will cycle through all possible values and rollover

Please correct your post above and add code tags around your code:

[code]

[color=blue]// your code is here[/color]

[/code]

.

It should look like this:

// your code is here

(Also press ctrl-T (PC) or cmd-T (Mac) in the IDE before copying to indent your code properly)

Try:

 lightReading = analogRead(lightPin) / 4;

slipstick:
Sorry that makes no sense to me. LED goes way above 255 but also only goes from 0-255? Are you talking about variable ‘ledValue’ or something else? What actually happens?

Does the lightValue change as the analogWrite values change and if so in what way?

Steve

Hey slipstick,

Sorry for the confusion, that sentence wasn’t typed out too well. I know that the ledValue can only be set from 0-255, but when I upload my program I can see from the Serial Plotter that the ledValue starts at 0 and linearly increases to 1000000. When I physically look at the LED on my breadboard I see that it is cycling from off (0) to it’s highest setting (255), then it goes back to 0 and increases to 255, then goes back to 0… and so on. So obviously something is messed up in my code where the ledValue is just continually increasing.

The lightValue is working correctly because I see it cycle from about 60-450 depending on how intense the actually LED light is.

Attached is an image of what the serial plot looks like.

aarg: Try:

 lightReading = analogRead(lightPin) / 4;

Hey arrg, I tried that out but it just made the lightReading a lower value. The problem seems to be on the LED's end, the photocell seems to behaving normally.

Did you read answer #2? Your value just cycle and rolls over as one byte only is taken for parameter (when you hit 256 the PWM value is 0, same at 512 etc...)

Full brightness is not enough for your detector so it keeps asking for more but you hit the max without testing for it

J-M-L:
Did you read answer #2?
Your value just cycle and rolls over as one byte only is taken for parameter (when you hit 256 the PWM value is 0, same at 512 etc…)

Full brightness is not enough for your detector so it keeps asking for more but you hit the max without testing for it

Yep, I read answer 2 and answered the question in the post. There was no coding recommendations in that answer, so it obviously didn’t help me much.

My detector is functioning normally. If I shine a bright light at it, the value increases. While the LED is cycling through its intensities the lightReading increases, then goes to its baseline value, then increases while the LED increases.

So the problem is with the coding of the LED intensity. Please look back at my first post as I’ve included a Serial Plotter graph of what’s happening.

There is no coding recommendation but there is an explanation of what you see. The behavior is coherent with your code.

if you want a coding recommendation: don't use a counter that can go beyond 255 as a PWM value and test if you reach 255. don't go further as not only your LED won't be brighter, but it will turn off as 255+1 = 0 when using a byte....

and read #6 again, I offered further comments you seem to ignore

I suggest that your main problem is that your thresholds are wrong. The lightReading is ALWAYS <= 350 whether the LED is off or full on so you ALWAYS keeps adding 1 to ledValue.

A secondary problem is that although you know ledValue should not be >255 you do nothing to stop it climbing to ridiculous levels. It’s not difficult to say if ledValue > 255 make it 255. You could have even printed an error message saying something like “LED full on but lightReading is still too low”.

Steve

geraldfunk: Hey arrg, I tried that out but it just made the lightReading a lower value. The problem seems to be on the LED's end, the photocell seems to behaving normally.

You can't "try" it. It's mandatory. The analog output full scale is 1/4 of the analog input full scale. I'm not saying you have to use this method, but you have to scale it. Try using the map() function.

aarg: You can't "try" it. It's mandatory. The analog output full scale is 1/4 of the analog input full scale. I'm not saying you have to use this method, but you have to scale it. Try using the map() function.

Nothing prevents OP to do the compare with the full 0 - 1023 scale if he so desires.

Perceived light intensity might not be that linear with PWM, so nothing guarantees that mapping the value would give you the desired value - hence the feedback loop OP tries to implement to auto-tune the brightness

What he needs to ensure though is that the PWM value is between 0 and 255.

(The issue is that the brightness when LED is full on is not enough to meet the expected brightness condition so this is doomed anyway...)

The reason I mentioned map() is that it could set pragmatic endpoints for the LDR values.

J-M-L: the fact that it overshoots is that your LED is probably never bright enough to change anything but is capped at 255 but not the integer which will cycle through all possible values and rollover

I revisited this concept and this is what worked. Thank you, and sorry I overlooked it at first.

I lowered the lightReading values in the IF and ELSE IF parameters and the LED stopped cycling through.

It would still go high to values above 255, so I added an extra IF loop that doesn't let the LED go over 255.

aarg: The reason I mentioned map() is that it could set pragmatic endpoints for the LDR values.

I see what you have in mind. I think that could make sense if he had a direct linear interpolation function to decide the brightness. Here End points do not really matter as long as they are set. 0 to 1023 is as good (actually gives you more resolution if the decision tree was richer) than 0 to 255 and it's faster as you save the affine mapping calculation.

Constrain() would be better than map() for the output of the regulation

J-M-L: I see what you have in mind. I think that could make sense if he had a direct linear interpolation function to decide the brightness. Here End points do not really matter as long as they are set. 0 to 1023 is as good (actually gives you more resolution if the decision tree was richer) than 0 to 255 and it's faster as you save the affine mapping calculation.

Constrain() would be better than map() for the output of the regulation

I ran into the constrain() function last night after I used your advice and fixed the problem. I was experimenting with constrain to replace my new IF loop that limited the LED value, but I couldn't get it to work quite right. I didn't know which section of the code it should go (ie: before the void setup, in the void setup, or in the void loop). Do you mind providing an example of how to properly set up constrain() ?

well you are not in excel or in some sort of declarative language where everything gets evaluated and maintained for you. constrain() will get called when you call it. So the best place where to put it is when you need to constrain the value, likely in your loop when you increment or decrement ledValue and before calling analogWrite()

...
  lightReading = analogRead(lightPin); 
  if (lightReading >= 400) {
    ledValue--; // decrement ledValue
  }
  else if (lightReading <= 350){
    ledValue++; // increment ledValue
  }
  ledValue = constrain(ledValue, 0, 255); // make sure we do not over/undershoot https://www.arduino.cc/reference/en/language/functions/math/constrain/
  analogWrite(ledPin, ledValue); 
...

J-M-L:
well you are not in excel or in some sort of declarative language where everything gets evaluated and maintained for you. constrain() will get called when you call it. So the best place where to put it is when you need to constrain the value, likely in your loop when you increment or decrement ledValue and before calling analogWrite()

...

lightReading = analogRead(lightPin);
  if (lightReading >= 400) {
    ledValue–; // decrement ledValue
  }
  else if (lightReading <= 350){
    ledValue++; // increment ledValue
  }
  ledValue = constrain(ledValue, 0, 255); // make sure we do not over/undershoot constrain() - Arduino Reference
  analogWrite(ledPin, ledValue);

I can’t wait to try this, that would replace about 6 lines of code with 1 line. Thanks for all your help J-M-L!