Addison interactive butterfly project

I wanted to make a project that’s purpose was almost to imitate life. I love butterflies and I was very inspired by the preserved butterfly displays in museums. I wanted to make a project that looked like it came alive after you shined light on it. I like the idea of it making people who like bugs excited and people who don’t like bugs scared/disturbed. I used foam board to make the frame, watercolor paper and pain to make the butterflies, and two servo motors to make either the left or right side of all three butterflies wings flap when light is shined on the board the butterflies are attached to the servo motors with an extended stick and wire is then wrapped around and glued. If you use a normal powerful flashlight it should work when you shine it just generally in the middle but if you have a poor quality light the sensor is under the middle butterflies species label.

Materials-

  1. Foam board
  2. black and white paint
  3. pencil
  4. cutting tool
  5. paintbrush
  6. wire
  7. pliers/wire cutters
  8. pen
  9. paint markers
  10. watercolor paper
  11. hot glue
  12. paper textured tape

instructions –

  1. Measure and cut out foam board to make shadow box and make sure to cut holes in the back for the butterfly wings.
  2. paint the back white and the frame black
  3. cut out and paint butterfly wings and bodies
  4. glue servo motors with extended horns on the backside of the board
  5. glue wire on the back of the butterfly wings and wrap around each servo rod
  6. attach the body raised up so the wings can flap under
  7. cut hole below a butterfly and cover the hole with the species label
  8. attach Arduino board with light sensor behind the hole

code-

#include <Arduino.h>

#include <Servo.h>

#include <Adafruit_CircuitPlayground.h>

#include <math.h>

int LEFT_SERVO_PIN = A2;

int RIGHT_SERVO_PIN = A3;

Servo leftWing;

Servo rightWing;

// —————- Wing movement settings —————-

int centerAngle = 90;  // starts with wings open / flat

// How wide the wings flap at the start:

float startAmplitudeDeg = 65.0;  // (10 – 30)=smaller flap //(40 – 80)=larger flap

float amplitudeDeg = startAmplitudeDeg;

// How fast the wings flap at the start (flaps per second)

float startFrequencyHz = 1.5;  //(0.5 – 1.0) = slower starting flap, (1.5 – 3.0) = faster starting flap

float flapFrequencyHz = startFrequencyHz;

// The rate at which *the width* of the flap gets smaller. Range about 0.970 – 0.999

float amplitudeDecay = 0.9985;  // Lower number = width diminishes sooner

// How quickly *the flap speed* slows down. Range about 0.970 – 0.999.

float frequencyDecay = 0.9999;  // Lower number = slows down more quickly

// When speed drops below this number, stop. Good range: 2.0 – 20.0

float minAmplitudeDeg = 8.0;

float phaseCycles = .75;  // 0 – 1 = full sine wave cycle (one left-right-left flap)

// How often the servos update (in milliseconds).

int updateEveryMs = 10;  // 5 – 10 = smooth, 15 – 25 = less smooth

unsigned long lastFlapUpdateMs = 0;  

// How long to rest between flap animations

int flapRestMs = 4000;  // 4 sec

// —————- Light sensor setting —————-

// Only restart a new flap cycle if the light sensor is above this level.

int lightThreshold = 200; // 0 – 1023

// ———————————————————

void setup() {

  Serial.begin(115200);

  CircuitPlayground.begin();

  // Servos start centered, then immediately detach

  leftWing.attach(LEFT_SERVO_PIN);

  rightWing.attach(RIGHT_SERVO_PIN);

  leftWing.write(centerAngle);

  rightWing.write(centerAngle);

  leftWing.detach();

  rightWing.detach();

  lastFlapUpdateMs = millis();

}

// ———————————————————

void loop() {

  // ——— ALWAYS CHECK LIGHT FIRST ———

  int lightLevel = CircuitPlayground.lightSensor();

  Serial.print(“Light level: “);

  Serial.println(lightLevel);

  if (lightLevel < lightThreshold) {

    // Too dark → stay still + save battery

    leftWing.detach();

    rightWing.detach();

    return;

  }

  // If bright enough → run a FULL flap cycle

  // (Routine will NOT be interrupted by light checks)

  leftWing.attach(LEFT_SERVO_PIN);

  rightWing.attach(RIGHT_SERVO_PIN);

  leftWing.write(centerAngle);

  rightWing.write(centerAngle);

  // Reset cycle variables

  amplitudeDeg = startAmplitudeDeg;

  flapFrequencyHz = startFrequencyHz;

  phaseCycles = 0.75;

  lastFlapUpdateMs = millis();

  // ——— Flap Routine ———

  while (true) {

    unsigned long now = millis();

    if (now – lastFlapUpdateMs >= (unsigned long)updateEveryMs) {

      float dt = (now – lastFlapUpdateMs) / 1000.0f;  

      lastFlapUpdateMs = now;

      float sinePos = sinf(2.0f * M_PI * phaseCycles);

      float easeFactor = 0.35f + 0.65f * fabsf(sinePos);

      // flapAmount: 0 = wings flat, 1 = wings fully lifted

      float flapAmount = (sinePos + 1.0f) * 0.5f;  

      // Opposite movement

      int leftAngle  = centerAngle + (int)roundf(flapAmount * amplitudeDeg);

      int rightAngle = centerAngle – (int)roundf(flapAmount * amplitudeDeg);

      leftAngle  = constrain(leftAngle,  centerAngle, 160); // movement limits

      rightAngle = constrain(rightAngle, 20, centerAngle); // movement limits

      leftWing.write(leftAngle);

      rightWing.write(rightAngle);

      // advance the sine wave

      phaseCycles += flapFrequencyHz * dt * easeFactor;

      if (phaseCycles >= 1.0f) {

        phaseCycles -= floorf(phaseCycles);

      }

      // slowly shrink flap motion and speed

      amplitudeDeg *= amplitudeDecay;

      flapFrequencyHz *= frequencyDecay;

      // ——— End routine ———

      if (amplitudeDeg < minAmplitudeDeg) {

        break;  // exit the flap loop when flap too small

      }

    }

  }

  // ——— reset wings ———

  leftWing.write(centerAngle);

  rightWing.write(centerAngle);

  leftWing.detach();  // save the motors

  rightWing.detach();

  delay(flapRestMs);  

}

photos-

video- I apologize ahead of time somehow I moved to college without a flashlight so I had to use the flash on my phone.