Arduino Photo Booth Code:
#include <FastLED.h>
#define NUM_LEDS 60 // Number of NeoPixels
#define DATA_PIN 6 // Data pin for NeoPixel strip
#define POT_PIN A0 // Potentiometer connected to A0
CRGB leds[NUM_LEDS]; // Array to hold the LED colors
int potValue = 0; // Read value from the potentiometer
void setup() {
Serial.begin(115200);
// Initialize FastLED with NeoPixel settings
// FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
// FastLED.setBrightness(50); // Adjust brightness as needed
// // Show an initial red LED to indicate setup
// leds[0] = CRGB::Red;
// FastLED.show();
// delay(3000);
// // Turn off the LEDs after the initial indicator
// fill_solid(leds, NUM_LEDS, CRGB::Black);
// FastLED.show();
}
void loop() {
// Read the potentiometer value
potValue = analogRead(POT_PIN);
// Send decadeIndex to Processing
Serial.println(potValue);
// fill_solid(leds, NUM_LEDS, CRGB::Blue);
// FastLED.show();
// // Show the updated colors on the LEDs
// FastLED.show();
// Add a small delay for stability
delay(100);
}
Processing Photo Booth Code:
import processing.serial.*;
import processing.video.*;
import processing.sound.*;
PFont font;
Capture cam;
SoundFile[] decadeAudios = new SoundFile[11];
PImage[] decadeImages = new PImage[11];
int decadeIndex;
int NUM_DECADES = 11;
// array stores values from Arduino
Serial serialPort;
int NUM_OF_VALUES_FROM_ARDUINO = 3;
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];
int rawPotValue = 0;
int countdown = 0; // Countdown timer
boolean isCountingDown = false; // Flag to indicate if countdown is active
int countdownStartTime = 0;
int pictureCount = 0;
int delayStartTime = -1; // Time when the third picture is taken, for delay
int delayDuration = 1000;
long lastPressTime = 0;
long debounceDelay = 50; // 50 ms debounce delay
boolean reset = false;
boolean showStartScreen = false; // Flag to control if we show the start screen or not
PImage processedCam;
PImage result;
float t = 0.08;
void setup() {
size(1920, 1050);
background(0);
font = createFont("Heiti SC", 48);
// Initialize camera
String[] cameras = Capture.list();
if (cameras.length == 0) {
println("No cameras found!");
exit();
}
cam = new Capture(this, cameras[1]);
cam.start();
result = createImage(cam.width, cam.height, RGB);
// Load images and audios
for (int i = 0; i < NUM_DECADES; i++) {
decadeImages[i] = loadImage("decade_" + (1920 + i * 10) + ".jpg");
decadeAudios[i] = new SoundFile(this, "decade_" + (1920 + i * 10) + ".mp3");
}
// Setup serial communication
printArray(Serial.list());
serialPort = new Serial(this, "/dev/cu.usbmodem11201", 115200); // Adjust port
}
void draw() {
getSerialData();
buttonPressed();
if (reset == true){
pictureCount = 0; // Reset the picture count
delayStartTime = -1; // Reset the delay timer
isCountingDown = false; // Reset the countdown flag
frameRate(60);
reset = false; // Reset the flag
println("Program reset");
}
if (showStartScreen == false) {
showStartPage(); // Show the start page
}
if (showStartScreen == true) {
// Main photo booth program
if (cam.available()) {
cam.read();
}
decadeIndex = (int) map(arduino_values[0], 0, 1023, 0, 10);
PImage processedCam = removeGreen(cam, t, decadeImages[decadeIndex]);
if (decadeIndex == 10) {
// No background image, just the live camera feed
image(cam, 0, 0, width, height);
}
else {
// Draw current decade's background image
if (decadeImages[decadeIndex] != null) {
if (decadeIndex < 3) {
applyGrayscale(processedCam);
decadeImages[decadeIndex].loadPixels();
}
image(processedCam, 0, 0, width, height);
}
}
// Play audio for the current decade
if (!decadeAudios[decadeIndex].isPlaying()) {
for (SoundFile audio : decadeAudios) {
audio.stop(); // Stop any other audio
}
decadeAudios[decadeIndex].play();
}
// Display current decade
fill(255);
textFont(font);
textAlign(CENTER, CENTER);
textSize(70);
String decadeText = (1920 + decadeIndex * 10) + "s " + "\u65F6\u4EE3"; // Example of adding Chinese characters (时代 means "era")
text(decadeText, width / 2, height - 50);
String count = (3 - pictureCount) + " photos remaining";
textSize(40);
text(count, width / 4, 1000);
if (isCountingDown) {
int elapsed = millis() - countdownStartTime; // Calculate elapsed time
countdown = 3 - elapsed / 1000; // Calculate remaining time
// Display countdown
if (countdown > 0) {
fill(0, 150); // Transparent overlay
rect(0, 0, width, height); // Dim the background
fill(255);
textAlign(CENTER, CENTER);
textSize(100);
text(countdown, width / 2, height / 2); // Show countdown number
} else {
saveFrame("snapshot-######.png"); // Take a snapshot
textSize(50);
text("Photo saved!", width/2, height/2);
isCountingDown = false; // Stop the countdown
// If third picture is taken, start the delay timer
if (pictureCount >= 3 && delayStartTime == -1) {
delayStartTime = millis(); // Start the delay when the third picture is taken
}
}
}
// Show alert after delay if third picture was taken
if (delayStartTime != -1) {
int elapsed = millis() - delayStartTime; // Time passed since third picture
if (elapsed >= delayDuration) {
text("All pictures taken! Please collect your prints.\nPress and hold START to restart.", width/2, height/2);
frameRate(1); // Stop the program after the alert
}
}
}
}
void applyGrayscale(PImage img) {
img.loadPixels();
for (int i = 0; i < img.pixels.length; i++) {
color c = img.pixels[i];
float gray = (red(c) + green(c) + blue(c)) / 3;
img.pixels[i] = color(gray);
}
img.updatePixels();
}
void showStartPage() {
background(0);
fill(255);
textFont(font);
textAlign(CENTER, CENTER);
textSize(70);
text("Nostalgia PhotoBooth\nPress START to begin!\n(3 pictures total)", width / 2, height / 2);
buttonPressed();
}
void buttonPressed() {
// Check if enough time has passed since last button press to avoid debounce
if (millis() - lastPressTime > debounceDelay) {
lastPressTime = millis(); // Update the last press time
// Button press is detected when arduino_values[1] is HIGH (1)
if (arduino_values[1] == 1) {
if (showStartScreen) {
// If we're on the start screen, transition to the photo booth
frameRate(60); // Adjust frame rate for the photo booth program
pictureCount = 0; // Reset picture count
showStartScreen = false;
println("Transition to photo booth.");
} else {
// If already in photo booth, reset the program (restart)
resetProgram();
println("Program reset.");
}
}
// Handle the countdown if the photo booth is active
if (!isCountingDown && arduino_values[2] == 1 && pictureCount < 3) {
isCountingDown = true;
countdownStartTime = millis();
countdown = 3;
pictureCount++;
}
}
}
PImage removeGreen(PImage orig, float thresh, PImage decadeImage) {
PImage result = createImage(orig.width, orig.height, RGB);
result.loadPixels();
// Ensure the decade image matches the dimensions of the result
if (decadeImage != null) {
if (decadeImage.width != result.width || decadeImage.height != result.height) {
decadeImage.resize(result.width, result.height);
}
}
for (int i = 0; i < orig.pixels.length; i++) {
color og = orig.pixels[i];
float r = red(og);
float g = green(og);
float b = blue(og);
// Define a threshold for the red channel based on input 'thresh'
float limitG = g - g * thresh;
// If red channel is significantly higher than green and blue, replace with decade background
if (r < limitG && b < limitG && decadeImage != null) {
result.pixels[i] = decadeImage.pixels[i]; // Use the corresponding decade image pixel
} else {
result.pixels[i] = og; // Keep the original pixel
}
}
result.updatePixels();
return result;
}
void resetProgram() {
showStartScreen = true; // Show the start screen again
pictureCount = 0; // Reset the picture count
delayStartTime = -1; // Reset the delay timer
isCountingDown = false; // Reset the countdown flag
frameRate(60); // Reset frame rate for start screen
}
void getSerialData() {
while (serialPort.available() > 0) {
String in = serialPort.readStringUntil( 10 );
if (in != null) {
print("From Arduino: " + in);
String[] serialInArray = split(trim(in), ",");
if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
for (int i=0; i<serialInArray.length; i++) {
arduino_values[i] = int(serialInArray[i]);
}
}
}
}
}
Neopixel Processing Code:
import processing.sound.*;
import processing.serial.*;
Serial serialPort;
SoundFile sample;
FFT fft;
Amplitude amplitude;
int NUM_LEDS = 60;
color[] leds = new color[NUM_LEDS];
int bands = 1024;
color currentColor, nextColor;
float transitionProgress = 0; // Progress of the transition (0 to 1)
float transitionSpeed = 0.05; // Speed of the color transition
void setup() {
fullScreen();
colorMode(RGB, 255); // Use RGB color mode for full control over red, green, and blue
serialPort = new Serial(this, "/dev/cu.usbmodem1101", 115200);
sample = new SoundFile(this, "decade_2000.mp3");
sample.loop();
fft = new FFT(this, bands);
fft.input(sample);
amplitude = new Amplitude(this);
amplitude.input(sample);
// Initialize the starting color
currentColor = color(255, 215, 0); // Initial gold color (warm yellow)
nextColor = color(255, 0, 0); // Starting next color, for example, red
}
void draw() {
background(0);
fft.analyze(); // Analyze the sound frequencies
float volume = amplitude.analyze(); // Get the overall amplitude
int ledsToLight = int(map(volume, 0, 1, 0, NUM_LEDS)); // Number of LEDs to light based on volume
// Flashing effect based on amplitude (beat detection)
float beatThreshold = 0.1; // Sensitivity to the beat, adjust as needed
for (int i = 0; i < NUM_LEDS; i++) {
// Calculate the brightness based on song position and volume
float brightness = map(i, 0, NUM_LEDS, 255, 50);
brightness = constrain(brightness, 0, 255); // Ensure brightness is within valid range
// Introduce flashing based on the beat detection (volume level)
if (volume > beatThreshold) {
brightness = random(255); // Flash with random brightness for a flashing effect
}
// 1920s-inspired color palette with smooth transition
if (sample.position() / sample.duration() < 0.33) {
// Gold tones (warm yellows and browns)
nextColor = color(int(brightness * 0.9), int(brightness * 0.8), int(brightness * 0.3)); // Goldish
} else if (sample.position() / sample.duration() < 0.66) {
// Burgundy and deep reds
nextColor = color(int(brightness * 0.8), int(brightness * 0.2), int(brightness * 0.2)); // Burgundy Red
} else {
// Cream or ivory tones
nextColor = color(int(brightness * 0.95), int(brightness * 0.9), int(brightness * 0.75)); // Soft Cream
}
// Smoothly transition between the colors
currentColor = lerpColor(currentColor, nextColor, transitionProgress);
// Set the color to the current color after transitioning
leds[i] = currentColor;
}
// Gradually increase the transition progress (from 0 to 1)
if (transitionProgress < 1) {
transitionProgress += transitionSpeed;
} else {
// Once the transition is complete, reset and start a new color transition
transitionProgress = 0;
}
sendColors(); // Send the updated color data to Arduino
}
void sendColors() {
byte[] out = new byte[NUM_LEDS * 3];
for (int i = 0; i < NUM_LEDS; i++) {
// Get RGB values for each LED
int r = (int)red(leds[i]);
int g = (int)green(leds[i]);
int b = (int)blue(leds[i]);
// Send RGB values for each LED
out[i * 3] = (byte)(r); // Red channel
out[i * 3 + 1] = (byte)(g); // Green channel
out[i * 3 + 2] = (byte)(b); // Blue channel
}
serialPort.write(out); // Send the color data to the Arduino
}