Translate

Tuesday, 8 April 2014

Room Management System – Part 4 of Switching some lights while on vacation


As promised, today we take care of the photocell readings. If you have experimented with the photocell before and you have done one of the basic Arduino tutorials reading the analogue pin connected to it you may have noticed that the reading are jumping quite a bit. In my case they are going up and down 50 units with a constant light source and that is not very helpful while we want to switch something on or off when the light reaches a specific level. Trying to get a clear switching point specially at dusk and dawn with variable cloud conditions make the lights switch on and off a few times until the reading doesn't go above ore below the switching point. Even programming a buffer didn't help much it just moved the “not knowing what to do effect” to the upper and lower limits of the buffer. Than I came across some very useful Analogue Smoothing Algorithm by Tom Igoe which I liked a lot and it nearly solved the problem but still there where a 2 to 3 kind of ghost switchings until the system was stable.
What I came up with next, was checking the photocell reading, make sure they are within a predefined limit and flag the effected rooms, meaning as long as they are set to 1 and are within the time frame, the lights will switch on no matter of change of light conditions or sensor readings. Soon as the time limit is reached the lights will switch off and the light sensor flag will reset to 0.
There where still some issues with time delays in lights switching specially when we have more than 2 timers for a room. Implementing the “Analogue Smoothing Algorithm” from Tom Igoe finally brought enough accuracy to stop lights from coming on and off a couple of times before everything got settled.
For normal operation where we also partly depend on a photocell to determine if it's dark enough to switch the lights ore not, implementing the smoothing algorithm gives already enough accuracy since we are also using a time delay. 10 to 15 minutes should be enough to stabilize the light level either way on or off, since we don't have to deal with a up to 50 points jumping values.

Let's have a look at the sketch.

As usual, we start with a few additional variables and there for we go to the declaration part at the end of “//////////////all the other variables/////////////////////”:

unsigned int outsideOnTime = 0; //checking result if the time is within
//on or off time limits

byte room1Lights = 0;
byte room2Lights = 0;
byte room3Lights = 0;
byte room4Lights = 0;
byte room5Lights = 0;
byte room6Lights = 0;
byte room7Lights = 0;
byte room8Lights = 0;
byte room9Lights = 0;

byte currentHour = 0;
byte currentMinute = 0;

//>>>>>>>>>>>>addition starts here<<<<<<<<<<<<<<<
byte lightLevel[17] ={0};                 //array holding the light
                                                       //level flag value for each room
byte photocellSwitch = 0;                //check if the reading
                                                       //within a switching limit
int smoothed = 0;                            //var for the smoothed analogue reading value
int alpha = 4;                                   //the number of past samples to average
int sensor = 0;                                 //the photocell reading
//>>>>>>>>>>>>>>addition ends here<<<<<<<<<<<<<<<<<<<<<

void setup() {
//////////////Start Serial for Debugging/////////////////////


From here we move down in to the main loop and find “////checking the light status//////” where we add:


//////////////////checking the light status//////////////////////

//>>>>>>>>>>>>>>addition starts here<<<<<<<<<<<<<<<<<<<<<<<<<<
alpha = (analogRead(1) / 114) + 1;           //creating alpha by
                                                                //reading a trim pot 
                                                               //on Arduino pin A1
sensor = analogRead(lightSensor);           //reading the photocell
sensorValue = smoothValue(sensor);        //function call to smooth the actual reading 

//if you have some problems with adjusting the light sensor,
//or you just want to know what's going on,
//uncomment the following print statements

//Serial.print("alpha, sensor, Sensor value: ");     //debug only
//Serial.print(alpha);                                          //debug only
//Serial.print(" : ");                                             //debug only
//Serial.print(sensor);                                        //debug only
//Serial.print(" : ");                                             //debug only
//Serial.println(sensorValue);                             //debug only

//////////////////processing the input/////////////////////

//Serial.print("Light level room 1: ");                  //debug only
//Serial.println(lightLevel[0]);                            //debug only
//Serial.print("Light level room 4: ");                 //debug only
//Serial.println(lightLevel[3]);                           //debug only
photocellSwitch = getSensorValue(sensorValue, //function call to check
photoCellCutOff);                                          //if light level is within
                                                                     //switching limits
//>>>>>>>>>addition ends here<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

//////////////Holiday lighting/////////////////////////

if(switchState[20] == 1) {                       //check if the holiday switch
                                                              //is activated
tmElements_t tm;                                    //initializing RTC
if(RTC.read(tm)) {                                  //Reading the clock
currentHour = tm.Hour;                          //passing the time into a var
currentMinute = tm.Minute;                     //passing the time into a var
}


//>>>>>>>>>>>>addition starts here again<<<<<<<<<<<<<<<<<

//Double check if we are within permitted switching limits
//and flag the areas where lights being switched,
//to prevent lights from switching on and off when the
//light level is at the switching limit
//covering the time from evening to midnight 

if(photocellSwitch == 1 && currentHour >= 17 && currentHour <= 23) {
for(int i=0; i<9; i++){
lightLevel[i] = 1;
}
lightLevel[15] = 1;
}
//covering the time from midnight until morning
else if(photocellSwitch == 1 && currentHour >= 0 && currentHour <= 8){
for(int i=0; i<9; i++){
lightLevel[i] = 1;
}
lightLevel[15] = 1;
}

//if it's morning and the light level comes above the limit
//we make sure, the lights switch off
else if(photocellSwitch == 0 && currentHour >= 5 && currentHour <= 8){
for(int c=0; c<17; c++){
lightLevel[c] = 0;
}
}

//Again, if you would like to see what's going on,
//just uncomment the following serial print statements

//Serial.print("Light level room 1 after: ");       //debug only
//Serial.println(lightLevel[0]);                         //debug only
//Serial.print("photocell switch: ");                  //debug only
//Serial.println(photocellSwitch);                    //debug only
//Serial.print("Light level room 3 after: ");       //debug only
//Serial.println(lightLevel[2]);                         //debug only
//Serial.print("Light level room 4 after: ");       //debug only
//Serial.println(lightLevel[3]);                         //debug only
//>>>>>>>>>>addition ends here<<<<<<<<<<<<<<<<<<<<

///////Room 1 (Bed 1) /////////////

Now we need to implement the lightLevel[n] value in the statements where we check if the lights are to be switched on or not. There for we just go down a little further to the single rooms. Let's have a look at room 1:
///////Room 1 (Bed 1) /////////////
if(room1MActive == 1 && currentHour >= room1OnM[0] &&
currentHour <= (room1OffM[0] + 1)){             //checking if we came passed
                                                                         //the hour where the lights
                                                                         //to be switched on
                                                                         //checking the times
room1Lights = checkOnTime(room1OnM[0], room1OnM[1],
room1OffM[0], room1OffM[1]);
}
if(room1O1Active == 1 && currentHour >= room1On1[0] &&
currentHour <= (room1Off1[0] +1)){                 //checking if we came passed
                                                                          //the hour where the lights
                                                                          //to be switched on
                                                                          //checking the times
room1Lights = checkOnTime(room1On1[0], room1On1[1],
room1Off1[0], room1Off1[1]);
}
if(room102Active == 1 && currentHour >= room1On2[0] &&
currentHour <= (room1Off2[0] + 1)){                 //checking if we came passed
                                                                           //the hour where the lights
                                                                           //to be switched on
                                                                           //checking the times
room1Lights = checkOnTime(room1On2[0], room1On2[1],
room1Off2[0], room1Off2[1]);
}
//>>>>>>>>>>>>>>>>>>>>>>>>>>here we have to add<<<<<<<<<<<<<<<<<<<<
//in the if-statement below we currently check only
//if one of the timers is activated now we need to check
//if we hit the defined light level to switch on or off the lights.
//So we have to put that little addition in the if-statement
//“ && lightLevel[0] == 1”

if(room1Lights == 1 ){             //if with in the on time
lightOutput[0] =1;                    //switch on the lights
}
else {
lightOutput[0] = 0;                  //other keep them off
lightLevel[0] = 0;
}

//The above statement reads like that after the addition:
if(room1Lights == 1 && lightLevel[0] == 1){       //if within the on time
lightOutput[0] =1;                                                 //switch on the lights
}
else {
lightOutput[0] = 0;                  //other keep them off
lightLevel[0] = 0;                    //resetting the light level
}
////////Room 2 (Bed 2)//////////////

The same thing we have to do now for every single room where we want to check for light levels. In that process, I also modified the code for the outside lights a bit. Lets just go down to the point “ ////////Outside lights////////////////////” and there we need to change the following statement

if(sensorValue <= photoOutsideOn |
sensorValue < (photoOutsideOn + 50)                   //checking if light is
                                                                             //within photocell readings
&& outsideOnTime == 1){
lightOutput[15] = 32768;                                       //switching on the lights
}
else {
lightOutput[15] = 0;                                               //no matches, they switch off
}
to this:
if(outsideOnTime == 1 && lightLevel[15] == 1){
lightOutput[15] = 32768;
}
else {
lightOutput[15] = 0;
lightLevel[15] = 0;
}

You are still with me? Good, we are nearly done for today. We just need to add a couple of functions to make it work. Now we go all the way down to the bottom of our sketch:


//function to give the photo cell reading a switching range
//and compare it with the preset switch value
//returns 1 if within and 0 if not

byte getSensorValue(int sensorReading, int switchValue) {
byte onStatus = 0;
if(sensorReading <= (switchValue + 25)){
onStatus = 1;
}
else if(sensorReading < (switchValue + 50)){
onStatus = 0;
}
else {
onStatus = 0;
}
return onStatus;
}

The entry is the smoothed sensor reading (sensorReading) and the preset switch value (switchValue). The variable onStatus holds the final return value. In the first if-statement we check if the reading hits the switch value and switch on anyway even if the reading is 25 points higher. The next if-statement covers the jumping around the limits and keeps the lights off and finally if nothing else matches it is assumed that the lights should be off.

Finally we look at the smoothing function:

//function to smooth analog readings
//the analog smoothing algorythm was original
//written by Tom Igoe

int smoothValue(int rawValue){
  if(rawValue > smoothed) {
    smoothed = smoothed + (rawValue - smoothed) / alpha;
  }
  else {
    smoothed = smoothed - (smoothed - rawValue) / alpha;
  }
  return smoothed;
}

I know, it's a lot to update and I am still working on the complete code with all the current updates to make it a little easier to follow all the changes. 
 
One thing is left to do. We need to add a trim potentiometer to our breadboard to get a reference point for smoothing of analogue readings. I used a 100K potentiometer with one pin connected to 5V and the centre pin to pin 24 (A1) of the Atmega chip. I adjusted the reading to approximately to the switching value of the lights to get the best results.


No comments:

Post a Comment