Brendan Sweeny
Published © GPL3+

Simple Arduino Optical Chopper

This simple optical chopper periodically interrupts an external beam of light, useful for photoelectrochemistry.

BeginnerFull instructions provided2 hours5,978
Simple Arduino Optical Chopper

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
Capacitor 100 µF
Capacitor 100 µF
×1
Pushbutton switch 12mm
SparkFun Pushbutton switch 12mm
×1
Resistor 10k ohm
Resistor 10k ohm
×1
Servos (Tower Pro MG996R)
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
(optional)
×1
Single Turn Potentiometer- 10k ohms
Single Turn Potentiometer- 10k ohms
(optional)
×3
Resistor 221 ohm
Resistor 221 ohm
(optional)
×1

Story

Read more

Schematics

Simple Optical Chopper Schematic

Optical Chopper with LCD

Potentiometer Schematic

Code

Simple Optical Chopper Code

Arduino
Code for the simple optical chopper
// include the servo library and assign to myServo
#include <Servo.h>
Servo myServo;

// these variables cannot be assigned to 120 * 1000 for some reason
// thus, they are defined in milliseconds
unsigned long quietTime = 120000;
unsigned long chopInterval = 4000;
unsigned long totalTime = 240000;

// variable used to indicate that the chop program is running
// value of 1 or true means program is running
int programRunning = 0;
// previous state of the start button
int previousSwitchState = 0;
// start button input pin
const int startButtonPin = 2;
// onboard LED used to indicate that chop program is running
const int ledPin = 13;
// time of previous interval
unsigned long previousMillis = 0;
// time that the start button was pressed
unsigned long programStartTime = 0;

// servo angles for both open and closed positions
const int closedAngle = 10;
const int openAngle = 90;

void setup() {
  // pin 2, the start button is an input
  pinMode(startButtonPin, INPUT);
  // pin 13, the program running indicator LED is an output
  pinMode(ledPin, OUTPUT);
  // servo is attached to pin 9
  myServo.attach(9);
  // servo starts at 90 deg
  myServo.write(90);
  // console for diagnostics
  //Serial.begin(9600);
}

void loop() {
  // gets the current runtime in milliseconds
  unsigned long currentMillis = millis();
  // reads the start button switch state
  int switchState = digitalRead(startButtonPin);
  
  // start button
  if (switchState != previousSwitchState) {
    if (switchState == HIGH) {
      programStartTime = currentMillis;
      programRunning = !programRunning;
    }
  }
  
  // if the program running state is true as a result of pressing the start button
  if (programRunning) {
    digitalWrite(ledPin, HIGH);
    
    // if quiet time has passed and total time has not been exceeded
    if (currentMillis >= programStartTime + quietTime) {
      
      // if the current time is still less than the total run time
      if (currentMillis <= totalTime + programStartTime) {
       
        // if the interval time has passed
        if (currentMillis - previousMillis >= chopInterval) {
        
          // stores the current time as previous time
          previousMillis = currentMillis;
    
          if (myServo.read() == closedAngle) {
            myServo.write(openAngle);
          } else {
            myServo.write(closedAngle);
          }
        
        }
      
      // the servo returns to original position and the program is stopped
      // when total run time is reached
      } else {
        myServo.write(closedAngle);
        programRunning = !programRunning;
      }
    
    // servo remains in original position if still in quiet time  
    } else {
      myServo.write(closedAngle);
    }
  
  // servo remains in original position when chopper program is not running  
  } else {
    digitalWrite(ledPin, LOW);
    myServo.write(closedAngle);
  }

  // previousSwitchState is assinged to switchState
  previousSwitchState = switchState;
  
  // printing variables to the serial port for diagnostics
  /*
  Serial.print("programStart: ");
  Serial.print(programStartTime / 1000);
  Serial.print(" totalTime: ");
  Serial.print(totalTime);
  Serial.print(" quietTime: ");
  Serial.print(quietTime);
  Serial.print(" Running: ");
  Serial.print(programRunning);
  Serial.print(" previous: ");
  Serial.print(previousMillis / 1000);
  Serial.print(" current: ");
  Serial.println(currentMillis / 1000);
  */
}

Optical Chopper With LCD and Potentiometers

Arduino
Code for the optical chopper that includes the LCD and three potentiometers
// include the servo library and assign to myServo
#include <Servo.h>
Servo myServo;
#include <LiquidCrystal.h>
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

// quiet time, chop interval, and run time defined in milliseconds
unsigned long quietTime;
unsigned long chopInterval;
unsigned long programTime;
unsigned long totalTime;

// variable used to indicate that the chop program is running
// value of 1 or true means program is running
int programRunning = 0;
// previous state of the start button
int previousSwitchState = 0;
// start button input pin
const int startButtonPin = 8;
// onboard LED used to indicate that chop program is running
const int ledPin = 13;
// time of previous interval
unsigned long previousMillis = 0;
// time that the start button was pressed
unsigned long programStartTime = 0;

// servo angles for both open and closed positions
const int closedAngle = 10;
const int openAngle = 90;

// pin assignments for the potentiometers
int const chopIntervalPin(A0);
int const programTimePin(A1);
int const quietTimePin(A2);

void setup() {
  // pin 8, the start button is an input
  pinMode(startButtonPin, INPUT);
  // pin 13, the program running indicator LED is an output
  pinMode(ledPin, OUTPUT);
  // servo is attached to pin 9
  myServo.attach(9);
  // servo starts at 90 deg
  myServo.write(90);
  // console for diagnostics
  //Serial.begin(9600);
  lcd.begin(16, 2);
}

void loop() {
  // gets the current runtime in milliseconds
  unsigned long currentMillis = millis();
  // reads the start button switch state
  int switchState = digitalRead(startButtonPin);
  
  // start button
  if (switchState != previousSwitchState) {
    if (switchState == HIGH) {
      programStartTime = currentMillis;
      programRunning = !programRunning;
    }
  }
  
  // if the program running state is true as a result of pressing the start button
  if (programRunning) {
    digitalWrite(ledPin, HIGH);
    if ((currentMillis - programStartTime) % 500 == 0) {
      lcd.clear();
      lcd.print("Quiet Time:");
      lcd.setCursor(12, 0);
      lcd.print((quietTime - (currentMillis - programStartTime)) / 1000);
    }
    
    // if quiet time has passed and total time has not been exceeded
    if (currentMillis >= programStartTime + quietTime) {
      
      // if the current time is still less than the total run time
      if (currentMillis <= totalTime + programStartTime) {
        if ((currentMillis - programStartTime) % 500 == 0) {
          lcd.clear();
          lcd.print("Run Time:");
          lcd.setCursor(10, 0);
          lcd.print((programTime - (currentMillis - programStartTime - quietTime)) / 1000);
        }
       
        // if the interval time has passed
        if (currentMillis - previousMillis >= chopInterval) {
        
          // stores the current time as previous time
          previousMillis = currentMillis;
    
          if (myServo.read() == closedAngle) {
            myServo.write(openAngle);
          } else {
            myServo.write(closedAngle);
          }
        
        }
      
      // the servo returns to original position and the program is stopped
      // when total run time is reached
      } else {
        myServo.write(closedAngle);
        programRunning = !programRunning;
      }
    
    // servo remains in original position if still in quiet time  
    } else {
      myServo.write(closedAngle);
    }
  
  // servo remains in original position when chopper program is not running  
  } else {
    digitalWrite(ledPin, LOW);
    myServo.write(closedAngle);
    
    // the potentiometers that manipulate the time variables
    // quiet time between 0 and 300 seconds
    quietTime = analogRead(quietTimePin);
    quietTime = map(quietTime, 0, 1023, 0, 10);
    quietTime *= 30000;
    
    //chop time between 0 and 300 seconds
    programTime = analogRead(programTimePin);
    programTime = map(programTime, 0, 1023, 1, 10);
    programTime *= 30000;
    
    // chop interval between 1 and 10 seconds
    chopInterval = analogRead(chopIntervalPin);
    chopInterval = map(chopInterval, 0, 1023, 1, 10);
    chopInterval *= 1000;
    
    totalTime = quietTime + programTime;
    
    if (currentMillis % 500 == 0) {
      lcd.clear();
      lcd.print("Program Stopped");
      lcd.setCursor(0, 1);
      lcd.print("Q:");
      lcd.print(quietTime / 1000);
      lcd.print(" R:");
      lcd.print(programTime / 1000);
      lcd.print(" I:");
      lcd.print(chopInterval / 1000);
    }
  }

  // previousSwitchState is assinged to switchState
  previousSwitchState = switchState;
  
  // printing variables to the serial port for diagnostics
  /*
  Serial.print("programStart: ");
  Serial.print(programStartTime / 1000);
  Serial.print(" totalTime: ");
  Serial.print(totalTime);
  Serial.print(" quietTime: ");
  Serial.print(quietTime / 1000);
  Serial.print(" Running: ");
  Serial.print(programRunning);
  Serial.print(" previous: ");
  Serial.print(previousMillis / 1000);
  Serial.print(" current: ");
  Serial.println(currentMillis / 1000);
  */
}

Credits

Brendan Sweeny

Brendan Sweeny

1 project • 3 followers

Comments