harrisonm
Published © GPL3+

Zoetrope - 3D Gifs!

I made a 3D Zoetrope which uses high intensity LEDs to create real-time stop motion animation. Essentially, it's a 3D gif!

AdvancedShowcase (no instructions)379

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Dual H-Bridge motor drivers L298
SparkFun Dual H-Bridge motor drivers L298
×1
24VDC Power Supply
×1
12VDC Power Supply
×1
Smart Vision Lights Backlight (DLP-300x300)
×1
12VDC Motor
×1
Aluminum Extrusion 30 Series
×1

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Laser cutter (generic)
Laser cutter (generic)
Saw
Or some other way to cut the aluminum extrusion.

Story

Read more

Code

Main Code

Arduino
/*
  Author: Harrison McIntyre
  Youtube Channel: https://www.youtube.com/c/HarrisonMcIntyre
  Last Updated: 6.25.2021
  
  Credit for the "MillisDelay" library code here: https://www.forward.com.au/pfod/ArduinoProgramming/TimingDelaysInArduino.html

*/

#include <millisDelay.h>

// L298N Control Pins
#define directionPin1 9
#define directionPin2 10
#define speedPin 11

// Encoder Pin
#define encoderPin 2

// Light Triggering Pin
#define npnLine 8

// External Control Pins
#define switchPin1 3
#define switchPin2 4

// Various Timers for Checking External Controls
millisDelay switchDelay;
millisDelay potDelay;
millisDelay checkSpeedDelay;

// Some sort of delay after the light is triggered (I honestly don't remember what it does)
unsigned long pulseTimer = 0;

// The light's current state
int lightState = 0;
// The light's previous state
int prevLightState = 1;
// Wheter or not to strobe the light (turn it on then back off again quickly)
boolean strobe = false;
// Total recorded light pulses
unsigned long totalPulses = 0;

// The gear ratio for the motor
double gearRatio = 62.4;
// Number of encoder pulses per revolution of the motor
double ppr = 11;

// External switch state
int switchState = 1;
// External dial val
double dialVal = 0.0;
// Previous external dial val
double prevDialVal = 0.0;

// Adjustment delay for drift compensation on the animation
int adjustmentVal = 0;
// Number of total frames
double framesPerRev = 20.0;

// Used to calculate the RPM of the motor
unsigned long rpmTime = 1;
// The current rotations per minute of the motor
double rpm = 0.0;
// The Desired rotations per minute of the motor
double setRpm = 0.0;

// Variables used in closed loop control of the motor's RPM
double difference = 0.0;
double gain = 0.05;

// Used for debugging purposes
unsigned long debugVal = 0;

// Used to calculate when to strobe the light next
unsigned long rawStrobeTime = 0;

void setup() {
  // Initialize the necessary pins
  pinMode(directionPin1, OUTPUT);
  pinMode(directionPin2, OUTPUT);
  pinMode(speedPin, OUTPUT);
  pinMode(npnLine, OUTPUT);
  
  pinMode(switchPin1, INPUT_PULLUP);
  pinMode(switchPin2, INPUT_PULLUP);

  // Start the timers that we use to check various controls and machine states
  switchDelay.start(500);
  potDelay.start(700);
  checkSpeedDelay.start(1);

  // Attach an interrupt to the encoder pin
  attachInterrupt(digitalPinToInterrupt(encoderPin), increment, FALLING);

  // Start serial communication
  //Serial.begin(9600);

  // Wait (because why not)
  delay(1000);

  // Set some initial states for the L298N control pins
  digitalWrite(directionPin1, HIGH);
  digitalWrite(directionPin2, LOW);
  analogWrite(speedPin, 240);
}


// Function for pulsing the overhead light
void pulseLight(unsigned long val) {
  // If commanded to strobe, do
  if (strobe) {
    // Only strobe once
    strobe = false;
    // Turn the light on
    lightState = 1;
    // Store the current time in microseconds
    pulseTimer = micros();
  }
  // If the desired time has elapsed turn the light off
  if (micros() - pulseTimer >= val) {
    lightState = 0;
  }
  // Change the light state only if it has changed
  if (lightState != prevLightState) {
    digitalWrite(npnLine, lightState);
    prevLightState = lightState;
  }
}

// Function for checking the external switch
void checkSwitch() {
  // If it's time to check the switch, do
  if (switchDelay.justFinished()) {
    // Reset the timer
    switchDelay.repeat();
    // Read the switch state
    if (!digitalRead(switchPin1)) {
      switchState = -1;
    }else if (!digitalRead(switchPin2)) {
      switchState = 0;
    }else {
      switchState = 1;
    }
  }
  // Update the motor speed
  analogWrite(speedPin, dialVal);
}

// Function for checking the external dial
void checkDial() {
  // If it's time to check the dial, do
  if (potDelay.justFinished()) {
    // Reset the timer
    potDelay.repeat();
    // Read the dial
    adjustmentVal = (analogRead(A3) / 1024.0 * 400) - 200;
    if (adjustmentVal >= -5 && adjustmentVal <= 5) {
      adjustmentVal = 0;
    }
  }
}

// Function for closed loop control of the motor speed
void checkSpeed() {
  // If it's time to check the motor speed, do
  if (checkSpeedDelay.justFinished()) {
    // Restart the timer
    checkSpeedDelay.repeat();
    // Check the motor speed and make necessary adjustments to motor output
    difference = (setRpm - rpm) / 5;
    difference = constrain(difference, -1, 1);
    dialVal = dialVal + difference*gain;
  }
}

void loop() {
  // Pulse the light for 1000 microseconds (1 millisecond)
  pulseLight(1000);
  
  // Check the various external controls and motor speed
  checkSwitch();
  checkDial();
  checkSpeed();

  // Calculate the motor RPM
  rpm = 1000000/(debugVal*ppr*gearRatio)*60;

  // Various display modes based upon external switch input
  if (switchState == 1) {
    totalPulses = 0;
    setRpm = 84;

    // If it's time to strobe the light, do
    if (micros() - rawStrobeTime >= 35755 + adjustmentVal) {
      strobe = true;
      rawStrobeTime = micros();
    }

  }else if (switchState == 0) {
    totalPulses = 0;
    setRpm = 90;

    // If it's time to strobe the light, do
    if (micros() - rawStrobeTime >= 33270 + adjustmentVal) {
      strobe = true;
      rawStrobeTime = micros();
    }
  }else if (switchState == -1) {
    totalPulses = 0;
    dialVal = 150;

    // Turn the light off
    digitalWrite(npnLine, LOW);
  }
}

// Function to increment totalPulses every time the encoder pulses
void increment() {
  debugVal = (micros() - rpmTime);
  rpmTime = micros();
  totalPulses++;
}

Credits

harrisonm
0 projects • 1 follower
Contact

Comments

Please log in or sign up to comment.