Light Color Tracker
by tinyboatproductions in Circuits > Sensors
1056 Views, 8 Favorites, 0 Comments
Light Color Tracker
I was inspired to do this project by the question "will a plant survive over here?" I have several great windows where I live and love having plants on the window sills. I want to put plants other places around, so I want to find where light conditions will be similar enough to the window for the plant to do well if I move it.
This sensor will collect the red, green, and blue values of the light that it is in. The Arduino will read those values and write them in a file to the SD card. We can then use this file and the data points to see what the color of the light is over time, or the total of each over a given amount of time. To analyze the data I will be using python, but with some patience I think you could do it in Excel or Google Sheets.
I don't think that this simple setup will give you all the answers to your light questions or will necessarily answer if a plant will survive, for example. I do think that this is a good outline/baseline for using different simple sensors located around to collect data.
Supplies
Supplies
- Arduino Nano
- Sparkfun ILS29125 - When I looked in May of 2021 it looks like this part is on back order but should be back at some point
- It looks like this one from Adafruit should work too. Just be aware that I have not tried this one.
- Arduino Micro SD reader
- Micro SD card - Less than 16 GB
- Wire - Old ribbon cables from computers are great
- Solder
- Altoids Tin - Or other case
- Mini USB Cord
Tools
- Soldering Iron
- Pliers
- Side Cutters
- Card Reader
- Wire Strippers
- Hot Glue Gun
- Hammer - for the case
- Nail - for the case
The Circuit
The circuit is fairly simple if you have the breakout boards to start with. We essentially just need to give each board power and connect the boards to the right pins on the Arduino. The pin numbers I have listed need to be matched as those are the default pins for some of the communication protocols that the Arduino can use.
The Sensor uses I2C and the card reader uses SPI. I don't really know how either of these communication protocols work but the Arduino is able to take care of all the hard stuff for us.
Here are what the pins need to be connected to in text.
Sensor:
GND -> GND
3.3V -> 3.3V
SDA -> A4
SCL -> A5
SD Reader:
CS -> D10
SCK -> D13
MOSI -> D11
MISO -> D12
VCC -> 5V
GND -> GND
Code
The code for this is like the SD card reader and the RGB sensor example code had a baby. I basically just jammed the chunks that I needed from each to make it work.
I'll explain each step a bit more in the next step.
This code as written will set everything up and then take a reading from the sensor and write that to the SD card. It will then go into a low power mode for 8 seconds. After that time it will wake back up and write a new reading to the SD card. This setup will give you about 11,000 readings in 24 hours.
Code
#include "SPI.h"<br>#include "SD.h" #include "Wire.h" #include "SFE_ISL29125.h" #include "LowPower.h" SFE_ISL29125 RGB_sensor; File myFile; int nameGuess = 0; String fileName = ""; void setup() { //Set up the onboard LED and turn it on pinMode(13, OUTPUT); digitalWrite(13,HIGH); //Start serial communication Serial.begin(115200); delay(1000); //Set up the SD card Serial.print("Initializing SD card..."); if (!SD.begin(10)) { Serial.println("initialization failed!"); while (1); } Serial.println("initialization done."); //Find the file name that can be used that does not already exist while(SD.exists(String(nameGuess)+".TXT")){ nameGuess++; } fileName = String(nameGuess) + ".txt"; //Add a heading line to the file myFile = SD.open(fileName, FILE_WRITE); myFile.print("Red | Green | Blue"); myFile.close(); //Make sure the sensor is ready if (RGB_sensor.init()){ Serial.println("Sensor inititalization successful"); } //blink the LED to notify of ready state digitalWrite(13, LOW); delay(100); digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100); digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(200); //Make sure the light is off when we read a value } void loop() { //read the sensor values unsigned int red = RGB_sensor.readRed(); unsigned int green = RGB_sensor.readGreen(); unsigned int blue = RGB_sensor.readBlue(); //open the file myFile = SD.open(fileName, FILE_WRITE); //record the data myFile.print(red,DEC); myFile.print(","); myFile.print(green,DEC);myFile.print(","); myFile.println(blue,DEC); //close the file myFile.close(); //blink the LED to notify of a new value digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); //delay(1000); //Put the Arduino in a Low power mode for 8 seconds. //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF); }
Code Explanation
For this code we just want to read from the sensor and write the data to a text file on the SD card. We can look at the data and work with it later.
To start with lets add some libraries because why write our own code when someone else has already done it better than we can. I am using 5 libraries here. SPI.h, for the SPI communication to the SD card reader. SD.h to talk with the SD card. Wire.h is for the I2C communication. SFE_ISL29125.h provided by Sparkfun to talk with their sensor. Finally, LowPower.h to use the low power functions of the board.
#include "SPI.h"<br>#include "SD.h" #include "Wire.h" #include "SFE_ISL29125.h" #include "LowPower.h"
Now we need to declare a bunch of things so that we can move them going forward. The sensor is first, followed by the a generic 'File' we will work on this more in the setup(). Then a guess for a file name to start with and a final file name, we leave this blank. We will add something here in a bit to make it work. The reason we declare these here is so we can use them any where in the sketch, if we were to declare them just in the setup() we wouldn't be able to use it in the loop().
SFE_ISL29125 RGB_sensor; File myFile; int nameGuess = 0; String fileName = "";
Now in the setup we start wit some pretty generic stuff, first setting up the onboard LED so we can use it to communicate with us without needing to plug into a computer to use the serial monitor.
Then we start the serial communication.
void setup() { //Set up the onboard LED and turn it on pinMode(13, OUTPUT); digitalWrite(13,HIGH); //Start serial communication Serial.begin(115200); delay(1000);
Now we need to setup the SD card and print out some messages to tell us what is going on.
//Set up the SD card Serial.print("Initializing SD card..."); if (!SD.begin(10)) { Serial.println("initialization failed!"); while (1); } Serial.println("initialization done.");
Now we need to find an open file name. We can use a function of the SD library SD.exists() to look on the SD card and tell us if a file exists. We start with the guess that we declared above "0" and then check to see if it exists. If it does we add 1 to the guess. The while loop will keep checking and adding until the file name doesn't exist. Then we can set our file name to that number and add ".TXT" to make it a text file.
//Find the file name that can be used that does not already exist while(SD.exists(String(nameGuess)+".TXT")){ nameGuess++; } fileName = String(nameGuess) + ".txt";
Now we need to make sure the sensor is ready. I would recommend once you get everything together check this to make sure the sensor is working right.
Finally we will just blink the LED to tell us we are ready to go.
//Make sure the sensor is ready if (RGB_sensor.init()){ Serial.println("Sensor inititalization successful"); } //blink the LED to notify of ready state digitalWrite(13, LOW); delay(100); digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(100); digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); delay(200); //Make sure the light is off when we read a value }
In the loop we are going to read the values from the sensor then write it to file and the put the Arduino to sleep. Then do it all over again, and again, and again, until the end of time.
First off, read the values from the sensor.
void loop() { //read the sensor values unsigned int red = RGB_sensor.readRed(); unsigned int green = RGB_sensor.readGreen(); unsigned int blue = RGB_sensor.readBlue();
Now we add those values we just got to the file and then close it. I want to close the file each time to make sure that if something goes wrong, or we pull the plug the data will be safe.
//open the file myFile = SD.open(fileName, FILE_WRITE); //record the data myFile.print(red,DEC); myFile.print(","); myFile.print(green,DEC);myFile.print(","); myFile.println(blue,DEC); //close the file myFile.close();
Now we blink the LED to let us know we are done reading and writing to the file.
//blink the LED to notify of a new value digitalWrite(13, HIGH); delay(100); digitalWrite(13, LOW); //delay(1000);
Now we put the Arduino to sleep for 8 seconds. 8 seconds is the longest time that this library allows. It will give us way more data than we need. But what is the point of automating data collection if we arn't going to go over the top with it?
//Put the Arduino in a Low power mode for 8 seconds. LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF); }
And that's it for the code.
Case
For a case I am using an empty Altoids tin. They are cheap and easy to get your hands on, also they are full of candy, so a snack while you work. Just make sure to rinse it out before you add your components. You could also make and 3D print a case here. Chose what works for you.
We need two holes in the tin, one for the sensor and one for the cord. Be careful and wear gloves for this, the thin metal can slice you open without thinking twice. I would recommend manual tools here as things go wrong a lot slower with hand tools.
To make the holes I used a nail and hammer to punch a hole then some pliers and side cutters to open the holes up to make some space.
To hold the sensor in the top I used some hot glue. For the board, I just hold everything in place and plug it in.
Data Analyzation
With the code I have provided you will now have an absolutely ridiculous number of data points, over 10,000. To work with that data in a reasonable amount of time I am going to load it and graph it using Python. If you are patient you could also use Google Sheets or Excel, just be aware that crashing is a real risk.
When capturing data I found that setting it up after dark and unplugging it the next day after dark was the easiest data to work with. Once I got this onto the computer I changed the extension from ".TXT" to ".CSV" to make it easier to work with. I also deleted the data points that were 0's, overnight, just to make the graphs neater. Also, to get the best most accurate information you should sample all the spots at the same time for several days and average over the days. In my graphs the first one I did on a sunny day and the second one on a cloudy day and you can see a huge difference in the overall values captured.
First I am going to make a graph of the different colors over time as well as find the average of each color over the time that I collected data.
I made graphs for each spot that I sampled and three graphs, one for each color, and putting all the locations that I sampled on it. I've included the code I used below.
You could do all kinds of stuff with this data. There are tons of ways to analyze this data. Choose what makes sense to you.
import matplotlib.pyplot as plt
import pandas #if statements to skip parts easily. if True: #import the baseline data colnames = ['red','green','blue'] blData = pandas.read_csv('baseline.CSV', names=colnames) #split the data into the three colors blr = blData.red.tolist() #baseline red blg = blData.green.tolist() blb = blData.blue.tolist() blRma = blData['red'].rolling(500).mean() #use a smooting function to make the data look better blGma = blData['green'].rolling(500).mean() blBma = blData['blue'].rolling(500).mean() if True: #if statment so I can skip it #sampled area colnames = ['red','green','blue'] sData = pandas.read_csv('sample.CSV', names=colnames) sr = sData.red.tolist() #baseline red sg = sData.green.tolist() sb = sData.blue.tolist() sRma = sData['red'].rolling(500).mean() #use a smooting function to make the data look better sGma = sData['green'].rolling(500).mean() sBma = sData['blue'].rolling(500).mean() #baseline data if True: figure, axis = plt.subplots(2,2) axis[0,0].plot(blRma,'r') axis[0,0].plot(blBma,'b') axis[0,0].plot(blGma,'g') axis[0,0].set_title("All") axis[0,1].plot(blRma,'r') axis[0,1].set_title("Red") axis[1,0].plot(blBma,'b') axis[1,0].set_title("Blue") axis[1,1].plot(blGma,'g') axis[1,1].set_title("Green") plt.show() #sample data if True: figure, axis = plt.subplots(2, 2) axis[0,0].plot(sRma, 'r:') axis[0,0].plot(sBma, 'b:') axis[0,0].plot(sGma, 'g:') axis[0,0].set_title("All") axis[0,1].plot(sRma, 'r:') axis[0,1].set_title("Red") axis[1,0].plot(sBma, 'b:') axis[1,0].set_title("Blue") axis[1,1].plot(sGma, 'g:') axis[1,1].set_title("Green") plt.show()