Brightness Controller

Hi!

I am new to the Arduino forum community. I am trying to build a brightness controller using the following code

/* Light Sensor v4
   P Controller
   Expected value = 11:50 AM, Ceiling lights on, LED OFF
   Major Disturbance: Complete darkness, LED OFF = 512
                      Complete darkness, LED ON  = -200
   Proportional Band +/- 60% of SP
*/

// To create the running average values
const int numRead = 50;
int readings[numRead];
int ReadIndex = 0;
int total = 0;
int LS_Average = 0;

//Establish the pins and SetPoint
int Ambient = 50; //This is the SetPoint
int Lowerbound = Ambient - (0.4 * Ambient);
int Upperbound = Ambient + (0.4 * Ambient);
int R = A0;
int L = A1;
int LED = 3;

//Create the proportional control
double brightness = 50;
int brightness_up = 0;
int brightness_down = 0;

void setup() {
  Serial.begin(9600);
  pinMode(LED, OUTPUT);
  pinMode(R, INPUT);
  pinMode(L, INPUT);
  for (int CurrentRead = 0; CurrentRead < numRead; CurrentRead++) {
    readings[CurrentRead] = 0;
  }
}

void loop() {
  int R_Output = analogRead(R);
  int L_Output = analogRead(L);
  int LS_Output = R_Output - L_Output;

  total = total - readings[ReadIndex];
  readings[ReadIndex] = LS_Output;
  total = total + readings[ReadIndex];
  ReadIndex = ReadIndex + 1;
  if (ReadIndex >= numRead) {
    ReadIndex = 0;
  }
  LS_Average = total / numRead;
  Serial.print(Ambient);
  Serial.print(" ");
  Serial.print(Lowerbound);
  Serial.print(" ");
  Serial.print(Upperbound);
  Serial.print(" ");
  Serial.print(LS_Average);
  Serial.println(" ");

  if (LS_Average > Upperbound) {
    brightness = 255;
    analogWrite(LED, brightness);
  }

  if (LS_Average < Lowerbound) {
    brightness = 0;
    analogWrite(LED, brightness);
  }
    if ((Ambient < LS_Average) && (LS_Average < Upperbound)) {
      // Initial condition LED is NOT FULL ON
        brightness = brightness + (0.235) * (LS_Average - Ambient); //Will bring LS_Average down while brightness increases (overall brightness > 0
      // Initial condition LED is FULL ON
      if (brightness >= 255) {
        brightness = 255;
        brightness = brightness - (0.235) * (LS_Average - Ambient); //Will bring LS_Average down while brightness decreases (overall brightness >0
      }
    }

    if ((Lowerbound < LS_Average) && (LS_Average < Ambient)) {
      // Initial condition LED is NOT FULL OFF
      if (brightness > 0){
        brightness = brightness + (0.235) * (LS_Average - Ambient); //Will bring LS_Average up while brightness is decreasing (overall brightness >0
      }
      // Initial condition LED is FULL OFF -> This is troublesome since initial condition brightness is off
      brightness = brightness - (0.235) * (LS_Average - Ambient); //Will bring LS_Average up while brightness is increasing (overall brightness >0
    }
  delay(100);
}

The problem that I have is it keeps giving me a continuously oscillating LS_Average Output. I believe the correct output should be oscillating, but with diminishing amplitude.

Would you mind helping me figure out why? My guess is I must have made some mistake in the if statements, but I couldn’t figure out where exactly.

In the graph attached, the high amplitude square graph is the brightness, the three lines are the lowerbound, ambient, and upperbound, and the LS_Average is the one changing shapes.

I also attached the setup of my arduino and the txt file of the output

Thank you for your help!

Output Data.txt (16.9 KB)

How did you generate output data.txt? I see no print out of the brighness setting in your code.

Is this output from every pass of the loop?

If so, don't understand how LS_average is changing by 10 every pass when the difference between two analog reads are added to a moving average of 50. What are the raw analog read values from the two sensors?

Are you sure of the sensor values? If you are really responding to correct inputs, your brightness settings are totally mismatched to the response of the system.

Is there a known brightness setting at which the system is stable at the setpoint?

I didn't see any algorithmic errors on first look.

One possible issue: You are totaling 50 values that might go from -1023 to +1023 each. That can produce a value that won't fit in a signed integer. You should make 'total' a long integer.

@cattledog

Thank you for your reply!

How did you generate output data.txt? I see no print out of the brighness setting in your code.

  1. the Output data.txt contains 5 values: Ambient, Lowerbound, Upperbound, LS_Average, Brightness
    However, there is a problem with the Brightness printout since there are 4 cases, and I don't know how to print the brightness which are currently applied by the system

Is this output from every pass of the loop?
2. Yes, this is the output for every pass of the loop (every 100ms)

If so, don't understand how LS_average is changing by 10 every pass when the difference between two analog reads are added to a moving average of 50. What are the raw analog read values from the two sensors?
3. The reason why I use the 50 moving average is to simulate the inertia of the system. (I think of it as a water heater - when the heater starts, the water does not warm up immediately but need some adjustment time to reach equal temperature with the heater)

Are you sure of the sensor values? If you are really responding to correct inputs, your brightness settings are totally mismatched to the response of the system.
4. Yeah, that's what I don't understand. I believe it should be inverse (ie. when the system darkens, the brightness should increase since I want to maintain ambient brightness)

@Johnwasser

Thank you for your reply.

I tried your suggestion, but it did not change the behavior of the system. The main problem that I have (you can see this from output data.txt) is the system does not respond correctly to the input

this is an example of what I meant:

50 30 70 17 0.00
50 30 70 16 0.00
50 30 70 16 0.00
50 30 70 26 0.00
The above LS_Average values are below 30, so the applied brightness is zero (this is correct)

50 30 70 36 3.29
50 30 70 45 3.29
The above LS_Average values are between 30 and 50, so the applied brightness is increased gradually (I don't know why the second one does not increase here)

50 30 70 55 4.47
50 30 70 65 7.99
The above LS_Average values are between 50 and 70, so the applied brightness is supposed to be decreased -> BUT somehow it kept increasing -> This is wrong

50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00
50 30 70 75 255.00

The LS_Average values above is above 70, then brightness is fully on (this is correct)

Thank you so much for your help!

richard889:
The above LS_Average values are below 30, so the applied brightness is zero (this is correct)

The above LS_Average values are between 30 and 50, so the applied brightness is increased gradually (I don’t know why the second one does not increase here)

The above LS_Average values are between 50 and 70, so the applied brightness is supposed to be decreased → BUT somehow it kept increasing → This is wrong

The LS_Average values above is above 70, then brightness is fully on (this is correct)

I’m confused by the transfer function here. When the input value is below a lower limit the the output value is zero and when the input value is above an upper limit the output value is maximum but between the limits the low values cause an increase in output and the high values cause a decrease in output???
I would expect either:
low input → low output, high input → high output
or
low input → high output, high input → low output
I think part of the problem is in your calculations. In the case where (Ambient < LS_Average) you add a portion of (LS_Average - Ambient) to brightness. Since (LS_Average - Ambient) is a positive number this will increase brightness. In the case where (LS_Average < Ambient) you SUBTRACT a portion of (LS_Average - Ambient) from brightness. (LS_Average - Ambient) is a NEGATIVE number. Since you are subtracting a negative number this will ALSO increase brightness.

There is possibly a problem with the behaviour of analogWrite(). The function expects a value of 0-255. If you input a negative number or a number >255 it will wrap and the output will not be as expected. Your code addresses the case of brightness>255, it should also test for brightness<0.

I would a try a different transfer function, where possible brightness is moved up or down only by 1, and you might be able to better monitor the response of your system to see what is going on.

I’m still uneasy with the analogRead() values, and how the LS_Average can change so much with only one additional reading out of 50.