A. The Block – Peirong Li – Professor Gottfried Haider
(Picture: finished product)
B. CONTEXT AND SIGNIFICANCE
In the research phase of the group project, I researched two interactive artifacts that informed my initial understanding of what an “interaction” is. One of them is a chatGPT typewriter with a retro design. Together with reading the Crawford piece on interactivity, I reached the conclusion that an interaction is a process in which two actors alternatively listen, think, and speak; or input, process, output (Page 5, Crawford, The Art of Interactive Design). However, interactions do not necessarily need to strictly take turns. For example, Arduino’s program can be written in a way so that the user can interrupt whatever action is taken at any point. This means that the interactivity of my design can be more “fluid” instead of more rigidly defined. This principle will constitute a major change that I implemented after user testing. My project is unique due to its use of mechanical movement to make sound, and its target audience would be anyone that’s interested in creating alternative, interesting sound, regardless of music literacy.
C. CONCEPTION AND DESIGN
The initial concept for this project is to create a music keyboard using buzzers and buttons. But since the buzzers are overused and it lacked the musicality that makes an instrument pleasant to use, we decided to do something different. The first change we made was to move away from the buzzer and use something physical to make sound. With the help from Professor Haider, we had the idea to use servos and attach something on it to make sound. After some research and thinking, we settled on using something metallic (a spoon) and attached it to the servo using cable ties. Soon, we prototyped it in which the servo would control the spoon to hit a glass whenever the button is pressed. When looking for an object to attach the servos, I chose a wooden block from a pile of unused materials, and I was immediately attracted by the aesthetics that it had, so I expanded the setup to four servos and four wooden blocks. After that, it was almost intuitive for us to fill the glasses with different amounts of water to let it have different pitches of sound when hit, so the musicality was amplified. Lastly, when arranging the wood blocks, we could have gone with laying them in a straight line, but we went with a rectangular setup in which servos would hit the glass in several directions. Sketches will be provided below for illustration.
D. FABRICATION AND PRODUCTION
For fabrication and production, we started with a sketch of how the orientation of the servos would correspond to the positioning of the wood blocks. The large rectangles are the wood blocks, and these wood blocks have different heights. Our initial idea is to have the height of the blocks to correspond to the tone that it makes. Then, the small rectangles are representative of the servos, and the arrows indicate the directions that the spoon will hit. The circles are glasses filled with different amounts of water.
(Figure 1: layout of the blocks and orientation of spoon activation)
(Figure 2: experimenting with the layout)
Figure 2 is a picture of how we experimented with the layout of the wood blocks. Next, we prototyped the mechanism using Arduino. The component of the initial prototype consisted of a button (that has an integrated resistor) and a servo. Whenever the button is pressed, the servo will move for a certain degree. However, how many degrees should the movement be, how the code will be structured was still unclear at this point. So there was a lot of hands-on experimentation with the building process.
(Figure 3: wiring with Arduino that connected the button with the servo)
We wrote a simple code controlling the servo using a for loop and the delay function. In this code, when the button is not pressed, it will go to 20 degrees and stay idle. When the button is pressed, the servo will execute a one time movement from 150 degrees back to 30 degrees. Note that because the initial position is at 20 degrees, the servo will move to 150 degrees as fast as possible, this is the part where it hits the glass. Then, the for loop is actually only making it go back from 150 to 30 degrees, completing the movement. A section of the code is attached below. We tested this code with 4 servos and 4 buttons.
if (buttonState1 == HIGH) {
for (pos1 = 150; pos1 >= 30; pos1 += -1) {
// moving the servo in steps of 1 degree
myservo1.write(pos1);
// tell servo to go to position in variable 'pos'
delay(4);
// waits 4ms for the servo to reach the position
}
} else {
myservo1.write(20);
}
(Figure 4: testing the code using four servos and four buttons)
Then, we attached the servos to the wood blocks and positioned the glasses accordingly for it to be properly hit by the spoons. We also taped the buttons on another small wood block, completing the aesthetics. This was the state of the project during user testing.
(Figure 5: the state of the project during user testing)
We received some very useful feedback from user testing. The major ones are:
- The keys should be able to be pressed at the same time
- The key travel is too long, it takes more than a second to reach the glass and back
- Could switch the glasses to something else, i.e., percussion
- Color code the blocks so the user knows which key corresponds to which block
- Add more blocks
We adopted the first three feedbacks due to their feasibility and time constraints. For the keys to be pressed at the same time, it took a large amount of time to rewrite the entire code from for loop-delay based to millis() based. Because the delay function halts the entire arduino, millis() is the best alternative to achieve simultaneous key press. Secondly, we modified the idle position of the servo to make the spoon travel smaller. In other words, the spoon now hits faster and also bounces back faster. The key section of the code is attached below, and the full code is attached at the end of this document.
if buttonState1 = digitalRead(buttonPin1); // read the state of the button unsigned long currentMillis1 = millis(); // get the current time if (buttonState1 == HIGH) { if (pos1 < 100) { pos1 = 100; // move to 100 degrees } if (currentMillis1 - previousMillis1 >= interval) { //move the servo to the new position only at the specified interval myservo1.write(pos1); // tell servo to go to position in variable 'pos' previousMillis1 = currentMillis1; // save the last time the servo moved } } else { pos1 = 30; // set the position to 30 degrees immediately if button is not pressed myservo1.write(pos1); // move the servo to the new position immediately }
(Figure 6: using percussive items such as the Arduino box, water bottle, and phone)
(Figure 7: button layout)
(Figure 8: a close-up shot of the spoon mechanism)
E. CONCLUSIONS
Our project’s intended goal is to provide inspiration and entertainment through the use of a device that makes sound creatively. It aligns mostly well with my definition of interaction, which emphasizes a more fluid way of communicating between actors, and the “input, process, output” cycle that an interaction entails. Moreover, the audience’s response and their interaction with the project fits with our expectations. The buttons, combined with their layout on the wood block, creates affordances that indicate this is the area of input, therefore the users always start by pressing the buttons. Upon pressing the buttons, they would soon find out how the mechanism works – in that respect, the project was intuitive and easy to understand. However, some users did not try to press the buttons at the same time at first, implying that the interface design can be improved to suggest to users that this is more like a keyboard. If there is more time, we would improve the project by scaling it – meaning not only having more blocks and servos but also making them more spreaded and occupying a larger physical space, to provide a more immersive experience. Also, we would improve the user interface, the buttons to a more musical shape (keyboard or drum pad). One drawback of the mechanism is that when all buttons are pressed at once, there could be a chance that the Arduino freezes, and we don’t know exactly how to fix that. Another is the instability of the servos. We positioned the servos using double-sided tape, which was an easy fix but cannot last a long time. Since the mechanism involves a lot of physical movement, there can be better ways to fix the servos. Lastly, my takeaways are mainly twofold. The first one is when designing projects, practicality matters a lot. There has to be something (existing knowledge or known ways) to support the ideas and make them into reality. For example, I underestimated the level of difficulty involved in coding and it ended up costing a majority of the time of the project. The second takeaway is improvisation can make the idea better along the way. A plan or proposal can be perfect in its own ways, but a little bit of improvisation can always bring unexpected, sometimes brilliant, results. The wood blocks that we used in this project were purely picked up by chance and so was the choice of using spoons and glasses. In conclusion, although practicality and improvisation matters, it is always important to learn continuously, only practice and learning can improve our ability to convert ideas into reality
F. ANNEX
(Video: a user testing demo)
(Video: the same user testing demo but with a closer up shot of the controls)
(Video: tuning the servos)
FULL CODE
#include //can use millis for pressing buttons at once Servo myservo1; Servo myservo2; // create servo object to control a servo Servo myservo3; Servo myservo4; const int buttonPin1 = 2; const int buttonPin2 = 3; const int buttonPin3 = 4; const int buttonPin4 = 5; int buttonState1 = 0; int buttonState2 = 0; int buttonState3 = 0; int buttonState4 = 0; int pos1 = 0; // variable to store the servo position int pos2 = 0; int pos3 = 0; int pos4 = 0; unsigned long previousMillis1 = 0; // will store last time the servo moved unsigned long previousMillis2 = 0; unsigned long previousMillis3 = 0; unsigned long previousMillis4 = 0; unsigned long interval = 100; // interval at which to perform the one-time motions void setup() { myservo1.attach(9); //blue myservo2.attach(10); //red button myservo3.attach(11); myservo4.attach(12); //yellow pinMode(buttonPin1, INPUT); pinMode(buttonPin2, INPUT); pinMode(buttonPin3, INPUT); pinMode(buttonPin4, INPUT); } void loop() { //1st servo buttonState1 = digitalRead(buttonPin1); // read the state of the button unsigned long currentMillis1 = millis(); // get the current time if (buttonState1 == HIGH) { if (pos1 < 100) { pos1 = 100; // move to 100 degrees } if (currentMillis1 - previousMillis1 >= interval) { //move the servo to the new position only at the specified interval myservo1.write(pos1); // tell servo to go to position in variable 'pos' previousMillis1 = currentMillis1; // save the last time the servo moved } } else { pos1 = 30; // set the position to 30 degrees immediately if button is not pressed myservo1.write(pos1); // move the servo to the new position immediately } //2nd servo buttonState2 = digitalRead(buttonPin2); unsigned long currentMillis2 = millis(); if (buttonState2 == HIGH) { if (pos2 < 100) { pos2 = 100; } if (currentMillis2 - previousMillis2 >= interval) { myservo2.write(pos2); previousMillis2 = currentMillis2; } } else { pos2 = 30; myservo2.write(pos2); } //3rd servo buttonState3 = digitalRead(buttonPin3); unsigned long currentMillis3 = millis(); if (buttonState3 == HIGH) { if (pos3 < 100) { pos3 = 100; } if (currentMillis3 - previousMillis3 >= interval) { myservo3.write(pos3); previousMillis3 = currentMillis3; } } else { pos3 = 30; myservo3.write(pos3); } //4th servo buttonState4 = digitalRead(buttonPin4); unsigned long currentMillis4 = millis(); if (buttonState4 == HIGH) { if (pos4 < 100) { pos4 = 100; } if (currentMillis4 - previousMillis4 >= interval) { myservo4.write(pos4); previousMillis4 = currentMillis4; } } else { pos4 = 30; myservo4.write(pos4); } }