16-376 Spring 2021 https://courses.ideate.cmu.edu/16-376/s2021 Kinetic Fabrics Mon, 10 May 2021 20:00:53 +0000 en-US hourly 1 https://wordpress.org/?v=5.7.11 Project 2: Masks Off – Anishwar Tirupathur and Elise Delgado https://courses.ideate.cmu.edu/16-376/s2021/5415/project-2-masks-off-anishwar-tirupathur-and-elise-delgado/ https://courses.ideate.cmu.edu/16-376/s2021/5415/project-2-masks-off-anishwar-tirupathur-and-elise-delgado/#respond Mon, 10 May 2021 13:28:49 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5415

CONCEPT:

Masks Off is a project that investigates the proxy face or, in other words, the face upon the face. The masks that are constructed as a result of this investigation juxtapose the abstracted face of the wearer’s partner on top of that of the wearer and the partner’s environment upon the wearer. The mask takes data from the partner’s environment in order to activate the masks. In the case of Elise’s mask, the position of Anishwar’s head while wearing his mask sends data to Elise’s mask in order to activate the pneumatic and speaker of the Bluefruit. Anishwar’s mask is activated by the level of darkness in Elise’s environment which causes the pneumatic in his mask to inflate and the speaker on his Bluefruit to play tones.

The team decided to explore the concept of the proxy face and the juxtaposition of distant people and environments on another because they were interested in analyzing the significance and implications of the remote digital communication that has become popularized during the Covid-19 pandemic through the symbol of the mask which has taken on a new prominence and significance during this time period.

Concept Sketch

EXECUTION:

We decided to overlap tulle over fabric since the embroidery was much easier on the tulle. Elise stitched the designs into the tulle to create the faces and attached the tulle to the fabric underneath. For Anishwar, we used the lace ‘sheet’ that Elise sewed together. For Elise, we used the stretchy metallic fabric. The masks were pretty jarring in imagery and interesting at the same time.

Anishwar’ Mask
Elise’s Mask

PROCESS:

Our process begin with experimenting on what we wanted the masks to incorporate and how might they look. Anishwar drew sketches of ideas and the one below was what we ultimately went with. The idea was to create a proxy face on top of our own and involve lights and noise when reacting to the other’s environment. Our final tweaked those processes.

This image has an empty alt attribute; its file name is IMG_6738-584x1024.jpg
First Sketches

The idea for the masks came from the runway, looking at the work of John Galliano. You can see the influence in our work as we used tulle and bold colors as well as the black thread detailing in Anishwar’s mask. Elise’s mask also used embroidery detailing and tulle but in a red thread.

This image has an empty alt attribute; its file name is mask3.jpeg
This image has an empty alt attribute; its file name is mask-1-790x1024.jpeg

After gathering the basic idea, we chose to look at the working parts. We experimented with pneumatics before we got the motor pump. Once we got the motor, we also made sure to add holes to that the inflatables could relax between pumping cycles. The final inflatables were smaller round bladders.

This image has an empty alt attribute; its file name is IMG_5067-768x1024.jpg

We also experimented with the design aspects, looking at embroidery and the fabric that we could use. When making the final mask, the embroidery foot on the sewing machine was eating up the tulle and making holes, so we chose to do hand-stitching with embroidery floss when making the faces.

This image has an empty alt attribute; its file name is IMG_6744-1-1024x768.jpg
Anishwar’s Embroidery Experimentation
This image has an empty alt attribute; its file name is IMG_5114-768x1024.jpg
Lace used for Anishwar’s mask

After we figured out our working parts, we combined our efforts to create the final masks. We had a lot of trouble getting the code to work over the bridge and that took a lot of our time. The final masks also used a double layer of fabric, one with embroidery and the other to give more structure and color. The pneumatics were placed under the masks, either with elastic around the head or attached to the inside of the mask.

ANISHWAR’S CODE:

# cpb_pump_control.py

# Demonstrate control of an air pump based on a DC motor driven via a MOSFET
# transistor driver circuit *or* a DRV8833 motor driver in a single-ended mode.
#
#  1. The driver PWM input           connects to A2
#  2. The battery ground line        connects to GND

# Related documentation:
# https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html#module-pwmio
# https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#module-adafruit_motor.servo

# ----------------------------------------------------------------
# Import standard Python modules.
import time, math, sys

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import pwmio
import supervisor

# ----------------------------------------------------------------
# Initialize hardware.

# Create a PWMOut object on pad A2 to generate control signals.
pwm = pwmio.PWMOut(board.A3, duty_cycle=0, frequency=20000)

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second
next_pwm_update = time.monotonic_ns()
next_status_update = time.monotonic_ns()
toneCheck = True
nextSound = time.monotonic_ns()
pitch = 329
pitchTwo = 293
nextPrint = time.monotonic_ns()
tokens = []
motionDuration = 0
soundDuration = 0
soundDurationTwo = 0
lastNow = time.monotonic_ns()
# ----------------------------------------------------------------
# Enter the main event loop.
while True:
    # Read the current integer clock.
    #print (pitch)
    now = time.monotonic_ns()
    soundNow = time.monotonic_ns()

    elapsedTime = now - lastNow
    lastNow = now

    x,y,z = cp.acceleration
    if now >= nextPrint:
        print(pwm.duty_cycle)
        nextPrint = now + 2000000000
        if x &gt; 1 or y < 0:
            print("start")
            if x &gt;= 1.25:
                print("go")
            elif y < 0:
                print("onward")
    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        tokens = line.split()
        if line.startswith("begi"):
            motionDuration = 7050000000
        if line.startswith("now"):
            soundDuration = 500000000
        if line.startswith("geronimo"):
            soundDurationTwo = 500000000
    # If the time has arrived to update the servo command signal:
    if soundDuration &gt; soundDurationTwo:
        soundDurationTwo = 0
    elif soundDurationTwo &gt; soundDuration:
        soundDuration = 0
    if motionDuration &gt; 0:
        if now &gt;= next_pwm_update:
            next_pwm_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
            pwm_level = 0.5 + 0.5 * math.sin(phase_angle)

            # convert a unit-range (0 to 1) pwm_level to a 16-bit integer representing a fraction
            new_duty_cycle = min(max(int(pwm_level * 2**16), 0), 2**16-1)

            # If the new value is less than a reasonable minimum, clamp to zero.
            # The pump motor will stall if the PWM fraction is too low; this turns
            # it off instead.
            if new_duty_cycle < 48000:
                pwm.duty_cycle = 0
            else:
                pwm.duty_cycle = new_duty_cycle

            phase_angle = (phase_angle + phase_rate * 0.01) % (.5 * math.pi)

        # If either button is pressed, override the generated servo signal and run the motor.
        if cp.button_a or cp.button_b:
            pwm.duty_cycle = 2**16-1

        # If the time has arrived to display the status:
        if now &gt;= next_status_update:
            next_status_update += 500000000  # 0.5 sec in nanoseconds (2 Hz update)
        motionDuration -= elapsedTime
    else:
        pwm.duty_cycle = 0



    if soundDuration &gt; 0:
        if soundNow &gt;= nextSound:
            if pitch == 329:
                pitch = 493
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
            elif pitch == 493:
                pitch = 329
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
            soundDuration -= elapsedTime
    elif soundDurationTwo &gt; 0:
        if soundNow &gt;= nextSound:
            if pitchTwo == 293:
                pitchTwo = 466
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
            elif pitchTwo == 466:
                pitchTwo = 293
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
            soundDurationTwo -= elapsedTime
    else:
        cp.stop_tone()

ELISE’S CODE:

# https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html#module-pwmio
# https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#module-adafruit_motor.servo
import time, math, sys
from adafruit_circuitplayground import cp
import board
import pwmio
import supervisor
pwm = pwmio.PWMOut(board.A3, duty_cycle=0, frequency=20000)
phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second
next_pwm_update = time.monotonic_ns()
next_status_update = time.monotonic_ns()
toneCheck = True
nextSound = time.monotonic_ns()
pitch = 329
pitchTwo = 293
nextPrint = time.monotonic_ns()
motionDuration = 0
soundDuration = 0
soundDurationTwo = 0
lastnow = time.monotonic_ns()

while True:
    # Read the current integer clock.
    #print(cp.light)
    now = time.monotonic_ns()
    soundNow = time.monotonic_ns()

    elapsed_time = now - lastnow
    lastnow = now

    if now &gt;= nextPrint:
        nextPrint = now + 2000000000
        if cp.light < 100:
            print("begi")
            if cp.light < 81 and cp.light &gt;= 21:
                print("now")
            elif cp.light < 20:
                print("geronimo")
        else:
            print("bright")


    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        #print(line)
        #tokens = line.split()
        if line.startswith("start"):
            motionDuration = 9000000000
        if line.startswith("go"):
            soundDuration = 5000000000
        if line.startswith("onward"):
            soundDurationTwo = 5000000000
        # If the time has arrived to update the servo command signal:
    if motionDuration &gt; 0:
        if now &gt;= next_pwm_update:
            next_pwm_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
            pwm_level = 0.5 + 0.5 * math.sin(phase_angle)

            # convert a unit-range (0 to 1) pwm_level to a 16-bit integer representing a fraction
            new_duty_cycle = min(max(int(pwm_level * 2**16), 0), 2**16-1)

            # If the new value is less than a reasonable minimum, clamp to zero.
            # The pump motor will stall if the PWM fraction is too low; this turns
            # it off instead.
            if new_duty_cycle < 48000:
                pwm.duty_cycle = 0
            else:
                pwm.duty_cycle = new_duty_cycle

            phase_angle = (phase_angle + phase_rate * 0.020) % (1 * math.pi)

        # If either button is pressed, override the generated servo signal and run the motor.
        if cp.button_a or cp.button_b:
            pwm.duty_cycle = 2**16-1

        # If the time has arrived to display the status:
        if now &gt;= next_status_update:
            next_status_update += 500000000  # 0.5 sec in nanoseconds (2 Hz update)
            #print(pwm.duty_cycle)
        motionDuration -= elapsed_time
    else:
        pwm.duty_cycle = 0
        cp.stop_tone()
    if soundDuration &gt; soundDurationTwo:
        soundDurationTwo = 0
    elif soundDurationTwo &gt; soundDuration:
        soundDuration = 0

    if soundDuration &gt; 0 and soundDurationTwo == 0:
        if soundNow &gt;= nextSound:
            if pitch == 329:
                pitch = 493
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
            elif pitch == 493:
                pitch = 329
                cp.stop_tone()
                cp.start_tone(pitch)
                nextSound = soundNow + 500000000
        soundDuration -= elapsed_time
    elif soundDurationTwo &gt; 0 and soundDuration == 0:
        if soundNow &gt;= nextSound:
            if pitchTwo == 293:
                pitchTwo = 466
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
            elif pitchTwo == 466:
                pitchTwo = 293
                cp.stop_tone()
                cp.start_tone(pitchTwo)
                nextSound = soundNow + 500000000
        soundDurationTwo -= elapsed_time
    else:
        cp.stop_tone()

REFLECTION:

If there was more time, the team would have liked to make more masks and potentially create an installation with multiple of these masks being animated and producing sound together. It would have been interesting to explore this current iteration of the project in different contexts. The primary location for testing the final version of this project was the College of Fine Arts building; however, it would be interesting to see how the masks would be affected if used outside or in vastly more distant locations.

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5415/project-2-masks-off-anishwar-tirupathur-and-elise-delgado/feed/ 0
Class 27: Project 2 – Final Presentations https://courses.ideate.cmu.edu/16-376/s2021/5461/class-27/ https://courses.ideate.cmu.edu/16-376/s2021/5461/class-27/#respond Mon, 10 May 2021 13:28:44 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5461 Mon. May 10, 2021, 9:30AM to 11:30PM (Zoom)

Today’s class includes the remaining three project presentations.

Warm-up

  • Brief check-in.
  • Any questions?

Announcements


In-Class Activities

Each remaining project group will present their work for critique.


Final Report

  • The final report deadline will be 10AM on Monday May 17 to allow you to integrate any critique. We will not offer any extensions past this due date.

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5461/class-27/feed/ 0
Project 2: Defensive Vests – Helen and Rebecca K https://courses.ideate.cmu.edu/16-376/s2021/5378/project-2-defensive-vests-helen-and-rebecca-k/ https://courses.ideate.cmu.edu/16-376/s2021/5378/project-2-defensive-vests-helen-and-rebecca-k/#respond Mon, 10 May 2021 03:36:11 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5378 Concept

With the rise of AAPI hate in the past year, we set out to create defensive vests to start a conversation on what it means to feel unsafe as Asian American women. Our vests remind us of the precautions and protections that women take to feel safe. However, we also add our identity as Asian Americans to the purpose of the vest to highlight our vulnerability caused by race tensions in our nation.

Our vests are customized to represent our needs and defensive strategies in times of distress. However, they both retain two key components: one part of the vest is inflated with a hand pump and one part of the vest is inflated with a motor pump.

When the user presses their hand pump to inflate their vest, a signal is sent via a soft touch sensor to inflate their partner’s vest using their motor pump. The vest allows the user to seek physical comfort by squeezing a hand pump. It also allows the user to communicate their emotional state to their partner. Their partner’s vest flies into a protective mode, almost like a call to action or like someone rushing to rescue.

The Vests

Helen’s Vest

Exposed Organs

My vest is an abstract quilt of our major organs, with many individual pieces filled with different fabrics and stitched together. Besides the heart organ and the collar, which are inflated using the hand pump and motor pump, the other organs are inflated to varying degrees. The more vital the organ, the more inflated it is.

Controlling transparency of vest with different shirts

I paired my inflatable vest with a black long-sleeved shirt and a neutral-colored tank top to see how different bases can bring out different elements of the vest.

Lace

The infill in the vest is mainly pink, blue, and gray lace, with hints of sequins and glitter. Some organs support a variety of colors to represent its function, oxygen level, and value. The lace confetti is incased in the vest, juxtaposing both the delicacy of the lace with the protective purpose of the vest.

Collar

The exposed lace on the collar is reminiscent of a lion’s mane. The raw edges of the lace border creates a delicate yet prickly effect. It is both beautiful and unsettling, as the lace tassels flare out of the collar.

Rebecca’s Vest

In its resting state, my vest is boxy and asymmetrical. I created coral-like edges around the neckpiece; the ridges droop like wings on the shoulders. Brackets and lines coat the body of the vest.

While experimenting with patterns, I explored one-piece construction — having one body and vacuum as opposed to disassembled parts. The style mimicked a life vest and was visually consistent with the clear tubing I used to attach to the motors. I ended up using two pieces: one spanned from the entire left side to the right shoulder, and the other piece was a linear pouch motor to be actuated by the motor pump.

Heart Pump: sending signals

The heart pump has a soft sensor attached to it, so each squeeze is detected. The air itself is pumped into a compartment that is closed off and separated from the rest of the vest. It is coated with one layer of white paint to distinguish it from the other panels of the vest, and it is marked with the phrase “HELP OTW”.

Motor Pump: receiving signals

ascent

I used a layer of white paint to create a frosted effect on the part of the vest that is actuated by the motor so the vinyl wouldn’t be as revealing.

Reflection

As someone who likes to slowly refine my work as I progress, I felt exposed while working with a transparent medium. I addressed this by painting some areas of the vest and drawing patterns on both sides. I also appreciated Helen’s use of filling to beautify and also conceal some elements of her vest. Vinyl offered a lot of opportunities to play around with linework and edges, and I had a lot of fun with that.

I can now say I “used telematics to physically actuate and inflate a wearable,” which is an impressive sentence. It actually was really exciting to watch something on your body move at someone else’s signal, especially when that person is a distance away from you. It was also reaffirming to send a (homemade, on top of everything else) help signal and knowing a familiar person was at the other end of it. However, if Helen were to actually be in danger, I would feel helpless watching my vest slowly ascend to reveal some safety tools. If we had more time, Helen and I could refine this idea to make it more functional.

Given more time, we would explore adding more vests to this line, such that when one person is in distress, all the vests fly into action. The vests are meant to represent the need for safety when someone in our communities are distresses. In a sense, we are gearing up to protect our fellow community members as well as feeling their sense of danger and being affected by their distress as well.

Code

"""
Project 2: Defensive Vests

Helen Yu (heleny1), Rebecca Kim

Written by Helen Yu
Last Updated: 5/6/21

Summary: When you clench your pump and inflate your vest, your
partner's vest leaps into defense mode. A hobby servo helps control
the defense mechanism, be it spikes, projectiles, webs, etc.

Inputs: Soft Touch Sensor on A1
Outputs: Motor Pump on A2
"""
# ----------------------------------------------------------------
# Import any needed standard Python modules.
import time, math, sys

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import digitalio
import analogio
import pwmio

# Import the Adafruit helper library.
from adafruit_motor import servo

# Import the runtime for checking serial port status.
import supervisor

# ----------------------------------------------------------------
# Initialize hardware.

# Configure the digital input pin used for its pullup resistor bias voltage.
bias = digitalio.DigitalInOut(board.D10)        # pad A3
bias.switch_to_input(pull=digitalio.Pull.UP)

# Configure the analog input pin used to measure the sensor voltage.
sensor = analogio.AnalogIn(board.A1)
scaling = sensor.reference_voltage / (2**16)

# Create a PWMOut object (motor pump) on pad A2 to generate control signals.
pwm = pwmio.PWMOut(board.A2, duty_cycle=0, frequency=2000)

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

remote_touch = [0]

# Measure the time since the last remote move message, and reset after a period without data.
remote_touch_timer = False

# Convenient time constant expressed in nanoseconds.
second = 1000000000

# Integer time stamp for the next console output.
sensing_timer = time.monotonic()

# Integer time stamp for next behavior activity to begin.
next_activity_time = time.monotonic_ns() + 2 * second

# Flag to trigger motion.
inflate_state = False

phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second

# Integer time stamp for next servo update.
next_pump_update = time.monotonic_ns()

# The serial port output rate is regulated using the following timer variables.
serial_timer    = 0.0
serial_interval = 0.5

# ----------------------------------------------------------------
# Begin the main processing loop.
while True:

    # Read the current integer clock.
    now = time.monotonic()

     #---- soft sensor input and display -----------------------------
    # Read the integer sensor value and scale it to a value in Volts.
    volts = sensor.value * scaling

    # Normalize the soft sensor reading.  Typically you'll need to adjust these values to your device.
    low_pressure_voltage  = 0.65
    high_pressure_voltage = 0.20
    pressure = abs((high_pressure_voltage - volts) / (high_pressure_voltage - low_pressure_voltage))

     # Check the serial input for new line of remote data
    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        tokens = line.split()
        if len(tokens) == 1:
            try:
                remote_touch = [int(token) &gt; 0 for token in tokens]
                remote_touch_timer = 4.0
            except ValueError:
                pass

    #---- periodic console output -----------------------------------
    # Poll the time stamp to decide whether to emit console output.
    if now &gt;= sensing_timer:
        sensing_timer += 100000000 #0.1sec

    if pressure &gt; 0.5:
        remote_touch_timer = now + 4000000000 # 4 sec timeout

    if now &gt;= serial_timer:
        serial_timer += serial_interval
        touch = ["1" if pressure &gt; 0.5 else "0"]
        print(pressure)
        print(" ".join(touch))

    # If a slow movement has been received, sweep twice at a constant speed
    if inflate_state is True:
        pwm.duty_cycle = 2**16-1
        print("Defense activated.")
        time.sleep(10)
        pwm.duty_cycle = 0
        print("Defense deactivated")
        inflate_state = False

    if any(remote_touch):
        # Check whether there was any remote movement
        if remote_touch[0]:
            inflate_state = True
            print("Engage defense")
        remote_touch = [False]

    #---- periodic servo motion commands ----------------------------
    # If the time has arrived to update the servo command signal:
    if now &gt;= next_pump_update:
        next_pump_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
]]>
https://courses.ideate.cmu.edu/16-376/s2021/5378/project-2-defensive-vests-helen-and-rebecca-k/feed/ 0
Project #2: Rebecca Jean – Louis and Celia Kasberg https://courses.ideate.cmu.edu/16-376/s2021/5401/project-2-rebecca-jean-louis-and-celia-kasberg/ https://courses.ideate.cmu.edu/16-376/s2021/5401/project-2-rebecca-jean-louis-and-celia-kasberg/#respond Mon, 10 May 2021 02:00:18 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5401 Concept & Execution

The concept of our project is broken down into two major actions and reactions. The first idea mimics the fact that our professors can be our number one fans as we draw near to end of another year. The second idea centers around how students at times need a break, a rest, and want to be able to slow down the pace that our classes enforce. These two ideas were able to be told by the pace and movements of the marionettes which are based off of Garth and Olivia.

Our idea of marionettes came to life when we thought about telling a story through fabric. We thought the best way to do this was to emote and bring to reality what control looks like. As students many of our decisions are decided by others: our advisors, professors, and ultimately our institutions. We wanted students to be in control for once.

In the video we showcase the first idea, where one sensor pad starts the movement and the other increases speed. As time goes on, professors encourage students to push through final exams and projects, so they bring more encouragement and joy to keep us afloat. This increase in speed is due to their increased motivation and their desire to see us succeed. Secondly, the marionettes move at a quickened pace, but at times students need their classes and workload to slow down. We need a break and just time to take a breath. This is very crucial since we are always on the go and this rest would alleviate unneeded stress and worry.

Process

Video

Conclusion

We wanted our movements to be very intentional; we knew the movement of the puppets would serve a great purpose and we wanted to ensure the motors could emote these feelings. With the two settings the professors either quicken in pace or slow down if the students need a break. The sensors allow for users to control the movements easily and be accessible. We decided to enclose the puppets into one stage, to truly see how the puppets take up space in one environment and how their actions look beside each other. As the Garth puppet moves quicker and quicker it travels in great distance towards the back of the box. The Garth puppet seems to be light on its feet, just flying. Whereas the Olivia puppet floats up and down and uses her light and flowy fabric to put on a show for everyone. After combining all of these elements together, we felt we were able to truly echo the presence of Garth and Olivia and explore through telematics what it means for a student to take control!

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5401/project-2-rebecca-jean-louis-and-celia-kasberg/feed/ 0
Assignment 24: Rebecca K and Helen https://courses.ideate.cmu.edu/16-376/s2021/5323/assignment-24-rebecca-k-and-helen/ https://courses.ideate.cmu.edu/16-376/s2021/5323/assignment-24-rebecca-k-and-helen/#respond Thu, 06 May 2021 14:31:44 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5323 Helen’s Vest

The hand pump inflates the heart shaped organ. Inflating the heart tightens the vest, offering physical security to the user.

The motor pump inflates the collar. The inflated collar offers comfort and protection around the neck.

gravity pulls lace confetti down
vest is “zippered” with ties
individual pieces are secured with thread

Why inflate the collar? The inflation of the collar creates comfort and protection. The comfort comes from the collar’s similarity to a travel pillow. The protection comes from the fact that the inflated collar shields a vulnerable area. Often when people are anxious, their hand goes to their neck as a subconscious way to protect themselves.

Still to do: add more movement and textiles to the collar to enhance the effect, finishing touches on the body of the vest,

Rebecca’s Vest

I am planning to use an inflatable pouch motor to slowly rise the way a garage door does to reveal the safety kit underneath. See a prototype perform that here:

It travels a considerable vertical distance.

I was alternating between the ideas of inflating one large vacuum and inflating a bunch of smaller pouches. To create each small pouch, I added an extra segment that would hold the bolt connector and be soldered off after inflating the pouch with the motor pump. However, after spending too much time troubleshooting each pouch and looking for holes and accidentally soldering off vinyl, I decided to commit to the idea of creating a one-piece vest.

deflated pouches

Following up on the last check-in post, I created “darts” in a one piece vest to form folds around the bends of the body. With the dart:

Without the dart:

Because there is only one motor pump which is going to inflate the pouch motor shown in the video, I considered using the heart pump to inflate the one-vacuum vest. I quickly discarded that idea.

So, I have to create three separate compartments: one pouch motor, one segment to inflate with the heart pump so I can send Helen my signal, and the body of the vest (which will hold my safety kit).

Code

"""
Project 2: Active Proxy Body

Helen Yu (heleny1), Rebecca Kim

Written by Helen Yu
Last Updated: 5/4/21

Summary: When you clench your pump and inflate your vest, your
partner's vest leaps into defense mode. A hobby servo helps control
the defense mechanism, be it spikes, projectiles, webs, etc.

Inputs: Soft Touch Sensor on A1
Outputs: Motor Pump on A2
"""
# ----------------------------------------------------------------
# Import any needed standard Python modules.
import time, math, sys

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import digitalio
import analogio
import pwmio

# Import the Adafruit helper library.
from adafruit_motor import servo

# Import the runtime for checking serial port status.
import supervisor

# ----------------------------------------------------------------
# Initialize hardware.

# Configure the digital input pin used for its pullup resistor bias voltage.
bias = digitalio.DigitalInOut(board.D10)        # pad A3
bias.switch_to_input(pull=digitalio.Pull.UP)

# Configure the analog input pin used to measure the sensor voltage.
sensor = analogio.AnalogIn(board.A1)
scaling = sensor.reference_voltage / (2**16)

# Create a PWMOut object (motor pump) on pad A2 to generate control signals.
pwm = pwmio.PWMOut(board.A2, duty_cycle=0, frequency=2000)

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

remote_touch = [0]

# Measure the time since the last remote move message, and reset after a period without data.
remote_touch_timer = False

# Convenient time constant expressed in nanoseconds.
second = 1000000000

# Integer time stamp for the next console output.
sensing_timer = time.monotonic()

# Integer time stamp for next behavior activity to begin.
next_activity_time = time.monotonic_ns() + 2 * second

# Flag to trigger motion.
inflate_state = False

phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second

# Integer time stamp for next servo update.
next_pump_update = time.monotonic_ns()

# The serial port output rate is regulated using the following timer variables.
serial_timer    = 0.0
serial_interval = 0.5

# ----------------------------------------------------------------
# Begin the main processing loop.
while True:

    # Read the current integer clock.
    now = time.monotonic()

     #---- soft sensor input and display -----------------------------
    # Read the integer sensor value and scale it to a value in Volts.
    volts = sensor.value * scaling

    # Normalize the soft sensor reading.  Typically you'll need to adjust these values to your device.
    low_pressure_voltage  = 0.65
    high_pressure_voltage = 0.20
    pressure = abs((high_pressure_voltage - volts) / (high_pressure_voltage - low_pressure_voltage))

     # Check the serial input for new line of remote data
    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        tokens = line.split()
        if len(tokens) == 1:
            try:
                remote_touch = [int(token) &gt; 0 for token in tokens]
                remote_touch_timer = 4.0
            except ValueError:
                pass

    #---- periodic console output -----------------------------------
    # Poll the time stamp to decide whether to emit console output.
    if now &gt;= sensing_timer:
        sensing_timer += 100000000 #0.1sec

    if pressure &gt; 0.5:
        remote_touch_timer = now + 4000000000 # 4 sec timeout

    if now &gt;= serial_timer:
        serial_timer += serial_interval
        touch = ["1" if pressure &gt; 0.5 else "0"]
        print(pressure)
        print(" ".join(touch))

    # If a slow movement has been received, sweep twice at a constant speed
    if inflate_state is True:
        pwm.duty_cycle = 2**16-1
        print("Defense activated.")
        time.sleep(10)
        pwm.duty_cycle = 0
        print("Defense deactivated")
        inflate_state = False

    if any(remote_touch):
        # Check whether there was any remote movement
        if remote_touch[0]:
            inflate_state = True
            print("Engage defense")
        remote_touch = [False]

    #---- periodic servo motion commands ----------------------------
    # If the time has arrived to update the servo command signal:
    if now &gt;= next_pump_update:
        next_pump_update += 20000000  # 20 msec in nanoseconds (50 Hz update)

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5323/assignment-24-rebecca-k-and-helen/feed/ 0
Class 26: Project 2 – First Presentations https://courses.ideate.cmu.edu/16-376/s2021/5372/class-26/ https://courses.ideate.cmu.edu/16-376/s2021/5372/class-26/#respond Thu, 06 May 2021 14:29:12 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5372 Thu. May 6, 2021, 10:30AM to 12:20PM (Zoom)

Today’s class includes one project presentation and other final interventions.

Warm-up

  • Brief check-in.
  • Any questions?

Announcements

  • We will keep our Monday, May 10 (9:30AM – 11:30AM) exam window. All students will be expected to attend this session.
  • Any group that would like to give a practice talk early on Monday (prior to the 9:30 session), please send us a note.

In-Class Activities

Private practice talk for the one group presenting today.

Work session for everyone else.

Near the end of class one group will present their work for critique.


Assignment 25

  • Prepare for the final critique, including a draft of your final report post.
  • The final report deadline will be 10AM on Monday May 17 to allow you to integrate any critique. We will not offer any extensions past this due date.

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5372/class-26/feed/ 0
Assignment 24: Masks Off https://courses.ideate.cmu.edu/16-376/s2021/5342/assignment-24-masks-off/ https://courses.ideate.cmu.edu/16-376/s2021/5342/assignment-24-masks-off/#respond Thu, 06 May 2021 13:22:18 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5342
Motor Running Even Though Duty Cycle is 0

Anishwar~ I’m still struggling to solve the problems from class the other day. The motor pump is still constantly pumping air; additionally, I am having trouble with getting the non-blocking version of the sound part of the code working. However, one success was getting the program to run only when there isn’t any light. While, the motor pump was still running because of the aforementioned issue the duty cycle was 0 and no sound played.

Motor pump under new fabric

Elise~ I found new fabric that stretches well with the pneumatic. I had two versions that worked but I think a bigger inflatable makes more movement. Also tested code: It turned green when in brighter light and back to blue when in darker light. The tones changed with the light but no motor pump output.

Code tested on my end
# cpb_pump_control.py

# Demonstrate control of an air pump based on a DC motor driven via a MOSFET
# transistor driver circuit *or* a DRV8833 motor driver in a single-ended mode.
#
#  1. The driver PWM input           connects to A2
#  2. The battery ground line        connects to GND

# Related documentation:
# https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html#module-pwmio
# https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#module-adafruit_motor.servo

# ----------------------------------------------------------------
# Import standard Python modules.
import time, math

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import pwmio

# ----------------------------------------------------------------
# Initialize hardware.

# Create a PWMOut object on pad A2 to generate control signals.
pwm = pwmio.PWMOut(board.A3, duty_cycle=0, frequency=20000)

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second
next_pwm_update = time.monotonic_ns()
next_status_update = time.monotonic_ns()
toneCheck = True
nextSound = time.monotonic_ns()

# ----------------------------------------------------------------
# Enter the main event loop.
while True:
    # Read the current integer clock.
    now = time.monotonic_ns()
    soundNow = time.monotonic_ns()


    # If the time has arrived to update the servo command signal:
    if cp.light < 81:
        if now &gt;= next_pwm_update:
            next_pwm_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
            pwm_level = 0.5 + 0.5 * math.sin(phase_angle)

            # convert a unit-range (0 to 1) pwm_level to a 16-bit integer representing a fraction
            new_duty_cycle = min(max(int(pwm_level * 2**16), 0), 2**16-1)

            # If the new value is less than a reasonable minimum, clamp to zero.
            # The pump motor will stall if the PWM fraction is too low; this turns
            # it off instead.
            if new_duty_cycle < 48000:
                pwm.duty_cycle = 0
            else:
                pwm.duty_cycle = new_duty_cycle

            phase_angle = (phase_angle + phase_rate * 0.020) % (2 * math.pi)

        # If either button is pressed, override the generated servo signal and run the motor.
        if cp.button_a or cp.button_b:
            pwm.duty_cycle = 2**16-1

        # If the time has arrived to display the status:
        if now &gt;= next_status_update:
            next_status_update += 500000000  # 0.5 sec in nanoseconds (2 Hz update)
            print(pwm.duty_cycle)

        if cp.light < 81 and cp.light &gt;= 36:
            cp.play_tone(329,.5)
            cp.play_tone(493,.5)
            cp.pixels.fill((0,255,0))
            
        elif cp.light < 36:
            cp.play_tone(293,.5)
            cp.play_tone(466,.5)
            cp.pixels.fill((0,0,255))
    else:
        pwm.duty_cycle = 0
        print("ok")



Below is my attempt to try making the sounds play using a non-blocking code; however, it doesn’t seem to be working and I need to continue messing with it.

Elise~ tested and needed A2 to change to A3 like previous code. Motor and light combination has some issues moving smoothly.

Testing second code set
# cpb_pump_control.py

# Demonstrate control of an air pump based on a DC motor driven via a MOSFET
# transistor driver circuit *or* a DRV8833 motor driver in a single-ended mode.
#
#  1. The driver PWM input           connects to A2
#  2. The battery ground line        connects to GND

# Related documentation:
# https://circuitpython.readthedocs.io/en/latest/shared-bindings/pwmio/index.html#module-pwmio
# https://circuitpython.readthedocs.io/projects/motor/en/latest/api.html#module-adafruit_motor.servo

# ----------------------------------------------------------------
# Import standard Python modules.
import time, math

# Import the board-specific input/output library.
from adafruit_circuitplayground import cp

# Import the low-level hardware libraries.
import board
import pwmio

# ----------------------------------------------------------------
# Initialize hardware.

# Create a PWMOut object on pad A2 to generate control signals.
pwm = pwmio.PWMOut(board.A2, duty_cycle=0, frequency=20000)

# ----------------------------------------------------------------
# Initialize global variables for the main loop.

phase_angle = 0.0
cycle_duration = 12                       # seconds per cycle
phase_rate  = 2*math.pi / cycle_duration  # radians/second
next_pwm_update = time.monotonic_ns()
next_status_update = time.monotonic_ns()
toneCheck = True
nextSound = time.monotonic_ns()

# ----------------------------------------------------------------
# Enter the main event loop.
while True:
    # Read the current integer clock.
    now = time.monotonic_ns()
    soundNow = time.monotonic_ns()


    # If the time has arrived to update the servo command signal:
    if now &gt;= next_pwm_update:
        next_pwm_update += 20000000  # 20 msec in nanoseconds (50 Hz update)
        pwm_level = 0.5 + 0.5 * math.sin(phase_angle)

        # convert a unit-range (0 to 1) pwm_level to a 16-bit integer representing a fraction
        new_duty_cycle = min(max(int(pwm_level * 2**16), 0), 2**16-1)

        # If the new value is less than a reasonable minimum, clamp to zero.
        # The pump motor will stall if the PWM fraction is too low; this turns
        # it off instead.
        if new_duty_cycle < 48000:
            pwm.duty_cycle = 0
        else:
            pwm.duty_cycle = new_duty_cycle

        phase_angle = (phase_angle + phase_rate * 0.020) % (2 * math.pi)

    # If either button is pressed, override the generated servo signal and run the motor.
    if cp.button_a or cp.button_b:
        pwm.duty_cycle = 2**16-1

    # If the time has arrived to display the status:
    if now &gt;= next_status_update:
        next_status_update += 500000000  # 0.5 sec in nanoseconds (2 Hz update)
        print(pwm.duty_cycle)

    if cp.light < 81 and cp.light &gt;= 36:
        print(soundNow)
        print(nextSound)
        if soundNow &gt;= nextSound:
            print("something")
            cp.start_tone(329)
            nextSound = soundNow + 500000000
            cp.stop_tone()
        else:
            print("cool")
            cp.start_tone(493)
            cp.stop_tone()
    elif cp.light < 36:
        cp.play_tone(293,.5)
        cp.play_tone(466,.5)
    else:
        cp.stop_tone()



]]>
https://courses.ideate.cmu.edu/16-376/s2021/5342/assignment-24-masks-off/feed/ 0
Assignment 24: Rebecca and Celia https://courses.ideate.cmu.edu/16-376/s2021/5349/assignment-24-rebecca-and-celia/ https://courses.ideate.cmu.edu/16-376/s2021/5349/assignment-24-rebecca-and-celia/#respond Thu, 06 May 2021 05:33:35 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5349 We met with Garth earlier today and were able to transfer data back and forth to eachother through MQTT! We updated the code, to take account of the data being processed into the command, as well as adjusting the collaboration of the sensor pad. Our next goal is to incorporate another sensor pad so we can control speed along with the direction of the puppets. We have also made plans to come together and assemble the environment that the puppets will be in. We would like to invite other students visit our puppet show this weekend if they would like to participate!

import time
import supervisor
import sys

# Import the low-level hardware libraries.
import board
import digitalio
import analogio
import pwmio

from adafruit_circuitplayground import cp
from adafruit_motor import servo


# ----------------------------------------------------------------
# Initialize hardware.

# Create a PWMOut object on pad SDA A5 to generate control signals.
pwm = pwmio.PWMOut(board.A5, duty_cycle=0, frequency=50)

# Create a Servo object which controls a hobby servo using the PWMOut.
actuator = servo.Servo(pwm, min_pulse=1000, max_pulse=2000)


# Create a PWMOut object on pad SCL A4 to generate control signals.
pwm2 = pwmio.PWMOut(board.A4, duty_cycle=0, frequency=50)

# Create a Servo object which controls a hobby servo using the PWMOut.
actuator2 = servo.Servo(pwm2, min_pulse=1000, max_pulse=2000)

# Configure the digital input pin used for its pullup resistor bias voltage.
bias = digitalio.DigitalInOut(board.D10)
bias.switch_to_input(pull=digitalio.Pull.UP)

# Configure the analog input pin used to measure the sensor voltage.
sensor = analogio.AnalogIn(board.A2)
scaling = sensor.reference_voltage / (2**16)


last_touch = 0
touched_arm_one = 0
touched_arm_two = 0
touched_leg_one = 0
touched_leg_two = 0

def flush_check():
    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()


# ----------------------------------------------------------------
# Begin the main processing loop.
while True:
    # Read the integer sensor value and scale it to a value in Volts.
    volts = sensor.value * scaling
    touched_arm_one = 0
    pressure_one = (3.2 - volts) / (3.2 - 0.4)
    print(pressure_one) #function to send data 
    pressure_two = (1.4 - volts) / (1.4 - 0.1)

    if supervisor.runtime.serial_bytes_available:
            line = sys.stdin.readline()
            tokens = line.split()
            if len(tokens) == 1 and float(tokens[0]) &gt; .90:
                touched_arm_one = 1

    if touched_arm_one:
        for angle in range(0, 180, 5):
            actuator.angle = angle
            time.sleep(0.02)
            flush_check()
        for angle in range(180, 0, -5):
            actuator.angle = angle
            time.sleep(0.02)
            flush_check()
    time.sleep(.5)



]]>
https://courses.ideate.cmu.edu/16-376/s2021/5349/assignment-24-rebecca-and-celia/feed/ 0
Project 2: Desk Pets – Kanvi and Yael https://courses.ideate.cmu.edu/16-376/s2021/5330/project-2-desk-pets-kanvi-and-yael/ https://courses.ideate.cmu.edu/16-376/s2021/5330/project-2-desk-pets-kanvi-and-yael/#respond Thu, 06 May 2021 04:58:46 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5330 Concept

The two of us were interested in exploring a rivalry between two devices that respond to very deliberate stimulus and communicate that stimulus between each other. We were drawn to the idea of creating small expressive creatures that beg for the users attention and compete with each other almost as siblings do. We were also interested in creating a sort of ‘counter’ that is communicated back and forth through the MQTT connection in order to keep track of which Desk Pet is in the lead and which is losing.

We see the narrative as two connected desk pets that both seek to gain more attention from their users than the other. They communicate to the user and to each other through idle movement, boasting when they receive more attention, and begging when they are receiving less than their other pair.

Process

Our initial ideas for how to express a rivalry between two Desk Pets was through the stretching and folding of fabric. In order to determine the best type of fabric to use and the best type of connection to the servos, we did a few material studies.

The two of us quickly found that muslin and wooden sticks were too stiff to generate our desired effects. From there, we moved on to fabrics that stretched more.

Along with the fabric tests, we also considered different mounting options for the servos. We began rather ambitious, expecting the use of 4 servos per person. By the end, we were able to cut the servos down to 2 per person, but maintain 4 points of contact by doubling up on each servo.

Once we had the method of movement and the best type of fabric and connection figured out, we were driven to finding a better form for out Desk Pet. The flat fabric was ideal for stretching, but it lacked depth and character. This inspired the new form of the Desk Pet that also included a more expressive face.

This new form inspired our idea for where to place the touch sensor. Making the head itself the sensor proved to be too difficult, but it shone a light on the option of making the touch sensor something detached that still rests atop the head- a hat! We stuck a touch sensor between the fabric attached to the hat to pit it between the form and the fabric and connected it accordingly to allow for the Desk Pet to respond to touch stimulus.

We created three different states for our Desk Friends.

Jealous begging and happy movements:

Lonely movements:

Execution

Our final Desk Pet looks like this:

Code

# Kinetic Fabrics S21
# Kanvi and Yael

# -----------------------------------------
# Import the standard Python time functions.
import time

# Import the low-level hardware libraries.
import board
import digitalio
import analogio

import pwmio
from adafruit_circuitplayground import cp
from adafruit_motor import servo

import supervisor
import sys

analogin = analogio.AnalogIn(board.A2)

# Create a PWMOut object on pad SDA A5 to generate control signals.
pwm1 = pwmio.PWMOut(board.A5, duty_cycle=0, frequency=50)

# Create a PWMOut object on pad A3 to generate control signals.
pwm2 = pwmio.PWMOut(board.A3, duty_cycle=0, frequency=50)

# Create a Servo object which controls a hobby servo using the PWMOut.
servo1 = servo.Servo(pwm1, min_pulse=1000, max_pulse=2000)

# Create a Servo object which controls a hobby servo using the PWMOut.
servo2 = servo.Servo(pwm2, min_pulse=1000, max_pulse=2000)

# adjust these for indivudal projects
HAPPY_START = 0
HAPPY_STOP = 30
HAPPY_SERVO2_OFFSET = 60

BEG_START = 90
BEG_STOP = 10

#########################################


def getVoltage(pin):  # helper
    return (pin.value * 3.3) / 1023


def happy(level):
    for i in range(10):
        for angle in range(HAPPY_START, HAPPY_STOP, 1):
            servo1.angle = angle
            servo2.angle = angle + HAPPY_SERVO2_OFFSET
            time.sleep(0.1 / level)

        for angle in range(HAPPY_STOP, HAPPY_START, -1):
            servo1.angle = angle
            servo2.angle = angle + HAPPY_SERVO2_OFFSET
            time.sleep(0.1 / level)


def beg():
    for i in range(10):
        for angle in range(HAPPY_START, HAPPY_STOP, 1):
            servo1.angle = angle
            time.sleep(0.1)

        for angle in range(HAPPY_START, HAPPY_STOP, 1):
            servo2.angle = angle + HAPPY_SERVO2_OFFSET
            time.sleep(0.1)

        for angle in range(HAPPY_STOP, HAPPY_START, -1):
            servo1.angle = angle
            time.sleep(0.1)

        for angle in range(HAPPY_STOP, HAPPY_START, -1):
            servo2.angle = angle + HAPPY_SERVO2_OFFSET
            time.sleep(0.1)


def neglect():
    for i in range(2):
        for angle in range(BEG_START, BEG_STOP, -1):
            servo2.angle = angle
            time.sleep(0.02)

        for angle in range(BEG_STOP, BEG_START, 1):
            servo2.angle = angle
            time.sleep(0.02)

        for angle in range(BEG_STOP, BEG_START, 1):
            servo1.angle = angle
            time.sleep(0.02)

        for angle in range(BEG_START, BEG_STOP, -1):
            servo1.angle = angle
            time.sleep(0.02)


petCount = 0
lonely = 0

while True:
    if supervisor.runtime.serial_bytes_available:
        line = sys.stdin.readline()
        print("hi")
        if line == "beg lots":
            reading = getVoltage(analogin)
            # print("Analog Voltage: %f" % reading)
            while reading < 10:
                beg()
            happy(16)

    reading = getVoltage(analogin)
    #print("Analog Voltage: %f" % reading)

    if reading &gt; 10:
        petCount += 1
    else:
        lonely += 1

    if petCount == 16:
        print("beg lots")
        happy(petCount)
        petCount = 0
        lonely = 0

    if lonely &gt; 50:
        neglect()
        lonely = 0

    time.sleep(0.2)

Conclusion and Reflection

This project was so much fun! We had a lot of fun building these Desk Pets and their accompanying code. The material and servo studies at the beginning were extremely helpful in informing our final outcome and we expect these studies to continue to inform our design decisions in future projects to come. The combination of several servos and various levels of input were also challenging to implement at first but very worthwhile by the end of the project.

There is so much more that the two of us would love to develop had we more time to continue working on these Desk Pets. Our initial concepts included multiple stimulus inputs including light and sound along with touch. We both feel that had we more time, the project would be much more compelling with those additional inputs to create different outputs that aren’t in the current project. In our initial concept, we also discussed having a visual ‘counter’ that displayed how ahead or behind the users Desk Pet was with attention received. We chose to leave it out in this iteration due to not having a good way of connecting the circuit playground to the constructed Desk Pet. We felt that by having the counter elsewhere on the table, flashing bright lights at the user, it would be too distracting and take away from the interesting folding and stretching of the fabric.

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5330/project-2-desk-pets-kanvi-and-yael/feed/ 0
Assignment 23: Masks Off https://courses.ideate.cmu.edu/16-376/s2021/5297/assignment-23-masks-off/ https://courses.ideate.cmu.edu/16-376/s2021/5297/assignment-23-masks-off/#respond Tue, 04 May 2021 14:39:23 +0000 https://courses.ideate.cmu.edu/16-376/s2021/?p=5297

Anishwar: I experimented with using the DC Motor Pump to inflate a pneumatic that would correspond differently to different eyes. I think this experiment was quite successful at creating a pneumatic that could inflate two different areas of the mask.

]]>
https://courses.ideate.cmu.edu/16-376/s2021/5297/assignment-23-masks-off/feed/ 0