12/6/2017
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.
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.
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.
Warren: Electronics, Schematics
Soonho: Housing, Documentation, Drawings
Shared: Fluid Mechanics, Code
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.
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.
These are pictures of children and parents interacting with our project.
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.
Schematic created by Warren Glasner.
All drawings were created by Soonho Kwon.
Housing Schematics
Input Housing
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); } }
]]>
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:
Gearbox and Airplane Mount Assembly
Joystick Enclosure
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); } } }
]]>
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:
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); }
]]>
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.
Implementation
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
Successes:
Failures:
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; } }}]]>
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.
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.
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.
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.
Jarrek:
Josh:
Child Interactions:
Getting ready, morning of project demo!
Jar Internals
Primary jar programming
#include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif #define PIN 3 int rec = 6; int pl = 7; &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 &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() > just_spoke + 1000){ record(); has_recording = true; } else if(!digitalRead(break_beam) and millis() < just_spoke + 1000){ has_recording = true; } else if(has_recording){ int brightness = 0; float my_time = millis() % 2000; if (my_time > 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<256; j++) { for(i=0; i<strip.numPixels(); i++) { strip.setPixelColor(i, Wheel((i+j) & 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<256*5; j++) { // 5 cycles of all colors on wheel for(i=0; i< strip.numPixels(); i++) { strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); } strip.show(); delay(wait); } } //Theatre-style crawling lights. void theaterChase(uint32_t c, uint8_t wait) { for (int j=0; j<10; j++) { //do 10 cycles of chasing for (int q=0; q < 3; q++) { for (uint16_t i=0; i < 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 < 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 < 256; j++) { // cycle all 256 colors in the wheel for (int q=0; q < 3; q++) { for (uint16_t i=0; i < 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 < 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<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 < 85) { return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 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 &lt; 2000){ analogWrite(led2, 0); analogWrite(led3, 0); if(millis() % 2000 &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 &lt; 4000){ analogWrite(led1, 0); analogWrite(led3, 0); if(millis() % 2000 &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 &lt; 1000){ brightness3 = map(millis() % 1000, 0, 1000, 0, 255); } else{ brightness3 = map(millis() % 1000, 0, 1000, 255, 0); } analogWrite(led3, brightness3); } }]]>
Alessandra Fleck and Christina Brown
December 5th, 2017
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.
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:
In order to achieve our goals stated above, the following design choices were implemented.
For Software:
For hardware:
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.
Alessandra:
Christina:
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
Stepper-Driven Turntable from 16-223 Course Website
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. 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>]]>
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;lt;Servo.h&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; servo1.read() == 0 &amp;amp;&amp;amp; servo2.read() == 10){ for(int i = 0; i &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; 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; servo1.read() == 0 &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;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; 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; servo1.read() == 0 &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;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;lt; 300 &amp;amp;&amp;amp; signal_len &amp;gt; 50) { Serial.println("."); return '.'; //if button press less than 0.6sec, it is a dot } else if (signal_len &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;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;lt;Wrong input&amp;gt;"); //if input code doesn't match any letter, error } } code = ""; //reset code to blank string }
Citations
Garth Zeglin
]]>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
Stone Butler
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
]]>