Reports – 16-223 Work https://courses.ideate.cmu.edu/16-223/f2017/work Introduction to Physical Computing: Student Work Wed, 21 Feb 2018 02:22:37 +0000 en-US hourly 1 https://wordpress.org/?v=4.8.24 Final Report: Insectopia https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/08/final-report-insectopia/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/08/final-report-insectopia/#respond Sat, 09 Dec 2017 04:59:33 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2302 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]> https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/08/final-report-insectopia/feed/ 0 Final Report: HydroKinesis https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-hydrokenisis/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-hydrokenisis/#respond Thu, 07 Dec 2017 02:26:08 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2263 Warren Glasner and Soonho Kwon

12/6/2017

Abstract —

This was a project in which we wanted to create an experience around the Stroboscopic Water Levitation effect. Our mechanism created the illusion of a water stream rising slowly, paused in midair, or falling slowly. We created an interaction in which children and parents could provide their own input and see the resulting water behavior.

We were hoping to involve both parents and their children through this experience, as the parents were more likely to understand the mechanism, and the children were more likely to find wonder in an unexpected phenomena.

The result was surprisingly weighted more heavily towards the parents, where they were intrigued by our project to the point of bringing their children to experience it for themselves.

Objectives —

Our goal was to create a project that would invoke a sense of wonder, disbelief, magic, and ultimately curiosity. For this to happen, we had to spend the time to make sure our water effect was performing as well as possible. It had to be created to be a blatantly noticeable effect. After this, our main objective was to create an intuitive input that would allow people to control the behavior with their own input. This took different forms throughout the project, but we ended up using force sensors.

Implementation —

Our project was designed to generate interest and wonder from both children and their parents. In terms of visual design, we wanted the device to not be particularly flashy or colorful., as we didn’t want to take away from the main effect of the water. As for physical design, we used a light corrugated plastic for ease of transportation, and a piece of acrylic as a splash guard. The water unit consisted of a 6V battery pack, switch circuit, pump, and housing. We tried to hide the pump and electronics as much as possible in order to give the illusion of “magic”. The LED unit consisted of corrugated plastic, acrylic, and our LED apparatus. It was the main point of interaction between user and result, via the force pads found at the front. We provided a small weight for the user to place on the pads so that they could interact with the water stream while a pad was pressed. The default LED state was set to on, illuminating the drops as normal. However, each pad, when pressed, gave a different effect: reverse motion, frozen, and slow motion. This was accomplished by strobing the leds nearly above, at, and nearly below the frequency of water dropping respectively.

We set our device on a low table, but gave it a high viewing angle such that adults could also see what was going on while their kids used the device.

Outcomes —

Overall, the project outcomes met our expectations. The children were drawn to the flashing lights, and seemingly mesmerized by the control that they had over the water. Some, but not all children were inquisitive about how the device worked, which was a very important expectation for us. The adults were also fascinated, and many asked about our design process, which was not necessarily an expectation but was a welcome addition. Some children were keen to interact with the water, however some parents were reluctant to let them get wet. Structurally, our machine held to par, and only required slight adjustments once we got it rolling.

Contribution —

Warren: Electronics, Schematics

Soonho: Housing, Documentation, Drawings

Shared: Fluid Mechanics, Code

Video —

Keep in mind that because our project is highly light-sensitive, it is difficult to film in a clear way. Also, because we use strobes, the camera shutter causes some strange effects.

Working Demo:

At the Museum:

Photos —

Final Apparatus:

Our apparatus was in two parts, the strobe portion and the water stream. This allowed us to play with different positioning to maximize lighting effectiveness.

 

The lighting apparatus had three force sensors. Depending on which one was pressed/activated, the LED panels would strobe at a fixed rate.

At the Museum:

These are pictures of children and parents interacting with our project.

Process Documentation: Housing

Our first prototype.

Our first prototype allowed us to understand the nature of water dripping. We understood the relationship between water pressure and the diaphragm through which the water drips from.

 

Our second prototype.

This prototype was one where we controlled the diaphragm and the water level (water pressure), but we realized that there was too much turbulence from the water tubing.

 

Our third prototype.

This was our third and most complicated prototype. We had a filter system through which the water would behave in a more laminar way, but we realized that we did not account for water tension and decided to shift to a drip straight from the tubing, which led us to the final prototype.

Process Documentation: First Demo

Citations —

ElectroBoom 1

ElectroBoom 2

Additional Materials —

Schematics:

Schematic created by Warren Glasner.

Mechanical Drawings:

All drawings were created by Soonho Kwon.

Housing Schematics

 

Input Housing

Code:


const int outputPin1 = 4;
 const int SPEED = A0;
 //Pressure Pads
 const int pressure1 = A2;
 const int pressure2 = A3;
 const int pressure3 = A5;

const int riseValue = 52;
 const int pauseValue = 49;
 const int fallValue = 56;

void setup() {
 pinMode(outputPin1, OUTPUT);
 pinMode(SPEED, INPUT);

pinMode(pressure1, INPUT);
 pinMode(pressure2, INPUT);
 pinMode(pressure3, INPUT);

Serial.begin(9600);
 }

void loop() {

float value1 = analogRead(pressure1);
 float value2 = analogRead(pressure2);
 float value3 = analogRead(pressure3);

// Serial.println(value1);
 //
 // float del_2= map(value1, 0, 1000, 40, 60);
 ////
 //// float del= map(analogRead(SPEED), 0, 1023, 40, 60);
 // Serial.println(del_2);
 // digitalWrite(outputPin1, HIGH);
 // delay(3);
 // digitalWrite(outputPin1, LOW);
 //// delay(del);
 // delay(45);

// SLOW RISE SETTING
 if (value1 > 100 and value2 < 100 and value3 < 100) {
 digitalWrite(outputPin1, HIGH);
 delay(3);
 digitalWrite(outputPin1, LOW);
 delay(riseValue);
 }

// PAUSE SETTING
 else if (value2 > 100 and value1 < 100 and value3 < 100) {
 digitalWrite(outputPin1, HIGH);
 delay(3);
 digitalWrite(outputPin1, LOW);
 delay(pauseValue);
 }

// SLOW FALL SETTING
 else if (value3 > 100 and value1 < 100 and value2 < 100) {
 digitalWrite(outputPin1, HIGH);
 delay(3);
 digitalWrite(outputPin1, LOW);
 delay(fallValue);
 }

// INACTIVE STATE
 else {
 digitalWrite(outputPin1, HIGH);
 }
 }

 

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-hydrokenisis/feed/ 0
Final Report: Interactive Paper Airplane Wind Tunnel https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-interactive-paper-airplane-wind-tunnel/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-interactive-paper-airplane-wind-tunnel/#respond Wed, 06 Dec 2017 14:17:24 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2227 Interactive Paper Airplane Wind Tunnel

Philip Baker & Evan Hill

12.6.2017

Abstract:

The goal of the project was to create an interactive experience centered around children creating and testing their own paper airplane designs. Our intent changed through each iteration from a focus on airplane creation to an interaction oriented around controlling the airplane to better suit the younger children. The option of creating and flying a paper airplane remained a part of the interaction though we found moving the airplane in the wind tunnel to be the characteristic that captured the attention of the children. For our final installation, we created a wind tunnel that housed a paper airplane mount that allowed the participant to move the airplane side-to-side and up and down using a joystick control. To help capture the attention of children and to aid in initiating an interaction, the airplane mount was programmed to move with an autopilot mode when not in use.

Objectives:

The central idea for the project was a paper airplane wind tunnel. Creating a wind tunnel with interactive computing features was the main goal of the project. Within the wind tunnel enclosure, we aimed to create a mount for the airplane that could move up and down. The airplane’s motion could be controlled with a joystick. Initially, the joystick also controlled the rotation of the fan. The implementation of a control system was intended to create a simplified flight simulator experience. Our next goal was to create a system for monitoring the stability of flight. We originally intended our project to focus on testing different airplane designs so quantitatively measuring and analyzing the airplane’s flight was essential. The final goal for our project was to include visually appealing elements such as lights or movement to attract museum visitors. We intended for these combination of features to enable an interaction that allows children to create paper airplane design and then to fly and test these designs. Our project was intended to be something which could be explained to children while also creating wonder and excitement.

Implementation:

For each aspect of our project we tried to keep the design as simple as possible to reduce the complexity when troubleshooting and modifying the design. The dimensions of the enclosure were chosen in relation to the first fan we used. We built the tunnel body out of a mix of wood and acrylic to ensure stability and enable viewing. The top of the tunnel was hinged to allow for easy access to the airplane mount. The tunnel was modeled in Solidworks to ensure flush connections at the edges of the pieces before they were manufactured. For our first iteration, we designed the wind tunnel to have a rotating fan which was actuated using a servo, a 2:1 gearbox, and a turntable. All of the parts in the fan turntable assembly were created in Solidworks and assembled to check for tolerance issues before laser cutting the physical parts. This helped to ensure a well-constrained gearbox. A 9 inch diameter circular fan was used to create the airstream and a filter using plastic straws was constructed to straighten the flow before entering the tunnel. To simplify the up and down control of the airplane, we designed the airplane mount to be a thin rigid wire mounted to a servo horn. The wire allows the plane to twist and flutter normally while also also for precise positioning. In order to quantify the airplane’s stability we designed a system of servo-mounted rotating arms that are positioned to be slightly higher than the tail end of the airplane. A moving infrared break beam sensor was created by mounting an emitter to one arm and a receiver on the opposing arm. All 3 servos are mounted on a laser cut acrylic stand which was designed for precise motor attachment and alignment.

For our airplane and fan control, we used an Arduino to map the input values from the joystick to a range of degrees that serve as constraints for the servo. Our first control code mapped the joystick input to an angle and moved to that position immediately. This result in very quick and rigid movement. The arms of the IR beam were programmed to move to the same position as the airplane mount servo. We designed the IR arms to be positioned above the tail end of the airplane so that the beam is only broken when the plane is fluttering in the air. Our plan was to count the numbers of beam breaks or ‘flutters’ in a given time period and relate this to the quality of the airplane’s flight. Due to the low resolution of our scanning system (only 1 emitter and 1 receiver) we found it difficult to obtain consistent results for many different airplanes.

Testing showed that rotating the fan produced negligible effects in the flight of the plane so our second iteration of the project switched from a rotating fan to a rotating airplane mount. During our first test at the museum the controlling of the plane captured the children’s attention the most, and this observation drove many of the design modifications for our second iteration. The same gearbox and turntable were used by replacing the fan mount with a base for the airplane mount. New parts were created and imported into the old Solidworks gearbox assembly to check tolerances before manufacturing. Adding rotational control to the airplane mount increased the airplane’s range of motion creating a more dynamic and interesting flight simulation. It also made the interaction more appealing to kids as it makes it easier for them to see the physical effects of their input. We designed and programmed an autopilot mode to move the airplane through a specified pattern when not in use. The autopilot mode was intended to spark children’s curiosity by ensuring our project was always in motion. To aid in the visual appeal of our project, we included LED strips in our second iteration which were programmed to shift through various colors.

We replaced the 9 inch circular fan with a 20 inch box fan in order to increase the airstream power for our second iteration. Once we switched to using a larger box fan we designed a duct by creating an assembly in Solidworks with the tunnel and fan and taking measurements for crucial dimensions. We then used these dimensions to design a duct assembly in Solidworks which would securely funnel air into the wind tunnel.

 

Outcomes:

We accomplished our original goals for the project, but throughout the process we altered these goals and our intentions for the project. We were successful in creating an interactive wind tunnel that had the capability of quantifying flight stability although the flight stability scanner needed some tuning in order to ensure consistent outputs. If we had continued to work on the flight stability scanner we would have had to either have a broad rubric for stability or more scanning inputs to increase the resolution. Many parts in our system performed well at the first test and were simply made to be more presentable for our second iteration. We found during the first testing day that the majority of the children were younger than we expected and that many parts of our planned interaction were not practical for children this age. The characteristics that proved to be too complex were creating a paper airplane that could fly and understanding the flight stability. The young children were not concerned with the quality of the airplane’s flight. All ages showed the most interest in controlling the airplane so we focused on controls for the second iteration. We also included an autonomous flight pattern and lights to increase the visual appeal. Our Arduino shorted on the morning of the final test, so when we wired up the replacement Arduino we decided not to use the lights to protect against potential shorts. On the final test day children of all ages and some parents enjoyed flying the paper airplane in the wind tunnel. Some of the youngest kids simply enjoyed playing with the joystick and paid little attention to the actual airplane. Slightly older kids often tried to move the airplane as far as possible as fast as possible with little to no concern for the quality of the airplane’s flight. Some of the older kids and parents took notice to the quality of flight and tried to control the airplane in a way that maintained steady flight. Every participant seemed to enjoy the experience. The duration of most of our interactions was around 30 seconds so we failed at capturing the children’s attention for an extended period of time. A little over half of the participants attempted to fly the plane by themselves while the others waited to be prompted or asked for permission to do so. While children may have waited to interact for a variety of reasons, one reason may be that the effects of user input in our project were not clearly evident. The autonomous flight pattern and the airplane control created a sense of wonder for many of the children, but our project may have been more successful in holding children’s attention if it had more magical characteristics to it.

 

Contribution:

While we collaborated on all ideas, the actual responsibility for implementation was often divided between us. Evan, with his background as a mechanical engineer, was responsible for the physical components that were created (gearbox, turntable, tunnel, duct). Phil, with his background as an information systems major, was responsible for all wiring and code created.

Media:

First Demo:

This is our original design that we used at the first demo.

Final Demo:

These pictures show users from several age groups demoing our project at The Children’s Museum.

Here is a video displaying the final version of our project in use.

 

Engineering Drawings

Tunnel Enclosure, Air Duct, and Flow Straightener:

TunnelSide

TunnelBack

TunnelFront

TunnelBase

TunnelTop

TensionLink

Duct

 

Gearbox and Airplane Mount Assembly

BasePiece

LegPiece

Side1&3Piece

Side2Piece

Side4Piece

TopPiece

ServoGear3

TurntableGear

PlaneServoMount

RotatingPlaneMount

 

Joystick Enclosure

JoystickMount

JoystickBottom

JoystickBack

JoystickFrontJoystickSide

Source Code

long next_output_time_1 = 0; // timestamp in microseconds for when next to update output 1
long next_output_time_2 = 0; // timestamp in microseconds for when next to update output 2

long flight_interval = 500; // interval in microseconds between output 1 updates
long light_interval = 5; // interval in microseconds between output 2 updates

int output_state_1 = LOW; // current state of output 1
int output_state_2 = LOW; // current state of output 2

#define REDPIN 5
#define BLUEPIN 6
#define GREENPIN 3

#include <Servo.h>

Servo horServo;
Servo vertServo;

int xpin = A0; //yellow
int ypin = A1; //white
int horServoPin = 9;
int vertServoPin = 10;
int xcurr = 90;
int ycurr = 10; // midpoints of acceptable ranges
int xval;
int yval;
int xdeg;
int ydeg;

int xcol;
int ycol;

int r, g, b;
int colorblock;

int mode = 0;
bool xinc = true;
bool yinc = true;

long now;
long last;

void setup() {
  pinMode(xpin, INPUT);
  pinMode(ypin, INPUT);

  pinMode(horServoPin, OUTPUT);
  pinMode(vertServoPin, OUTPUT);

  horServo.attach(horServoPin);
  vertServo.attach(vertServoPin);
  last = micros();
  now = micros();

  colorblock = 0; // for autopilot LED color sequence
  r = 0;
  g = 0;
  b = 0;

  xcol = 0;
  ycol = 0;
}


void loop()
{
  // read the current time in microseconds
  now = micros();

  // Polled task 1 for output 1. Check if the next_output_time_1 timestamp has
  // been reached; if so then update the output 1 state.
  if (now > next_output_time_1) {

    // reset the timer for the next polling point
    next_output_time_1 = now + flight_interval;

    if (now-last > 5000000){
      mode = 0;
    }

    xval = analogRead(xpin);
    yval = analogRead(ypin);

    xdeg = map(xval, 0, 1023, 0, 5);
    ydeg = map(yval, 0, 1023, 0, 5);

    if(xdeg != 2 || ydeg != 2){
      mode = 1;
      last = micros();
    }

    //autopilot flight
    if(mode == 0){
      if (xinc){
        xcurr = xcurr + 1;
      }
      else {
        xcurr = xcurr-1;
      }
      if(yinc){
        ycurr = ycurr + 1;
      }
      else{
        ycurr = ycurr-1;
      }

      // when a limit is reached in the x or y direction, reverse direction of flight
      if (xcurr >= 180){
        xinc = false;
      }
      else if(xcurr <= 20){
        xinc = true;
      }

      if (ycurr >= 70){
        yinc = false;
      }
      else if (ycurr <= 5){
        yinc = true;
      }

      horServo.write(xcurr);
      vertServo.write(ycurr);

    }

    // user is flying the plane
    else if (mode == 1){

      if (xdeg != 2) {
        xcurr = xcurr + (xdeg - 2); // 2 is the midpoint of possible xdeg values
        if (xcurr < 0) xcurr = 0;
        if (xcurr > 190) xcurr = 180;
        horServo.write(xcurr);
      }

      if (ydeg != 2) {
        ycurr =ycurr - (ydeg - 2) ; // // 2 is the midpoint of possible ydeg values
        if (ycurr < 0) ycurr = 0;
        if (ycurr > 60) ycurr = 60;
        vertServo.write(ycurr);
      }

    }
  }

  // Polled task 2 for output 2. Check if the next_output_time_2 timestamp has
  // been reached; if so then update the output 2 state.
  if (now > next_output_time_2) {

    // reset the timer for the next polling point
    next_output_time_2 = now + light_interval;


    // autpilot lighting sequence
    if (mode == 0) {

      switch(colorblock){
      case 0:
        r++;
        analogWrite(REDPIN, r);
        if (r > 256){
          colorblock = 1;
          b = 255;
        }
      case 1:
        b--;
        analogWrite(BLUEPIN, b);
        if (b == 0){
          colorblock = 2;
        }
      case 2:
        g++;
        analogWrite(GREENPIN, b);
        if (g == 256){
          colorblock = 3;
        }
      case 3:
        r--;
        analogWrite(REDPIN, b);
        if (r == 0){
          colorblock = 4;
        }
      case 4:
        b++;
        analogWrite(BLUEPIN, b);
        if (b == 256){
          colorblock = 5;
        }
      case 5:
        g--;
        analogWrite(GREENPIN, b);
        if (g == 0){
          colorblock = 0;
        }
      }
    }


    // user controlled flight lighting
    else if (mode == 1) {
    ycol = map(xcurr, 0, 180, 0, 255);
    analogWrite(BLUEPIN, ycol);
    analogWrite(GREENPIN, ycol);
    analogWrite(GREENPIN, 255);

    }

  }
}

 

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-interactive-paper-airplane-wind-tunnel/feed/ 0
Final Report: Earthquake Simulator https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-earthquake-simulator/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-earthquake-simulator/#respond Wed, 06 Dec 2017 14:07:26 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2224 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]> https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-earthquake-simulator/feed/ 0 Final Report: Ribbon Instrument https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-ribbon-instrument/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-ribbon-instrument/#respond Wed, 06 Dec 2017 13:00:01 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2201 Ribbon Instrument

Lumi

12.06.2017

Abstract

The goal of the Ribbon Instrument was to create a space that children and adults alike could interact with, and through the physicality of the ribbons themselves, and the sounds they triggered, evoke a sense of wonder, discovery, and play through the audio, visual, and tactile environment. The colors and tactility of the ribbons themselves created an inviting piece that allowed for different types of interaction. The audio lent itself to the element of discovery and another level of interaction. These interactions ranged from the intended walk-through and pulling of the ribbons, to children wrapping themselves in the ribbons, not seemingly caring about the sound elements. The final piece integrated into the museum environment as a space that was beautifully described as “an environment where other things can happen”.

Objectives

The goal of the ribbon instrument was to create a space that invited children to interact with, and physically be within, while they triggered sounds to create an ambient audio-scape through the motion of the ribbons. The final piece consisted of 18 ribbons hung on a 6 by 3.5 foot wooden structure suspended in the Maker Space room. The piece used two types of triggers: 12 roller switches, and 6 IR sensors to sense movement of the ribbons themselves to play sound. The entire system was powered by an Arduino attached to my laptop (Velcroed to a beam on the hanging structure).

Implementation

The critical design elements of this project related to the color, scale, and planned interaction with the piece. The final piece consisted of 18 ribbons multicolored ribbons hung about 6 feet above the ground on a 6 by 3.5 foot grid suspended in the Maker Space room. Two different types of ribbon were used to indicate different types of interaction (pull or wave) however the differences were very subtle (smooth versus ribbed) that in this case this difference did not add anything to the piece. Each of the ribbons was about 11 inches from the next creating space between the to walk in, but close enough to catch on to people as they did. The colors themselves served as a huge draw, one young boy saying, “so pretty” as he ran around the space. The use of the thick, slightly textured ribbons, invited people to pull on them, but not to hang on then as ropes would have done. The large scale of the piece lent itself to creating a space of ribbons that people could enter into and travel through. The sounds that were triggered were a range of single notes to short melodies, mostly from marimbas and kalimbas. These created sounds with a softer texture that worked as individual moments, as well as in combination with each other to create a dynamic soundscape.

Outcomes

There were multiple different types of interaction with the piece itself, particularly when it came to the engagement with the sound elements. Some children would walk though the space, pulling ribbons as they went along triggering the sound-scape, in some cases the pulling of the ribbons was prompted by the parents. Often, once a ribbon was pulled and a sound was triggered, a child would continue holding on to that ribbon for a while.

The physicality of the ribbons themselves played a critical role in the interaction with the piece, not only in the tactile click that occurred when the ribbons were pulled down and triggered, but even more so in just the hanging ribbons themselves. Often children would be seen, spinning around in the center of the structure, wrapping themselves in the ribbons, or running through the space, trailing the long strings around them. For them, it didn’t matter that sounds were being triggered, that wasn’t an important part of the experience, but it was the delight in the physicality of it that drew them.

The implementation of the soundscape itself provided a layer of discovery to the piece. Adults and children alike pulled the outer ribbons without being prompted to do so, and were then rewarded with an audio response. This then led to people to walk into the hanging structure itself to pull the other ribbons, triggering new sounds with each one. In some cases, especially with younger children, their parents would discover the sound element first, then prompt their children to pull on the ribbons as well.

It shows that the choice of the colorful ribbons and pull down triggers was a successful one in inviting people into, and to interact with the space created by the instrument.

I also had motion triggers to set off sound using IR sensors on six of the ribbons. While the few that did ultimately work provided a nice addition of sound to those running around in the space who didn’t tug on the ribbons, they also created a discontinuity with the type of interaction with the piece. This became evident when those who were navigating the instrument by tugging on the ribbons came across the IR sensor ribbons and were unable to pull them down in the same way and did not receive an audio response. In this sense, picking on time of intended trigger action would have been more successful. There is room to explore both the direct and ambient motion further.

 

Rhino Model for switches:

 

Max Patch:

LINK

Arduino:

// _________switch set (2)__________
 const int buttonPin2 = 2; 
 
 int buttonState2 = 0;
 int lastButtonState2 = 0;
 int clickSwitchState2 = 0;



// _________switch (3)______________
const int buttonPin3 = 3; 
 
 int buttonState3 = 0;
 int lastButtonState3 = 0;
 int clickSwitchState3 = 0;
 
//_____Switch set input (4)__________
const int buttonPin4 = 4;

int buttonState4 = 0;
 int lastButtonState4 = 0;
 int clickSwitchState4 = 0;

//_____Switch set input (5)__________
const int buttonPin5 = 5;

int buttonState5 = 0;
 int lastButtonState5 = 0;
 int clickSwitchState5 = 0;

//_____Switch set input (6)__________
const int buttonPin6 = 6;

int buttonState6 = 0;
 int lastButtonState6 = 0;
 int clickSwitchState6 = 0;

//_____Switch set input (7)__________
const int buttonPin7 = 7;

int buttonState7 = 0;
 int lastButtonState7 = 0;
 int clickSwitchState7 = 0;

//_____Switch set input (8)__________
const int buttonPin8 = 8;

int buttonState8 = 0;
 int lastButtonState8 = 0;
 int clickSwitchState8 = 0;

//_____Switch set input (9)__________
const int buttonPin9 = 9;

int buttonState9 = 0;
 int lastButtonState9 = 0;
 int clickSwitchState9 = 0;

//_____Switch set input (10)__________
const int buttonPin10 = 10;

int buttonState10 = 0;
 int lastButtonState10 = 0;
 int clickSwitchState10 = 0;

//_____Switch set input (11)__________
const int buttonPin11 = 11;

int buttonState11 = 0;
 int lastButtonState11 = 0;
 int clickSwitchState11 = 0;

//_____Switch set input (12)__________
const int buttonPin12 = 12;

int buttonState12 = 0;
 int lastButtonState12 = 0;
 int clickSwitchState12 = 0;

//_____Switch set input (13)__________
const int buttonPin13 = 13;

int buttonState13 = 0;
 int lastButtonState13 = 0;
 int clickSwitchState13 = 0;







// IR SWITCHES:

const int irInput = A0; // IR
 
 int irClickSwitchState = 0;
 int irButtonState = 0;
 int irLastButtonState = 0;

int val;

//______________________________
 const int irInput1 = A1; // IR
 
 int irClickSwitchState1 = 0;
 int irButtonState1 = 0;
 int irLastButtonState1 = 0;

int val1;

//______________________________
 const int irInput2 = A2; // IR
 
 int irClickSwitchState2 = 0;
 int irButtonState2 = 0;
 int irLastButtonState2 = 0;

int val2;

//______________________________
 const int irInput3 = A3; // IR

int irClickSwitchState3 = 0;
 int irButtonState3 = 0;
 int irLastButtonState3 = 0;

int val3;
 //_______________________________
 const int irInput4 = A4; // IR
 
 int irClickSwitchState4 = 0;
 int irButtonState4 = 0;
 int irLastButtonState4 = 0;

int val4;
//_______________________________
 const int irInput5 = A5; // IR
 
 int irClickSwitchState5 = 0;
 int irButtonState5 = 0;
 int irLastButtonState5 = 0;

int val5;
//_______________________________



bool triggerA0 = false;
 bool triggerA1 = false;
 bool triggerA2 = false;
 bool triggerA3 = false;
 bool triggerA4 = false;
 bool triggerA5 = false;
 
bool trigger = false; 
bool triggerD1 = false;
bool triggerD2 = false;
bool triggerD3 = false;
bool triggerD4 = false;
bool triggerD5 = false;
bool triggerD6 = false;
bool triggerD7 = false;
bool triggerD8 = false;
bool triggerD9 = false;
bool triggerD10 = false;
bool triggerD11 = false;
bool triggerD12 = false;
bool triggerD13 = false;



void setup() {

// pinMode(ledPin, OUTPUT);

pinMode(irInput, INPUT);
 pinMode(irInput1, INPUT);
// pinMode(irInput2, INPUT);
// pinMode(irInput3, INPUT);
// pinMode(irInput4, INPUT);
// pinMode(irInput5, INPUT);
//

pinMode(buttonPin2, INPUT);
 pinMode(buttonPin3, INPUT);
 pinMode(buttonPin4, INPUT);
 pinMode(buttonPin5, INPUT);
 pinMode(buttonPin6, INPUT);
 pinMode(buttonPin7, INPUT);
 pinMode(buttonPin8, INPUT);
 pinMode(buttonPin9, INPUT);
 pinMode(buttonPin10, INPUT);
 pinMode(buttonPin11, INPUT);
 pinMode(buttonPin12, INPUT); 
 pinMode(buttonPin13, INPUT); 
 
 
 Serial.begin(9600);
 
}
void loop() { 
 // DIGITAL INPUTS:______________________________________________________________DIGITAL iNPUTS

//___________button (2):

buttonState2=digitalRead(buttonPin2);

//compare button state aginst previous
 if (buttonState2 != lastButtonState2){
 
 triggerD2 = true;
 
 if (buttonState2 == HIGH) {
 
 clickSwitchState2 = 1;
 }
 else {
 
 clickSwitchState2 = 0;
 }
 }
 lastButtonState2 = buttonState2;

//______________button (3):

buttonState3=digitalRead(buttonPin3);

//compare button state aginst previous
 if (buttonState3 != lastButtonState3){
 
 triggerD3 = true;
 
 if (buttonState3 == HIGH) {
 
 clickSwitchState3 = 1;
 }
 else {
 
 clickSwitchState3 = 0;
 }
 }
 lastButtonState3 = buttonState3;
 
//___________button (4):

buttonState4=digitalRead(buttonPin4);

//compare button state aginst previous
 if (buttonState4 != lastButtonState4){
 
 triggerD4 = true;
 
 if (buttonState4 == HIGH) {
 
 clickSwitchState4 = 1;
 }
 else {
 
 clickSwitchState4 = 0;
 }
 }
 lastButtonState4 = buttonState4;

//___________button (5):

buttonState5=digitalRead(buttonPin5);

//compare button state aginst previous
 if (buttonState5 != lastButtonState5){
 
 triggerD5 = true;
 
 if (buttonState5 == HIGH) {
 
 clickSwitchState5 = 1;
 }
 else {
 
 clickSwitchState5 = 0;
 }
 }
 lastButtonState5 = buttonState5; 
 
//___________button (6):

buttonState6=digitalRead(buttonPin6);

//compare button state aginst previous
 if (buttonState6 != lastButtonState6){
 
 triggerD6 = true;
 
 if (buttonState6 == HIGH) {
 
 clickSwitchState6 = 1;
 }
 else {
 
 clickSwitchState6 = 0;
 }
 }
 lastButtonState6 = buttonState6;

//___________button (7):

buttonState7=digitalRead(buttonPin7);

//compare button state aginst previous
 if (buttonState7 != lastButtonState7){
 
 triggerD7 = true;
 
 if (buttonState7 == HIGH) {
 
 clickSwitchState7 = 1;
 }
 else {
 
 clickSwitchState7 = 0;
 }
 }
 lastButtonState7 = buttonState7;

//___________button (8):

buttonState8=digitalRead(buttonPin8);

//compare button state aginst previous
 if (buttonState8 != lastButtonState8){
 
 triggerD8 = true;
 
 if (buttonState8 == HIGH) {
 
 clickSwitchState8 = 1;
 }
 else {
 
 clickSwitchState8 = 0;
 }
 }
 lastButtonState8 = buttonState8;

//___________button (9):

buttonState9=digitalRead(buttonPin9);

//compare button state aginst previous
 if (buttonState9 != lastButtonState9){
 
 triggerD9 = true;
 
 if (buttonState9 == HIGH) {
 
 clickSwitchState9 = 1;
 }
 else {
 
 clickSwitchState9 = 0;
 }
 }
 lastButtonState9 = buttonState9;

//___________button (10):

buttonState10=digitalRead(buttonPin10);

//compare button state aginst previous
 if (buttonState10 != lastButtonState10){
 
 triggerD10 = true;
 
 if (buttonState10 == HIGH) {
 
 clickSwitchState10 = 1;
 }
 else {
 
 clickSwitchState10 = 0;
 }
 }
 lastButtonState10 = buttonState10;


 //___________button (11):

buttonState11=digitalRead(buttonPin11);

//compare button state aginst previous
 if (buttonState11 != lastButtonState11){
 
 triggerD11 = true;
 
 if (buttonState11 == HIGH) {
 
 clickSwitchState11 = 1;
 }
 else {
 
 clickSwitchState11 = 0;
 }
 }
 lastButtonState11 = buttonState11;


 //___________button (12):

buttonState12=digitalRead(buttonPin12);

//compare button state aginst previous
 if (buttonState12 != lastButtonState12){
 
 triggerD12 = true;
 
 if (buttonState12 == HIGH) {
 
 clickSwitchState12 = 1;
 }
 else {
 
 clickSwitchState12 = 0;
 }
 }
 lastButtonState12 = buttonState12;


 //___________button (13):

buttonState13=digitalRead(buttonPin13);

//compare button state aginst previous
 if (buttonState13 != lastButtonState13){
 
 triggerD13 = true;
 
 if (buttonState13 == HIGH) {
 
 clickSwitchState13 = 1;
 }
 else {
 
 clickSwitchState13 = 0;
 }
 }
 lastButtonState13 = buttonState13;

// ANALOG READ_IR SENSORS: _________________________________________________________________ANALOG 
// ____________(A0)______________:

val = analogRead(A0);
//Serial.println(val);
float avg = avg + 0.1*(val-avg);

if (abs(val-avg)>12){
 irButtonState = HIGH;
 }
 else if (abs(val-avg)>5){
 irButtonState = LOW;
 }
 else if (abs(val-avg)>1){
 irButtonState = HIGH;
 }

if (irButtonState != irLastButtonState){
 triggerA0 = true;
 
 // trigger = true;
 if (irButtonState == HIGH) {
 
 irClickSwitchState = 1;
 }
 else {
 
 irClickSwitchState = 0;
 }
 }
 irLastButtonState = irButtonState;
 
 ____________A1______________:
val1 = analogRead(A1);
float avg1 = avg1 + 0.1*(val1-avg1);

if (abs(val1-avg1)>12){
 irButtonState1 = HIGH;
 }
 else if (abs(val1-avg1)>5){
 irButtonState1 = LOW;
 }
 else if (abs(val1-avg1)>1){
 irButtonState1 = HIGH;
 }

if (irButtonState1 != irLastButtonState1){
 
 triggerA1 = true;
 if (irButtonState1 == HIGH) {
 
 irClickSwitchState1 = 1;
 }
 else {
 
 irClickSwitchState1 = 0;
 }
 //incase of bounce
 // delay(50);
 }
 irLastButtonState1 = irButtonState1;
 ____________A2______________:
val2 = analogRead(A2);
float avg2 = avg2 + 0.1*(val2-avg2);

if (abs(val2-avg2)>12){
 irButtonState2 = HIGH;
 }
 else if (abs(val2-avg2)>5){
 irButtonState2 = LOW;
 }
 else if (abs(val2-avg2)>1){
 irButtonState2 = HIGH;
 }

if (irButtonState2 != irLastButtonState2){
 
 triggerA2 = true;
 if (irButtonState2 == HIGH) {
 
 irClickSwitchState2 = 1;
 }
 else {
 
 irClickSwitchState2 = 0;
 }
 //incase of bounce
 // delay(50);
 }
 irLastButtonState2 = irButtonState2;

____________A3______________:
val3 = analogRead(A3);
float avg3 = avg3 + 0.1*(val3-avg3);

if (abs(val3-avg3)>12){
 irButtonState3 = HIGH;
 }
 else if (abs(val3-avg3)>5){
 irButtonState3 = LOW;
 }
 else if (abs(val3-avg3)>1){
 irButtonState3 = HIGH;
 }

if (irButtonState3 != irLastButtonState3){
 
 triggerA3 = true;
 if (irButtonState3 == HIGH) {
 
 irClickSwitchState3 = 1;
 }
 else {
 
 irClickSwitchState3 = 0;
 }
 //incase of bounce
 // delay(50);
 }
 irLastButtonState3 = irButtonState3;

____________A4______________:
val4 = analogRead(A4);
float avg4 = avg4 + 0.1*(val4-avg4);

if (abs(val4-avg4)>12){
 irButtonState4 = HIGH;
 }
 else if (abs(val4-avg4)>5){
 irButtonState4 = LOW;
 }
 else if (abs(val4-avg4)>1){
 irButtonState4 = HIGH;
 }

if (irButtonState4 != irLastButtonState4){
 
 triggerA4 = true;
 if (irButtonState4 == HIGH) {
 
 irClickSwitchState4 = 1;
 }
 else {
 
 irClickSwitchState4 = 0;
 }
 //incase of bounce
 // delay(50);
 }
 irLastButtonState4 = irButtonState4;



____________A5_____________:
val5 = analogRead(A5);
float avg5 = avg5 + 0.1*(val5-avg5);

if (abs(val5-avg5)>12){
 irButtonState5 = HIGH;
 }
 else if (abs(val5-avg5)>5){
 irButtonState5 = LOW;
 }
 else if (abs(val5-avg5)>1){
 irButtonState5 = HIGH;
 }

if (irButtonState5 != irLastButtonState5){
 
 triggerA5 = true;
 if (irButtonState5 == HIGH) {
 
 irClickSwitchState5 = 1;
 }
 else {
 
 irClickSwitchState5 = 0;
 }
 //incase of bounce
 // delay(50);
 }
 irLastButtonState5 = irButtonState5;



//___________sPrint_______

if (
(triggerA0 == true) ||
(triggerA1 == true) || 
(triggerA2 == true) ||
(triggerA3 == true) ||
(triggerA4 == true) ||
(triggerA5 == true) ||

(triggerD2 == true) || 
(triggerD3 == true) ||
(triggerD4 == true) ||
(triggerD5 == true) ||
(triggerD6 == true) ||
(triggerD7 == true) ||
(triggerD8 == true) ||
(triggerD9 == true) ||
(triggerD10 == true)||
(triggerD11== true) ||
(triggerD12== true) ||
(triggerD13 == true))

{

//row1
 Serial.print(buttonState2);
 Serial.print(" ");

Serial.print(buttonState3);
 Serial.print(" ");

Serial.print(buttonState4);
 Serial.print(" ");
 
 //row2

Serial.print(buttonState5);
 Serial.print(" ");
 
 Serial.print(buttonState6);
 Serial.print(" ");
 
 Serial.print(buttonState7);
 Serial.print(" ");
 //row3
 
 
 Serial.print(buttonState8);
 Serial.print(" ");
 Serial.print(buttonState9);
 Serial.print(" ");
 Serial.print(buttonState10);
 
 //row 4
 
 Serial.print(" ");
 Serial.print(buttonState11);
 Serial.print(" ");
 Serial.print(buttonState12);
 Serial.print(" ");
 Serial.print(buttonState13);
 Serial.print(" ");
 //IR

Serial.print(irClickSwitchState);
 Serial.print(" ");
 Serial.print(irClickSwitchState1);
 Serial.print(" ");
 Serial.print(irClickSwitchState2);
 Serial.print(" ");
 Serial.print(irClickSwitchState3);
 Serial.print(" ");
 Serial.print(irClickSwitchState4);
 Serial.print(" ");
 Serial.println(irClickSwitchState5);

triggerA0 = false;
 triggerA1 = false;
 triggerA2 = false;
 triggerA3 = false;
 triggerA4 = false;
 triggerA5 = false;


triggerD2 = false;
 triggerD3 = false;
 triggerD4 = false;
 triggerD5 = false;
 triggerD6 = false;
 triggerD7 = false;
 triggerD8 = false;
 triggerD9 = false;
 triggerD10 = false;
 triggerD11 = false;
 triggerD12 = false;
 triggerD13 = false;
// Serial.println(val);



}
 delay(50);
}

 

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-ribbon-instrument/feed/ 0
Final Report: Tricky Ball Drop https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-tricky-ball-drop/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-tricky-ball-drop/#respond Wed, 06 Dec 2017 08:06:05 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2176 Tricky Ball Drop

Annabelle Swain and Zachary Rapaport

December 6, 2017

Abstract

Tricky Ball Drop was created in response to the question, what does an interactive children’s’ toy look like in the context of the Children’s Museum of Pittsburgh and what features make it appealing to children of all ages? With this question in mind, the Tricky Ball Drop has many layers of involvement, providing young children with tangible interaction and older children with a mental challenge. This binary relationship between sophisticated and unsophisticated contributes to the success of the piece. Among young children, the interchangeable parts and gears elicit joy and engagement. The use of sensors, a responsive system, and goal-oriented design holds the attention if the older kids. This synthesis is ultimately how the piece achieved its goal of appealing to the many ages of children that visit the museum.

Provide a brief paragraph summarizing the overall goals and results.

The goal of this project was to create an experience wherein museum visitors of all ages would be able to appreciate and enjoy our piece. We combined computation with a durable, tangible wooden design that would allow for interaction among children, young and old. The more sophisticated elements, such as the ball-maze aspect, the responsive motor-driven pieces, and “trickiness” of the piece catered to children aes nine and older. The elements that were purely tangible, such as the interchangeable parts and the ability to spin the gears was more apt for younger children. The goal, which was to create a multi-layered device for all children, succeeded in engaging museum visitors. As expected, the older children found joy and wonder in the maze-like qualities and responsiveness of the device. The younger children, which there were many more of, enjoyed the 3D laser-cut shapes and tactile elements.

Objectives

Foremost, the objective of this piece was to create and experience that appealed to the visitors of the Children’s Museum of Pittsburgh, specifically children. The age range that we were targeting was 3 to 12. The piece was designed with this range of ages in mind, as children that fall within these ages vary greatly in terms of cognitive and physical ability. For that reason, this piece incorporates elements that appeal to and hold the attention of children of different ages. Physically speaking, the ability to directly interact with the gears and interchangeable parts of this piece appeals to children on the younger end of the range. On the other hand, the use if computation and sensors adds sophistication to the piece that is suitable for the older children. Another objective was to integrate computation, design, and fabrication to create a seamless and engaging experience. Our ability to engender wonder relied on our ability to execute these goals.

State your goals, and discuss what specific features are within scope for the project.

  1. Create a project that engages both younger and older children.
  2. Seamlessly implement software and hardware into a piece that looks to be completely made from wood.
  3. Hold the attention of the children that visit our project.
  4. Make a “tricky” machine.
  5. The use of photointerrupters, a stepper motor, a gear system, and interchangeable parts are included within the scope of this project.

Implementation

  1. Over the course of a month, we built this piece with a number of key milestones in minds. The first was a proof-of-concept demonstration. This was a crucial step in determining the viability of our system. Would the gears work? Would the photointerrupter function proper? Was a stepper motor the right choice? Would a ball reliably travel on top of a gear? In trying to answer these questions, we encountered many setbacks, which helped us make better-informed decisions moving forward. Our second phase was preparing for our first demonstration museum visit. We had built a working prototype, similar to the final iteration, and displayed it for two hours. After that experience we determined a number future plans. We would incorporate interchangeable piece, use light to our advantage, and make changes to the overall design. The final milestone was the second demonstration at the museum.
  2. We began the process of building the piece by creating CAD drawings in Rhino. We modeled the system and then proceeded to laser cut the pieces from plywood. Often, the addition of a new part or a design change would result in us cutting new pieces or modifying the current pieces. The incorporation of the stepper motor and sensors happened in tandem with the fabrication of the piece. It was necessary to plan for, and install them as it was being built. The code to run the arduino program happened separately, though at the same time.

Discuss your design choices.

We choose to build our piece entirely out of wood to give it the effect of a hand-made toy. Of course, the hardware and LEDs were not made of wood, but the entire structure was. We had two layers of gears: a bottom, hand-spun layer and a top, motor-driven layer. The intention was to design a piece that had the ability to respond to interaction without it being intrusive. The piece was modeled in Rhino and assembled by hand.

Outcomes

  1. The final iteration of the piece succeeded in attracting many children and preserving their attention. This success in measured partly in comparison to the first demonstration at the museum and partly in relation to our own expectations. On average, children spent between one and two minutes with the piece. As expected, the younger kids were more interested in the physical aspects of the piece: puzzle pieces and gear. The older children, who ended up staying significantly longer, took pleasure in the sophistication and challenge the piece provided.
  2. The insecure connections between wires posed a problem at the museum. The wires, specifically those for the photointerrupters and LEDs would fall out of place, causing them to stop working of turn off.
  3. Our goal to increase the number of people interacting with our piece as well as the length of interaction succeeded on our second visit. Visitors were drawn to our piece as a result of the constant movement of the interchangeable pieces and the incorporation if LEDs. The use the interchangeable puzzle pieces helped to increase interaction, among both younger and older children. However, the way different aged children used the pieces was different. Older children were using them strategically, often swapping out pieces to further their goal in completing the maze. Younger children were more interested in the tactility of the pieces. We observed them being stacked and waved around. We noticed similar interactions in the first visit to the museum, which influenced our decision to provide more of the pieces.

Successes:

  1. The piece successfully kept children engaged (an average of 1 minute)
  2. The piece worked properly, both physically and in the code (though faulty wiring led to some issues)
  3. The piece was child-durable and lasted the duration of the exhibition
  4. The LEDs and constant movement succeeded in attracting children over to the piece
  5. The older children did find the piece to be “tricky,” often spending significantly more time with the piece than the younger children

Failures:

  1. The wiring was not secure and parts came undone during transport
  2. The photointerrupters were unreliable
  3. The code for tracking “sweeps” was did not prove to be robust
  4. The hand-spun gears were underneath the large motor-driven gears, which proved to be an unintuitive design flaw

Contribution

We worked closely with one another to accomplish the outcome of the piece. Annabelle’s knowledge of Rhino was highly valuable and she took on the responsibility of creating CAD drawing. Zachary was more responsible for the hardware and software aspects of the piece. All other contributions, including assembly, testing, documentation, were split more-or-less equally.

Please provide a clear statement of each author’s individual contribution to the outcomes.

Both of us contributed to the design and implementation of the project. Annabelle was individually responsible for creating the CAD files. Zachary was responsible for using those files to make the laser-cut parts. We were each engaged with the assembly. Zachary was responsible for programming the motor, LEDs, and sensors. Throughout the process, we worked closely and mostly in tandem.

Moving pieces, interchangeable parts, LEDs, and gears were inviting for visitors.

 

The piece was situated in the Garage of the Children’s Museum.

Schematic

Code:

</pre>
// Copyright (c) 2016, Garth Zeglin. All rights reserved. Licensed under the
// terms of the BSD 3-clause license as included in LICENSE.
// MOTOR: https://courses.ideate.cmu.edu/16-223/f2017/text/ex/Arduino/stepper-motor/stepper-motor.html#exercise-stepper-motor
// PHOTO INT: http://www.martyncurrey.com/connecting-an-photo-interrupter-to-an-arduino/
// LIGHTS: code: https://learn.adafruit.com/rgb-led-strips/example-code
// set up: http://www.makeuseof.com/tag/connect-led-light-strips-arduino/
//================================================================================

// STEPPER MOTOR
#define DIR_PIN 2 // The direction pin controls the direction of stepper motor rotation.
#define STEP_PIN 3 // Each pulse on the STEP pin moves the stepper motor one angular unit.

// PHOTO INTERRUPTER
int ruptPin1 = 2; // select the input pin for the interrupter
int ruptPin2 = 3; // select the input pin for the interrupter
int PI_val1 = 0; // variable to store the value coming from the sensor
int PI_val2 = 0; // variable to store the value coming from the sensor
int sweeps = 0; // number of rotations the gear has made
int high_val = 25;
int low_val = 15;
int prev_state = 0; // alternates between low (0) and high (1)

//LEDS
#define redPin 11
#define greenPin 10
#define bluePin 9

#define FADESPEED 5 // make this higher to slow down

//Button
int inPinButton = 4; // the number of the input pin
int reading;

// ================================================================================

void setup(void)
{
// Initialize the stepper driver control pins to output drive mode.
pinMode(DIR_PIN, OUTPUT);
pinMode(STEP_PIN, OUTPUT);

// Initialize the serial UART at 9600 bits per second.
Serial.begin(9600);

//LEDS
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);

//Button
pinMode(inPinButton, INPUT);
}
/****************************************************************/
void rotate_stepper(int steps, float speed)
{
// Configure the direction pin on the stepper motor driver based on the sign
// of the displacement.
int dir = (steps > 0)? HIGH:LOW;
digitalWrite(DIR_PIN, dir);

// Find the positive number of steps pulses to emit.
int pulses = abs(steps);

// Compute a delay time in microseconds controlling the duration of each half
// of the step cycle.
// microseconds/half-step = (1000000 microseconds/second) * (1 step/2 half-steps) / (steps/second)
unsigned long wait_time = 500000/speed;

// The delayMicroseconds() function cannot wait more than 16.383ms, so the
// total delay is separated into millisecond and microsecond components. This
// increases the range of speeds this function can handle.
unsigned int msec = wait_time / 1000;
unsigned int usec = wait_time - (1000*msec);

// Loop for the given number of step cycles. The driver will change outputs
// on the rising edge of the step signal so short pulses would work fine, but
// this produces a square wave for easier visualization on a scope.
for(int i = 0; i < pulses; i++) {
digitalWrite(STEP_PIN, HIGH);
if (msec > 0) delay(msec);
if (usec > 0) delayMicroseconds(usec);

digitalWrite(STEP_PIN, LOW);
if (msec > 0) delay(msec);
if (usec > 0) delayMicroseconds(usec);
}
}

//https://learn.adafruit.com/adafruit-arduino-lesson-3-rgb-leds/arduino-sketch
void setColor(int red, int green, int blue)
{
#ifdef COMMON_ANODE
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
#endif
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
}

// ================================================================================
// Run one iteration of the main event loop. The Arduino system will call this
// function over and over forever.
void loop(void)
{

//PI
PI_val1 = analogRead(ruptPin1); // read the value from the sensor 1
PI_val2 = analogRead(ruptPin2); // read the value from the sensor 2

Serial.println(reading);

//Button
reading = digitalRead(inPinButton);

if (prev_state == 0) { //Low Value
if ((PI_val1 == high_val) or (PI_val2 > high_val)) {
prev_state = 1;
sweeps = (sweeps + 1);
delay(500);
}
}

else { //High Value
if ((PI_val1 == low_val) or (PI_val2 < low_val)) {
prev_state = 0;
sweeps = (sweeps + 1);
}
}

{
if (sweeps == 0) {
setColor(0, 255, 0); // green
}
if (sweeps == 1) {
setColor(50, 255, 0);
}
if (sweeps == 2) {
setColor(100, 255, 0);
}
if (sweeps == 3) {
setColor(150, 255, 0);
}
if (sweeps == 4) {
setColor(200, 255, 0);
}
if (sweeps == 5) {
setColor(255, 255, 0); // yellow
}
if (sweeps == 6) {
setColor(255, 200, 0);
}
if (sweeps == 7) {
setColor(255, 150, 0);
}
if (sweeps == 8) {
setColor(255, 100, 0);
}
if (sweeps == 9) {
setColor(255, 60, 0);
}
if (sweeps == 10) {
setColor(255, 30, 0);
}
if (sweeps == 11) {
setColor(255, 0, 0); // red
}
}

if (reading == 1) {

if (sweeps == 0) {
rotate_stepper( -1, 20.0);
}
if (sweeps == 1) {
rotate_stepper( -1, 25.0);
}
if (sweeps == 2) {
rotate_stepper( -1, 25.0);
}
if (sweeps == 3) {
rotate_stepper( -1, 35.0);
}
if (sweeps == 4) {
rotate_stepper( -1, 35.0);
}
if (sweeps == 5) {
rotate_stepper( -1, 45.0);
}
if (sweeps == 6) {
rotate_stepper( -1, 45.0);
}
if (sweeps == 7) {
rotate_stepper( -1, 55.0);
}
if (sweeps == 8) {
rotate_stepper( -1, 55.0);
}
if (sweeps == 9) {
rotate_stepper( -1, 65.0);
}
if (sweeps == 10) {
rotate_stepper( -1, 65.0);
sweeps = 0;
}
}}

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/final-report-tricky-ball-drop/feed/ 0
Message in a Bottle https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/message-in-a-bottle/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/message-in-a-bottle/#respond Wed, 06 Dec 2017 06:49:20 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2068 Message in a Bottle
Jarrek Holmes, Joshua Danzig

Abstract

The goal of Message in a Bottle was to provide an interactive exhibit where children could listen to and record messages in a tactile experience. We wanted to create a project that was engaging, inspired wonder, and highlighted communication and social connectivity. At the final presentation, we were able to achieve these goals by having an internally wired jar that could record and play back messages, while also being able to save those message by closing the lid. The best interactions occurred when children realized their own voice was coming from the jar, that moment of “My voice is in there!” and then sharing that experience with others around them.

Objectives 

We had a primary goal of inspiring wonder in children, and sub-goals of creating an aesthetic environment, inspiring social connectivity, and achieving the technical successes required to assemble the physical bottle. This leads to a few critical project features that we leveraged to achieve these goals. The first was the jar itself; by having a tactile object that could be interacted with, had beautiful and intriguing lighting elements, and ‘wonderous’ functionality we were able to create moments where children could share something captivating with their friends. The second keystone feature is the cart. By having a cart full of sand, jars, and interesting light patterns we were able to draw children’s attention and begin the entire interaction. The more complete and aesthetic the project looked, the easier it was to bring in and retain a child’s attention. This contributed directly to our goals, because it was crucial for creating that aesthetic environment and creating a situation where social connectivity could occur.

Implementation

There were  a number of critical design choices that we made that supported the objectives of our project. The first was to have jars be independent from one another, giving children the opportunity to pick up and move the jars around without any wires or other hindrances. This ended up being beneficial to the project, but extremely difficult to achieve. As one can see from our CAD drawings attached below, although the jars were large, space issues played a major role in the frustrations we experienced assembling the entire project. There was a lot of hardware (battery packs, Neopixel rings, arduino, speakers, microphone, etc.) and having each jar have all of those components inside was simply difficult to pull off. The second major design choice we made was behavioral when we decided to staff the project instead of allowing children to interact autonomously. This resulted in the two of us approaching children and parents and asking them to interact with the exhibit. When children did begin the experience, however, we were able to explain the nuances of the project and facilitate that moment of “My voice is in there!” much better than when the project was autonomous. This choice did take away from a child’s sense of accomplishment from discovering the jars’ functionality on their own, but we saw many more successful interactions when we staffed the project than during autonomous interaction. The final critical design choice was, once again, the cart itself. The cart allowed us to move the project around the museum space or station ourselves in a single location and allow children to approach us. Each experience had positives and negatives. Moving the cart around meant that we needed to approach museum-goers to truly get an interaction, but allowed us to interact with children in a number of different settings. Stationing the cart meant we were approached more often, but it limited our ability to spread messages from one side of the museum to the other. Ultimately, these three critical design choices supported our ultimate objectives, and each contributed positives and negatives to the overall experience.

Outcomes

There were a number of successful outcomes to our project. The central success factors revolve around a number of interactions where children were excited to hear their own voice echoed out of the jar. One child shouted their name in the jar half a dozen times, just to hear it repeated back to them. Another pulled their parent over to listen a message she had saved. As well, our final project had a beach aesthetic that was quite successful. The lights were beautiful in the darkness  of the hallways, and the sand immediately attracted the attention of museum guests. Finally, our project was able to generate moments of social connectivity when children would share with their parents or other children a message they had saved or an echo of their own voice. Having increased the volume of the jar from the first iteration was extremely successful since it was able to capture a room’s attention and bring about more interaction.

Nevertheless, there were a number of outcomes that weren’t as desired. We had attempted an extremely ambitious project, with an initial goal of nine fully functional jars. This meant that we had to order an enormous amount of parts and had to attempt to assemble nine units concurrently. Because of this, we a) weren’t able to complete as many jars as hoped for and b) were unable to test the jars adequately before launch. This meant that we had button malfunctions during the actual presentation, losing at least one interaction to the hardware issue. We were also using untested hardware, which meant that, as we assembled the jars, we found new, unexpected problems. For example, the speakers we had been using had a lot of static, and it diminished the experience. Museum goers sometimes struggled to hear their echoes, and the sound wasn’t always pleasing.

Regardless, of the multitude of successes and failure, we learned a lot, both technically and in terms of project management and assembly.

Contribution

Jarrek:

  1. Complete software development, from start to finish.
  2. Circuitry work by bread-boarding prototypes, researching new hardware data sheets, and creating wiring configuration for final project.
  3. Ancillary work on fabrication (e.g. soldering, crimping, laser cutting)
  4. Primary lead on documentation

Josh:

  1. Primary fabrication work, both digital and physical (e.g. developing CAD drawings, laser cutting pieces)
  2. Final project assembly by affixing pieces, building wire harnesses, and arranging final project
  3. Hardware research and acquisition

Video

Additional Photos

Child Interactions:

 

Getting ready, morning of project demo!

 

 

Jar Internals

CAD Drawings

Project Code

Primary jar programming
#include &lt;Adafruit_NeoPixel.h&gt;
#ifdef __AVR__
#include &lt;avr/power.h&gt;
#endif

#define PIN 3

int rec = 6;
int pl = 7;

&amp;nbsp;

int break_beam = 2;
boolean has_recording = false;
long long just_spoke;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(60, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
Serial.begin(9600);
pinMode(rec, OUTPUT);
pinMode(pl, OUTPUT);
pinMode(break_beam, INPUT);

#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code

&amp;nbsp;

strip.begin();
strip.show(); // Initialize all pixels to 'off'
just_spoke = millis();
}

void record(){
Serial.println("Recording");
long time = millis();
digitalWrite(rec, HIGH);

theaterChase(strip.Color(127, 0, 0), 100); // Red

digitalWrite(rec, LOW);
Serial.println("Stop Recording");
}

void speak(){
Serial.println("Speaking");
delay(1000);
digitalWrite(pl, HIGH);

rainbow(10);

digitalWrite(pl, LOW);
Serial.println("Stop Speaking");
just_spoke = millis();

}

void loop() {
if(has_recording and digitalRead(break_beam)){
speak();
if(!digitalRead(break_beam)){
has_recording = true;
}
else{
has_recording = false;
}
}
else if(!has_recording and digitalRead(break_beam) and millis() &gt; just_spoke + 1000){
record();
has_recording = true;
}
else if(!digitalRead(break_beam) and millis() &lt; just_spoke + 1000){
has_recording = true;
}
else if(has_recording){
int brightness = 0;
float my_time = millis() % 2000;
if (my_time &gt; 1000){
brightness = map(millis() % 1000, 1000, 0, 0, 180);
}
else{
brightness = map(millis() % 1000, 0, 1000, 0, 180);
}
colorWipeNoWait(strip.Color(brightness, brightness, brightness, 255));
}
else{
Green(); // Red
}
}



void rainbow(uint8_t wait) {
uint16_t i, j;

for(j=0; j&lt;256; j++) {
for(i=0; i&lt;strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel((i+j) &amp; 255));
}
strip.show();
delay(wait);
}
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
uint16_t i, j;

for(j=0; j&lt;256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i&lt; strip.numPixels(); i++) {
strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) &amp; 255));
}
strip.show();
delay(wait);
}
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j&lt;10; j++) { //do 10 cycles of chasing
for (int q=0; q &lt; 3; q++) {
for (uint16_t i=0; i &lt; strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, c); //turn every third pixel on
}
strip.show();

delay(wait);

for (uint16_t i=0; i &lt; strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
for (int j=0; j &lt; 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q &lt; 3; q++) {
for (uint16_t i=0; i &lt; strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, Wheel( (i+j) % 255)); //turn every third pixel on
}
strip.show();

delay(wait);

for (uint16_t i=0; i &lt; strip.numPixels(); i=i+3) {
strip.setPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}

void colorWipeNoWait(uint32_t c) {
for(uint16_t i=0; i&lt;strip.numPixels(); i++) {
strip.setPixelColor(i, c);
}
strip.show();

}

void Green(){
strip.setPixelColor(0, 0, 5, 0);
strip.setPixelColor(1, 5, 0, 0);
strip.setPixelColor(2, 0, 0, 5);
strip.setPixelColor(4, 0, 5, 0);
strip.setPixelColor(5, 5, 0, 0);
strip.setPixelColor(6, 0, 0, 5);
strip.setPixelColor(8, 0, 5, 0);
strip.setPixelColor(9, 5, 0, 0);
strip.setPixelColor(10, 0, 0, 5);
strip.setPixelColor(12, 0, 5, 0);
strip.setPixelColor(13, 5, 0, 0);
strip.setPixelColor(14, 0, 0, 5);
strip.setPixelColor(16, 0, 5, 0);
strip.setPixelColor(17, 5, 0, 0);
strip.setPixelColor(18, 0, 0, 5);
strip.setPixelColor(20, 0, 5, 0);
strip.setPixelColor(21, 5, 0, 0);
strip.setPixelColor(22, 0, 0, 5);
strip.setPixelColor(24, 0, 5, 0);
strip.setPixelColor(25, 5, 0, 0);
strip.setPixelColor(26, 0, 0, 5);
strip.setPixelColor(28, 0, 5, 0);
strip.setPixelColor(29, 5, 0, 0);
strip.setPixelColor(30, 0, 0, 5);
strip.show();
}


// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if(WheelPos &lt; 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if(WheelPos &lt; 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

‘Dummy’ jars programming


int led1 = 3;
int led2 = 5;
int led3 = 6;

int brightness1 = 0;
int brightness2 = 50;
int brightness3 = 125;

int fadeAmount1 = 5;
int fadeAmount2 = 7;
int fadeAmount3 = 3;

void setup() {
 pinMode(led1, OUTPUT);
 pinMode(led2, OUTPUT);
 pinMode(led3, OUTPUT);

}

void loop() {
 if(millis() % 6000 &amp;lt; 2000){
 analogWrite(led2, 0);
 analogWrite(led3, 0);
 
 if(millis() % 2000 &amp;lt; 1000){
 brightness1 = map(millis() % 1000, 0, 1000, 0, 255);
 }
 else{
 brightness1 = map(millis() % 1000, 0, 1000, 255, 0);
 }
 analogWrite(led1, brightness1);
 }
 
 else if(millis() % 6000 &amp;lt; 4000){
 analogWrite(led1, 0);
 analogWrite(led3, 0);
 
 if(millis() % 2000 &amp;lt; 1000){
 brightness2 = map(millis() % 1000, 0, 1000, 0, 255);
 }
 else{
 brightness2 = map(millis() % 1000, 0, 1000, 255, 0);
 }
 analogWrite(led2, brightness2);
 }

else{
 analogWrite(led2, 0);
 analogWrite(led1, 0);
 if(millis() % 2000 &amp;lt; 1000){
 brightness3 = map(millis() % 1000, 0, 1000, 0, 255);
 }
 else{
 brightness3 = map(millis() % 1000, 0, 1000, 255, 0);
 }
 analogWrite(led3, brightness3);
 }
}

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/06/message-in-a-bottle/feed/ 0
Final Project Reports – Fragment https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/final-project-reports-fragment/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/final-project-reports-fragment/#respond Wed, 06 Dec 2017 01:18:50 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2085 Fragment

Alessandra Fleck and Christina Brown

December 5th, 2017

Abstract

This project aimed to intrigue and inspire curiosity through the fragmentation and movement of light through the activation of a large artistic geometric form in the Children’s Museum lobby. The motion of the piece is affected by two PIR sensors that picks up motion of lobby passerby to add a level of complexity and reaction to its environment that could further inspire curiosity in children.  The project involves two main parts- a stepper/gear mechanism with a marble ball bearing to allow for piece activation, and a PIR software system that dictates how the information from the PIR would affect the piece’s movements.

The project was successful in capturing the curiosity of children during our museum installation on Saturday, but the interaction was limited due to the nature of the location. To our surprise, we captured the interest of people from the ledge (close to Maker space/stairs), and perhaps more interaction with people from that space could have been further investigated. The hardware of the project also failed after two hours of operation due to the tolerances of the stepper to gear connection, which made it difficult for the piece to run properly afterwards.

Objectives

The goals of this project ultimately was aimed to rotate a geometrically complex acrylic structure with variation in its motion based off of PIR sensor feedback. In order to achieve this, smaller goals were set:

  • Program that takes in PIR input and outputs some kind of variation in the rotation of a stepper motor, when the input is null, the stepper rotates at a constant default rate
  • A geometrically complex acrylic structure that can bounce and fragment light into the environment
  • Mechanical structure that can translate the stepper rotation into the rotation of the acrylic structure through the use of gears
Implementation

In order to achieve our goals stated above, the following design choices were implemented.

For Software:

  • Code that programs the stepper to rotate at a slow constant rate, and then when PIR senses change in environment (caused by motion), it causes the stepper to change direction and rotate at a faster speed for a fixed period of time before going back to its default constant rate and direction
  • Wiring of the different components
  • Soldering to extend the length of the PIR  wires to extend its length to the railing in the lobby

For hardware:

  • Wooden stepper shaft to gear connection that is rigid enough to support the high rotation loads
  • Stepper gear to Prism Gear connection on a structural base with correct leveling and tolerance to each other to support the desirable motion
  • Stepper enclosure that holds the stepper and inset it relative to the gears to allow for the stepper gear and prism gear to be flush with one another.
  • Geometric acrylic prisms that are bolted to one another with colorful metallic reflective strips that both fragments light and bounces color, but also visually provides an eye catching piece that is three-dimensionally complex to allow for the light to bounce in a more interesting and less predictable manner
  • Marble ball bearing that can minimize the friction in rotation of the acrylic prisms to lessen the amount of power needed in rotating the piece with the stepper motor
Outcomes

Originally the scale of the project was much larger with multiple acrylic prism structures being rotated relative to each other. Unfortunately, the budget and timeline of the project did not allow for us to take on so much (especially considering the expense of the acrylic itself). Thus, the project was scaled down to only have one prism structure that reacted to its surroundings. This decision was ultimately a good choice considering the amount of time, resources, and effort it took to actuate one prism structure, and a larger scale would have increased the chances of failure. The design of the acrylic prisms structure itself however was very successful. Upon it’s completion, no changes/concerns were presented for it was able to fragment light and was visually stunning enough to peak curiosity.

The mechanical design was overall functional, though many tweaks could have been made to make the overall design function more smoothly. The ball bearing had a slight unevenness that occasionally affected the gear to gear connection (only clearly evident when the rotation is at an extremely high speed). The base had to be adjusted/slightly warped to accommodate the unevenness of the ball bearing relative to the uneven distribution of weight in the prism structure itself (due to its geometrically complex form). The stepper rattled against the wooden structure, and caused an almost clock-like sound, which added a mechanical quality to the piece that added character and emphasized the slow rotation of the piece. The stepper shaft to gear connection performed exactly as intended, but after constant wear and tear after a week of testing, the connection had chipped away and broke during the actual museum exhibit, thus cutting our Saturday experience short. Our on site temporary solution of wedging tape into the gap between the stepper shaft and connection piece did not work well, and  would only allow for the piece to function for a short while before malfunctioning again.

The software performed as desired, though tweaks could have been made to make the interaction more nuanced, and the overall progression of the code could have been faster to allow for more testing of the overall system along with the code itself. The PIR sensors only provided a fairly binary input, and thus limited the potential for more explicit interaction between the museum visitors and the reaction of the prisms itself.

Contribution

Alessandra:

  • First trial of sensor to motion activated device using an ultrasonic sensor and a servo. Discovered that the distance detected by the ultrasonic was too weak and that the servo’s limit in rotation would make it difficult to  achieve the smooth motion that was desired
  • First basic code and circuit that activates the heavier duty stepper motor
  • Investigated various sensors (ultrasonic and IR sensors) as well as writing script and wiring the circuits to test them. Eventually settled upon using PIR, and then researched into how a PIR works
  • Purchased PIR sensors and additional Arduino from Amazon
  • Purchased 1/4 inch acrylic sheet from dfab (which ended up not being used in the project fabrication)
  • Purchased three 12 pack double  A batteries along with 4 9V batteries
  • Wrote the code to implement PIR input to activate stepper rotation output in code and circuit
  • Received gears and a sample prism structure in rhino file from Christina, and created one full prism structure in rhino space
  • Code and circuit for a PIR to LED piece that was eventually removed from the final project
  • Lasercutted with Christina for half of the geometric prism components
  • Code and circuit that programs the stepper to rotate at a slow constant rate
  • Quickly put together a LED piece out of lasercut pieces from Christina
  • [First Museum Visit]
  • Adjusted code so that when PIR senses change in environment (caused by motion), it causes the stepper to change direction and rotate at a faster speed for a fixed period of time before going back to its default constant rate and direction
  • Tried to remove calibration time, code failed; reverted back to code with calibration time
  • Tried a different way of connecting PIR to stepper in code, ended up reverting back to use of part of Professor Garth’s example on the course website
  • Soldering to extend the length of the PIR  wires to extend its length to the railing in the lobby
  • Added another PIR and more nuances to the stepper rotation
  • [Second Museum Trip]
  • Creating / Editing documentation video, edit photos, and cleaning up code for final project report

Christina:

  • Bought reflective metallic paper from the art store
  • First small scale acrylic mock up of individual prism shapes
  • Second small scale foamcore mock up of a multiple prism structure with rounded corners and bolt fasteners
  • Basic rhino model with the right sized gears and one basic prism structure, which was then given to Alessandra to work on
  • Received rhino model with one full structure from Alessandra, reiterated on the rhino model to create three uniquely shaped structures on a gear system, and implemented openings for where bolt connections would occur within that rhino model
  • Created a quick grasshopper code rotating the three structures to ensure that the structures will not collide into one another before laying out the lasercut file and reorienting all the pieces to be flat
  • Purchased 5 sheets of clear acrylic sheets from dfab
  • Lasercut 50% of the acrylic prism structures and two acrylic gears (using 5 sheets)
  • Purchased 3 more sheets of orange acrylic sheets
  • Lasercut the other 50% of the acrylic prism structures with Alessandra (using 2 sheets, 1 orange acrylic sheet extra)
  • Design and construction of the marble ball bearing based on the metal ball bearing found in Ideate (lasercut acrylic and black foamcore)
  • Assembling one full acrylic prism structure and laminating each acrylic piece with strips of metallic colorful paper, and then connecting the acrylic structure to the gear
  • Creation of a wooden base with elevated marble ball bearing and containment of the stepper motor in the center for the first museum trip (stepper shaft to gear connection provided from Alessandra from her assignment 2 project)
  • Scaled down the project from three acrylic structures to one after noticing the time, scale, and budget complications that this project is currently facing (having spent over $100 out of own pocket alone)
  • [First Museum Trip]
  • Created new wooden stepper shaft to gear connection based off of the CAD file found on ideate website, tested it, piece failed
  • Redesigned and created new wooden stepper shaft to gear connection piece from woodshop that is more rigid and durable, and lasercut stepper gear piece with metal tubes as the locking mechanism for the acrylic laminations
  • Creation of new base with an lasercut top component that fastens into the stepper motor (CAD file from ideate website) and a lower component created from woodshop that secures the stepper motor and provides space to house the circuits
  • After testing in ideate, readjusting the top component of the base by compression one side to accommodate the slight imbalance in the marble ball bearing to minimize potential of gears not touching. This is done by drilling into the top component and screwing a wood screw that pulls together the top and bottom components enough to balance out the mechanical structure
  • [Second Museum Trip]
  • Editing photos from museum trip, drawing electronic schematic and mechanical drawings for the final project report
project video
photo documentation

Project Documentation from First Museum Trip Natural lighting was brighter during our first trip, so the effects of our project are more dramatic in this photo

Model photos on site overlaid to highlight the movement and effects of the project

Closer image of the base and mechanism design of the final project

citations

Stepper-Driven Turntable from 16-223 Course Website

supporting material

Electronic schematics

Final Iteration Schematic

Mechanical drawings

Original Final Project Wood Base

Redesigned Wood Base after first museum visit

Redesigned stepper gear and stepper shaft after first museum visit

Redesigned stepper connections

Source code

</pre>
//INCLUDE STEPPER LIBRARY FOR USE OF BUILT IN FUNCTIONS

#include <Stepper.h>

//CONFIGURE THE PIR SENSOR

int calibrationTime= 10; //time for sensor to calibrate

long unsigned int lowln; // sensor outputs a low impulse

long unsigned int pause = 5000; //miliseconds the sensor has to be low before all motion is assumed to be stopped.

boolean lockLow=true;
boolean takeLowTime;
// begins assuming no motion detected

// DESIGNATE TWO PIR SENSORS TO SPECIFIC PINS AND STARTS

//set up pin for pir sensor 01
int pirPin = 6; // digital pin 6 connects to PIR output (pir close to the stairs)
int pirPos = 0; // variable for reading pir pin status

//set up pin for pir sensor 02
int pirPin2 = 9; // digital pin 9 connects to PIR_02 output (pir close to the ramps)
int pirPos2 = 0; //variable for reading pir pin status

//STEPPER SETUP AND CONFIGURATION

// StepperSweep - move a stepper motor at different rates
//
// Copyright (c) 2016, Garth Zeglin. All rights reserved. Licensed under the
// terms of the BSD 3-clause license as included in LICENSE.
//
// This program assumes that:
//
// 1. A A4988 (Nema17) stepper motor driver is connected to pins 2 and 3.
// 2. A control potentiometer can vary the voltage on A0.
// 3. The serial console on the Arduino IDE is set to 9600 baud communications speed.

// ================================================================================
// Define constant values and global variables.

//STEPPER PIN DEFINITIONS

// Define the pin numbers on which the outputs are generated.
#define DIR_PIN 2 // The direction pin controls the direction of stepper motor rotation.
#define STEP_PIN 3 // Each pulse on the STEP pin moves the stepper motor one angular unit.
#define ENABLE_PIN 4 // Optional control of the driver power.

// ================================================================================
// Configure the hardware once after booting up. This runs once after pressing
// reset or powering up the board.

&nbsp;

void setup(void)
{
//SET PINMODE AND CALIBRATION TIME FOR PIR SENSOR

Serial.begin(9600); //begins serial communication
pinMode(pirPin, INPUT);
pinMode(pirPos, OUTPUT);
digitalWrite(pirPos, HIGH);

//give sensor time to calibrate (this is needed to optimize the accuracy of the sensor)
Serial.println("calibrating sensor");
for(int i = 0; i<calibrationTime; i++){
Serial.print(calibrationTime - i);
Serial.print("-");
delay(1000);

}

Serial.println();
Serial.println("done");
//make sure the PIR output is low (meaning no motion is detected) before ending setup
while (digitalRead(pirPin)==HIGH){
delay(500);
Serial.print(".");
while (digitalRead(pirPin) ==HIGH){
delay(500);
}

}
//PIR HAS COMPLETED SETUP AND CONFIGURATION AND IS READY TO DETECT MOTION
Serial.print("SENSOR ACTIVE");

//SET PINMODES FOR THE STEPPER

// Initialize the stepper driver control pins to output drive mode.
pinMode(DIR_PIN, OUTPUT); //
pinMode(STEP_PIN, OUTPUT);
pinMode(ENABLE_PIN, OUTPUT);

// Drive the /ENABLE pin low to keep the motor always energized.
digitalWrite(ENABLE_PIN, LOW);

}
/****************************************************************/
/// Rotate the stepper motor a specified distance at constant speed. It does
/// not return until the motion is complete, e.g. it 'blocks' for the duration.
///
/// \param steps angular distance to move; the sign determines the direction,
/// but the precise angle depends upon the driver microstepping
/// configuration and type of motor.
///
/// \param speed speed in steps/second

void rotate_stepper(int steps, float speed)
{
// Configure the direction pin on the stepper motor driver based on the sign
// of the displacement.
int dir = (steps > 0)? HIGH:LOW;
digitalWrite(DIR_PIN, dir);

// Find the positive number of steps pulses to emit.
int pulses = abs(steps);

// Compute a delay time in microseconds controlling the duration of each half
// of the step cycle.
// microseconds/half-step = (1000000 microseconds/second) * (1 step/2 half-steps) / (steps/second)
unsigned long wait_time = 500000/speed;

// The delayMicroseconds() function cannot wait more than 16.383ms, so the
// total delay is separated into millisecond and microsecond components. This
// increases the range of speeds this function can handle.
unsigned int msec = wait_time / 1000;
unsigned int usec = wait_time - (1000*msec);

// Print a status message to the console.
Serial.print("Beginning rotation of ");
Serial.print(steps);
Serial.print(" steps with delay interval of ");
Serial.print(msec);
Serial.print(" milliseconds, ");
Serial.print(usec);
Serial.print(" microseconds.\n");

// Loop for the given number of step cycles. The driver will change outputs
// on the rising edge of the step signal so short pulses would work fine, but
// this produces a square wave for easier visualization on a scope.
for(int i = 0; i < pulses; i++) {
digitalWrite(STEP_PIN, HIGH);
if (msec > 0) delay(msec);
if (usec > 0) delayMicroseconds(usec);

digitalWrite(STEP_PIN, LOW);
if (msec > 0) delay(msec);
if (usec > 0) delayMicroseconds(usec);
}
}
// ================================================================================
// Run one iteration of the main event loop. The Arduino system will call this
// function over and over forever.

void loop(void)
{

// Now demonstrate that the stepper can freely rotate.
rotate_stepper(1000, 250.0);
rotate_stepper(-1000, 250.0);

// Now begin a simple back and forth motion with speed controlled by the analog input.
while (1) {
// Read the current value of the potentiometer input from analog input 0.
int an0 = analogRead(0);
Serial.print(an0);

Serial.println("testing speed");
// Map the input to a useful speed range. Last two values are the speeds.
int speed = map(an0, 0, 1023, 15, 20);
//The current speed is set to 15

//CONFIGURE MOVEMENT FOR PIR 1 IS MOTION IF DETECTED AT THE STAIRCASE
//this motion is a 180 degree rotation with a shortstep back 50 degrees, followed by another 180 degree rotation.
//This motion is executed as long as motion is detected in front of pir 1.

if (digitalRead(pirPin)==HIGH){ // motion detected in front of the staircase
Serial.println("in high");
rotate_stepper( 180, speed);
rotate_stepper( -50, speed);
rotate_stepper( 180, speed);
}

//CONFIGURE MOVEMENT FOR PIR 2 IF MOTION IS DETECTED AT THE RAMP
// this motion is a short 100 degree rotation back and forth motion.
if (digitalRead(pirPin2)==HIGH){ // motion detected at the ramp
Serial.println("in high");
rotate_stepper( 100, speed);
rotate_stepper( -100, speed);
rotate_stepper(100, speed);
rotate_stepper(-100, speed);

//CONFIGURE CONSTANT MOVEMENT IF NO MOTION IS DETECTED AT EITHER OF THE TWO PIR'S
}
else {
Serial.println("in low"); //The system has detected no motion
rotate_stepper(-360, speed); //rotate counterclockwise at a constant, slow speed.
}
}
}

/****************************************************************/
<pre>
]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/final-project-reports-fragment/feed/ 0
Final Report: Interactive Morse Code Device https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/2111/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/2111/#respond Wed, 06 Dec 2017 01:11:54 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2111 Abstract

The goal of my project was to teach kids about Morse Code.   The aspects of Morse Code that I hoped children would learn from was the concept that language can be represented differently than just letters.  I mapped Morse Code patterns with the letters: A, B, C.  Kids had to traverse through the “puzzle” of combinations of dots and dashes to reveal all three letters.  If at any point they messed up a letter, they would have to redo that letter; and the LED pattern would reveal that they had made a mistake.

Through my project, I hoped to show the general idea of how Morse Code works, while at the same time providing an interactive and fun time for children.  The LED’s would parse through the dots and dashes as kids input them, and the letters would be revealed once the children successfully completed the letter.  I was pleasantly surprised at how kids used their parents to help them, which reminded me of when my parents helped me complete puzzles and difficult math problems when I was little.

 

Objectives

The main of objective of my project was to get kids to think outside of the box.  Children’s minds are like a sponge, soaking in new information.  It has been shown that Morse Code is much easier learned by children than adults.  I wanted to implement a system where kids would be able to learn from the repetition of creating dots and dashes to generate a language they were (mostly) familiar with already.

Objectives relevant to the project itself:

Mounting a key:  Originally, I had wanted to use a real Morse Code key that is used by “professionals” who sent actual messages during war times and such in the age where Morse Code was a necessity to send messages.  However, since I was dealing with 4 year old children, I instead mounted a red button to a little box that kids would perhaps find more entertaining or drawing when they saw the project.

LED Display: The point of the LED display was for kids to be able to follow along with the pattern in generating letters.  If they were not quick enough or perhaps put in a dash instead of a dot that was shown by the LED’s, the LED pattern would start from the beginning.  This allowed for kids to understand the temporal necessity, as well as correctness, that comes when sending a message through Morse Code.  It was also a key reason children approached the project to begin with, as I was prepared for a rectangular box with holes in it to not be the most attractive part.

The Box:  The box is the most critical part of the project.  I used servos that would rotate letters in and out of view depending on whether the user correctly identified a letter.  I finally decided on just using 3 letters based on the fact that I wanted the project to represent more of a toy than simply a map of Morse Code to letters.  This would be more time consuming for children and might confuse or bore them.  I also showed the pattern that they were supposed to implement once they understood dots and dashes correctly.  This would be more helpful on the second and third time using the project.

 

Results

I believe that the scope of my project was fair, but I could have expanded it or perhaps improved the temporal cues.   The LED’s, though I liked how they worked, could have been more precise on how Morse Code works with regards to creating actual words or even sentences.

Despite this, I was very pleased with how my project worked.  Parents were very intrigued by the concept of the project and would often work with their children to get the correct result.  Children who understood how to use the button would play with it until they did understand, or needed some help understand what was a dot and what was a dash with respect to the button.  Once children understood what was happening, they seemed to love the project and do it over and over again.  This was an intended result of mine, and it made the children and parents very happy in the moment.  I do believe that they got a good taste of what Morse Code really was and how it could be used to make the familiar language of English.

Photos

(Demo)

(Demo)

(Final)

(Final)

(Final)

Videos

 

Technical Documentation


unsigned long signal_len,t1,t2; //time for which button is pressed
int inputPin = 2; //input pin for push button
int ledPin = 4; //outpu pin for LED
int ledPin1 = 7;
String code = ""; //string in which one alphabet is stored

#include &amp;amp;lt;Servo.h&amp;amp;gt;
const int SERVO_PINA = 9;
const int SERVO_PINB = 10;
const int SERVO_PINC = 11;
Servo servo;
Servo servo1;
Servo servo2;

void setup() {
Serial.begin(9600);
pinMode(inputPin, INPUT_PULLUP); //internal pullup resistor is used to simplify the circuit
pinMode(ledPin,OUTPUT);
pinMode(ledPin1, OUTPUT);
servo.attach(SERVO_PINA);
servo.write(90);
servo1.attach(SERVO_PINB);
servo1.write(90);
servo2.attach(SERVO_PINC);
servo2.write(90);
digitalWrite(ledPin, HIGH);
}

void loop()
{
if(servo.read() == 0 &amp;amp;amp;&amp;amp;amp; servo1.read() == 0 &amp;amp;amp;&amp;amp;amp; servo2.read() == 10){
for(int i = 0; i &amp;amp;lt; 2; i++) {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
delay(500);
digitalWrite(ledPin, LOW);
digitalWrite(ledPin1, HIGH);
delay(500);
}
servo.write(90);
servo1.write(90);
servo2.write(90);
}
if(servo.read() != 0) {
if(code == "") {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
}
if(code == ".") {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin, LOW);
}
}
else if(servo.read() == 0 &amp;amp;amp;&amp;amp;amp; servo1.read() != 0) {
if(code == "") {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin, LOW);
}
else {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
}
}
else if(servo.read() == 0 &amp;amp;amp;&amp;amp;amp; servo1.read() == 0 &amp;amp;amp;&amp;amp;amp; servo2.read() != 10) {
if(code == "" || code == "-.") {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin, LOW);
}
else {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
}
}
NextInput:
while (digitalRead(inputPin) == HIGH) {}
t1 = millis(); //time at button press
//digitalWrite(ledPin, HIGH);
while (digitalRead(inputPin) == LOW) {}
t2 = millis(); //time at button release

signal_len = t2 - t1; //time for which button is pressed
if (signal_len &amp;amp;gt; 50) { //to account for switch debouncing
code += readInput(); //function to read dot or dash
if(servo.read() != 0) {
if(code == "") {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
}
if(code == ".") {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin, LOW);
}
//else goto NextInput;
}
else if(servo.read() == 0 &amp;amp;amp;&amp;amp;amp; servo1.read() != 0) {
if(code == "") {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin, LOW);
}
else {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin, LOW);
delay(100);
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
}
}
else if(servo.read() == 0 &amp;amp;amp;&amp;amp;amp; servo1.read() == 0 &amp;amp;amp;&amp;amp;amp; servo2.read() != 10) {
if(code == "" || code == "-.") {
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin, LOW);
}
else {
digitalWrite(ledPin, HIGH);
digitalWrite(ledPin1, LOW);
}
}
Serial.println(code);
}
while ((millis() - t2) &amp;amp;lt; 1000) { //if time between button press greater than 0.5sec, skip loop and go to next alphabet
if (digitalRead(inputPin) == LOW) {
goto NextInput;
}
}
morseToAlpha();
}

char readInput() {
if (signal_len &amp;amp;lt; 300 &amp;amp;amp;&amp;amp;amp; signal_len &amp;amp;gt; 50) {
Serial.println(".");
return '.'; //if button press less than 0.6sec, it is a dot
}
else if (signal_len &amp;amp;gt; 300) {
Serial.println("-");
return '-'; //if button press more than 0.6sec, it is a dash
}
}

void morseToAlpha() {
static String letters[] = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-",
".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", "E"
};
int i = 0;
if (code == ".-.-.-") {
Serial.print("."); //for break
}
else {
if (code.length() &amp;amp;gt; 6) {
code = code.substring(5);
}
while (letters[i] != "E") { //loop for comparing input code with letters array
if (letters[i] == code) {
Serial.print(char('A' + i));
if(char('A' + i) == 'A') {
servo.write(0);
delay(2000);
}
else if(char('A' + i) == 'B') {
if(servo.read() == 0) {
servo1.write(0);
delay(2000);
}
}
else if(char('A' + i) == 'C') {
if(servo1.read() == 0) {
servo2.write(10);
delay(2000);
}
}
break;
}
i++;
}
if (letters[i] == "E") {
Serial.println("&amp;amp;lt;Wrong input&amp;amp;gt;"); //if input code doesn't match any letter, error
}
}
code = ""; //reset code to blank string
}

Citations

Garth Zeglin

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/2111/feed/ 0
Final Report: Drummer Fountain https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/final-report-drummer-fountain/ https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/final-report-drummer-fountain/#respond Tue, 05 Dec 2017 23:58:06 +0000 https://courses.ideate.cmu.edu/16-223/f2017/work/?p=2097 Drummer Fountain

Kaushik Murali and Gladstone Butler

12/6/2017

 

Abstract

The main goal of the drummer fountain is to provide an interactive platform between a set of drums and a water fountain that could invoke interest and wonder in younger children. There was also an added game element, with suspended colorful objects that the water from the fountain would hit if the drums were hit in the right manner.

Our results for this project were largely successful, with the fountain being operational but with a few technical faults. The insect objects were stable but did not add in the element of intrigue that we had intended. We had issues with implementation during the final demonstration, with plumbing failure causing our valves to not eject water as intended. Our drums were still operational and this acted as a source of interest for younger children that wanted to hit the drum pads.

Objectives

The main idea is to allow users to control water flow depending on the pressure and speed at which they hit the drum pads. There was an added game element, where we placed colorful insect objects in the path of the stream from the valves, allowing the objects to slightly swing around when hit with the water. This was the main goal but in order to accomplish this, we needed to set sub-goals that would allow us to build up to our final project.

The first sub-goal we had was to create an operational drum mechanism. The drum pads used force-sensors that lay under drum heads that we cut out. These sensors relayed information to the Arduino.

The next sub-goal was to implement a functional water fountain. Upon certain drum pads being hit, a certain valve opens and this allows water to eject from the valve.

Implementation

For the drum pads, we opted to not use pre-built drum sensors because of cost and also because our needs for the drums were simple. The drums were meant to take physical force as input and output electrical signal to the Arduino, which would allow us to undertake further computation for the fountain. To implement this, we constructed a box that had three rings on the top surface. We cut out circles from a drum head and attached them to each of the wooden rings. Under these drum heads, we placed the force sensors that would activate upon contact, allowing us to read it via the Arduino. An interesting and useful design choice we made for the drum heads was to place a metal bolt on the inner side of the drum head. This ensured that the force sensor would receive contact from the whole drum head surface. This proved to be crucial for us as it contributed significantly to the activation of the valve.

For the fountain, we made use of a 12V water pump and two solenoid valves. These valves were connected to the pump via flexible transparent tubing. This allowed us to monitor the water level and helped us troubleshoot when necessary. This setup was placed in a waterproof acrylic tank. The valves were placed on a wooden mount, elevating the valves and protecting the valve connections from water damage. We cut holes in the tank to facilitate the wiring of the valves and pump, and to improve the aesthetic of the tank. We suspended our colorful objects from a thin piece of acrylic sheet, using wire to secure the objects to the sheet.

Our software portion was relatively simple. We read input values from each of the force sensors and implemented logic that would open the corresponding valves, which were powered externally using a 12V adaptor.

Outcomes

We had originally planned for a more extravagant demonstration, with LED strobe lights and sound that would correspond to the hitting of the drums. This combined with the fountain would have been more attractive to viewers. However, after assembling our project together, we decided that due to the nature of our wiring and the dimensions of the drum box, it would be best to simplify the demonstration to just a fountain display. This turned out to be a good decision on our part as it allowed us to focus on the functionality of the fountain.

Video we took attempting to incorporate LED lights into our project

 

The choices we made in constructing the drum box turned out to be successful, with the box remaining sturdy and the drum rings staying firmly attached to the top of the drums. One area for improvement in the construction of the drums would have been material for the drum head. Since it was an authentic drum head, we were unable to laser cut it and thus, could not get a perfect circle to fit our drum ring. Furthermore, the material was white and flimsy, resulting in it becoming dirty and constantly getting separated from the drum rings. We could have improved on this by using a studier material, one that would also attach better with Epoxy glue to the drum ring.

For the construction of the fountain, the dimensions of the acrylic tank and the valve mount turned out to be ideal for the flow of water through the tube while keeping the connections away from water. We water-proofed the connections on the valve, which helped us significantly when working with water. However, the effort we put into protecting our connections and stabilizing the valves worked against us when we had to troubleshoot plumbing problems. During the final demonstration, we were unable to trouble shoot the plumbing problems and we could not truly analyze the circuits for faults due to the manner in which we had set up the valves. There is not a clear way to fix this problem, as one solution led to another problem but more frequent and consistent testing of the demo could have helped us mitigate this issue.

The straightforward nature of our software helped us immensely in troubleshooting code troubles and as a result, we did have trouble with the relaying of information from the drums to the fountain.

 

Contribution

Kaushik Murali

  • Designed the specifications for the drum box and acrylic tank.
  • Combined and wrote code for final demonstration
  • Physical work for the assembly of the drum box and fountain tank
  • Project Ideation
  • Documentation and reports

 

Stone Butler

  • Identified required pieces of software for valve and LED operation
  • Circuitry work for connecting the valves for fountain
  • Acquired key components for assembly of valve
  • Fabrication work (Soldering and Laser cutting)

 

Final Demonstration at the Children’s Museum

 

Demonstration Video

Demonstration for the drummer fountain

Supporting Material

CAD DRAWINGS

https://drive.google.com/open?id=1emJCBybPEvG61-zQ06g3dsT849uLimqJ

CODE

int fsrAnalogPin0 = 0; // FSR is connected to analog 0
int reading0;
int fsrAnalogPin1 = 1;
int reading1;
int fsrAnalogPin2 = 2;
int reading2;
int solenoidPin1 = 4;
int solenoidPin2 = 5;

void setup(void) {
Serial.begin(9600);   // We’ll send debugging information via the Serial monitor
pinMode(LEDpin0,OUTPUT);
pinMode(LEDpin1,OUTPUT);
pinMode(LEDpin2,OUTPUT);
pinMode(solenoidPin1,OUTPUT);
pinMode(solenoidPin2,OUTPUT);
}

void loop(void) {
reading0 = analogRead(fsrAnalogPin0);
reading1 = analogRead(fsrAnalogPin1);
reading2 = analogRead(fsrAnalogPin2);
// we’ll need to change the range from the analog reading (0-1023) down to the range
// used by analogWrite (0-255) with map!

// Check the readings and activate solenoid accordingly
// reading 0 and 2 for solenoid 1 and 2 respectively; reading 1 for both

if (reading1 > 500) {
digitalWrite(solenoidPin1,HIGH);
digitalWrite(solenoidPin2,HIGH);
} else if (reading0 > 40) {
digitalWrite(solenoidPin1,HIGH);
digitalWrite(solenoidPin2,LOW);
} else if (reading2 > 500) {
digitalWrite(solenoidPin2,HIGH);
digitalWrite(solenoidPin1,LOW);
} else {
digitalWrite(solenoidPin1,LOW);
digitalWrite(solenoidPin2,LOW);
}
}

 

References

  1. https://courses.ideate.cmu.edu/16-223/f2017/text/ex/mechanism/laser-tolerance/laser-tolerance.html#exercise-laser-tolerance
  2. https://courses.ideate.cmu.edu/16-223/f2017/text/ex/Arduino/WS2801-LED-SPI/WS2801-LED-SPI.html#exercise-ws2801-led-spi
  3. https://www.bc-robotics.com/tutorials/controlling-a-solenoid-valve-with-arduino/

 

 

]]>
https://courses.ideate.cmu.edu/16-223/f2017/work/2017/12/05/final-report-drummer-fountain/feed/ 0