// Sky color logger. // Takes a photograph of the sky every second, then finds and stores the average color value. // Each minute, average the one-second averages to find the average sky color over the last minute. // Upload the result to Pachube (http://www.pachube.com/) as a hexadecimal color value. // Built with the EEML Library for Processing, available at http://www.eeml.org/library/ import processing.video.*; import eeml.*; DataOut dataOut; Capture cam; int pixelCount; color averageColor; color minuteAverageColor; int lastMinute; int minuteAverageRed = 0; int minuteAverageGreen = 0; int minuteAverageBlue = 0; int averageRed = 0; int averageGreen = 0; int averageBlue = 0; int sampleCount = 0; int[] averages; // Stores recent averages. void setup() { size(640, 480); noStroke(); frameRate(2); // Set up communication with Pachube. String feedURL = "http://www.pachube.com/api/feeds/3929.xml"; String apiKey = "YOUR PACHUBE API KEY HERE"; dataOut = new DataOut(this, feedURL, apiKey); dataOut.addData(0, "sky,color"); // Set up the camera. String[] devices = Capture.list(); println(devices); cam = new Capture(this, 320, 240, devices[1]); cam.frameRate(1); // Read 1 frame per second. pixelCount = cam.width * cam.height; // Take note of the time. We will upload once per minute. lastMinute = minute(); // Store recent averages in an array. averages = new int[240]; // Fill the averages array with white to start for(int i = 0; i < averages.length; i++) averages[i] = 255; } void draw() { background(0); // Show the live camera feed in the top left. fill(0); image(cam, 0, 0); // Show the latest average color in the top right. fill(averageColor); rect(320, 0, 320, 240); // Show the last minute average in the bottom left. // This is what was actually sent to Pachube. fill(minuteAverageColor); rect(0, 240, 320, 240); // Draw the recent averages as horizontal lines in the bottom right. // The latest values are pushed onto the top of the stack. for(int i = 0; i < averages.length; i++) { stroke(averages[i]); strokeWeight(1); line(320, 240 + i, 640, 240 + i); } noStroke(); // Aggregate the averages and send to Pachube every minute. if((minute() != lastMinute) && (sampleCount > 0)) { // Find the aggregated average for each minute. minuteAverageRed = minuteAverageRed / sampleCount; minuteAverageGreen = minuteAverageGreen / sampleCount; minuteAverageBlue = minuteAverageBlue / sampleCount; minuteAverageColor = color(minuteAverageRed, minuteAverageGreen, minuteAverageBlue); // Convert to hex and send to Pachube. println("Sending sky value " + hex(minuteAverageColor, 6) + " to Pachube."); dataOut.update(0, hex(minuteAverageColor, 6)); int response = dataOut.updatePachube(); println("Pachube response: " + response); // Should be 200 if successful; 401 if unauthorized; 404 if feed doesn't exist. // Shift values in the averages array over one, and then add the latest to the beginning. for(int i = averages.length - 1; i > 0; i--) averages[i] = averages[i - 1]; averages[0] = minuteAverageColor; // Reset the minute averages. minuteAverageRed = 0; minuteAverageGreen = 0; minuteAverageBlue = 0; // Reset the sample count. sampleCount = 0; // Reset the minute mark. lastMinute = minute(); } } void captureEvent(Capture cam) { // Get the latest camera data. cam.read(); cam.loadPixels(); // Reset the averages for this frame. averageRed = 0; averageGreen = 0; averageBlue = 0; // Average the pixels. int pixelColor; for (int i = 0; i < pixelCount; i++) { pixelColor = cam.pixels[i]; // Extract the RGB values. averageRed += (pixelColor >> 16) & 0xff; averageGreen += (pixelColor >> 8) & 0xff; averageBlue += pixelColor & 0xff; } // Take the average. averageRed = averageRed / pixelCount; averageGreen = averageGreen / pixelCount; averageBlue = averageBlue / pixelCount; // Note this average for the aggregated average taken at the minute mark. // It would be simpler not to store every single frame's average, and instead to average // a minute's worth of pixel data (e.g. average = averageChannel / (pixelCount * sampleCount)) // but for the sake of the display it's useful to have average data from the latest frame as well. minuteAverageRed += averageRed; minuteAverageGreen += averageGreen; minuteAverageBlue += averageBlue; averageColor = color(averageRed, averageGreen, averageBlue); // Iterate the sample count... this is what we use to take the average // at the minute mark. With the camera at 1 FPS, it should always be 60, but // in case of timing issues it's worth keeping track. sampleCount++; }