Belay on!

Problem:

In rock climbing, when ascending a natural rock face, rock climbers use a technique called lead climbing. This is different from toprope climbing, where the rope is already tethered to the top of the route. Here, the lead climber brings the rope up the rock surface with them.

(Source)

More details about lead climbing can be found here.

Due to the physical position of the belayer, it is incredibly tiring for them to continuously keep a lookout on the climber. Sometimes, this is even impossible if there are outcrops of rock that occludes the belayer’s line of sight.

There are some tools today such as belay glasses that try to relieve the neck strain of belayers, but it is still a tiring job and it’s incredibly easy to make a mistake, when you take your eyes off of the climber or look down for a break.

In fact, it is so tedious and strenuous that lots of pro climbers get into accidents and falls often while lead climbing. How can we provide visual feedback to help reduce incidence of accidents and injury?

Solution:

Creating a system of visual feedback where there’s an indicator signaling how high or low the tension is in the rope. Compared to regular toprope, there is little physical feedback on the belayer on how taut the lead climber’s rope actually is. By introducing more indicators on the belay device, the belayer will be more aware of the rope tension as this feedback will be visible within their line of sight, and on the belay device.

(Source)

Part 1: Preventing falling

(Source)

One of the greatest causes of accidents is slow or absent braking when the lead climber falls. At all times, except when letting in more rope, the belayer should apply friction to the right side of the belay device to act as an emergency brake. In this visual feedback system, if there is insufficient tension applied, a red light will start blinking. Once it is pulled taut enough to withstand a fall from the lead climber, it will stop blinking.

Part 2: Slack awareness

One of the other most important factors in lead climbing is providing the lead climber with the appropriate amount of slack. If it’s too little, they can’t stretch or move far enough to progress. If it’s too much, they run the risk of a highly injurious fall if they do end up slipping. By introducing a color indicator of the range of rope tension, we enable belayers to better monitor this more closely and make adjustments more quickly. As the tension moves low to high, the range is indicated on the row of lights.

Side view, for better view of colors in visual feedback:

Front view demo of the whole system:

Schematic:

Code:

#define belayLeft   1 //left pot
#define belayRight  0 //right pot

//to store tension on upward and downard tension on belay device
int upwardPull = 0;
int downwardPull = 0;

//LED pins in a row, left
int Blue1 = 3;
int Blue2 = 4;
int Green1 = 5;
int Green2 = 6;
int Yellow1 = 7;
int Yellow2 = 8;

//Blinking LED, right
int R1 = 9;

void setup() {
  Serial.begin(9600);

  pinMode(Blue1, OUTPUT);
  pinMode(Blue2, OUTPUT);
  pinMode(Green1, OUTPUT);
  pinMode(Green2, OUTPUT);
  pinMode(Yellow1, OUTPUT);
  pinMode(Yellow2, OUTPUT);
  pinMode(R1, OUTPUT);

  // put your setup code here, to run once:
  
}

void loop() {
  // read tension on both sides of belay device
  upwardPull = analogRead(belayLeft);
  downwardPull = analogRead(belayRight);

  //show range of tension in upwards belay
  if (upwardPull <= 50){
    digitalWrite(Blue1, HIGH);
    digitalWrite(Blue2, LOW);
    digitalWrite(Green1, LOW);
    digitalWrite(Green2, LOW);
    digitalWrite(Yellow1, LOW);
    digitalWrite(Yellow2, LOW);
  }

  else if (upwardPull > 50 && upwardPull <= 100){
    digitalWrite(Blue1, HIGH);
    digitalWrite(Blue2, HIGH);
    digitalWrite(Green1, LOW);
    digitalWrite(Green2, LOW);
    digitalWrite(Yellow1, LOW);
    digitalWrite(Yellow2, LOW);
  }

  else if (upwardPull > 100 && upwardPull <= 150){
    digitalWrite(Blue1, HIGH);
    digitalWrite(Blue2, HIGH);
    digitalWrite(Green1, HIGH);
    digitalWrite(Green2, LOW);
    digitalWrite(Yellow1, LOW);
    digitalWrite(Yellow2, LOW);
  }

  else if (upwardPull > 150 && upwardPull <= 200){
    digitalWrite(Blue1, HIGH);
    digitalWrite(Blue2, HIGH);
    digitalWrite(Green1, HIGH);
    digitalWrite(Green2, HIGH);
    digitalWrite(Yellow1, LOW);
    digitalWrite(Yellow2, LOW);
  }

  else if (upwardPull > 200 && upwardPull <= 220){
    digitalWrite(Blue1, HIGH);
    digitalWrite(Blue2, HIGH);
    digitalWrite(Green1, HIGH);
    digitalWrite(Green2, HIGH);
    digitalWrite(Yellow1, HIGH);
    digitalWrite(Yellow2, LOW);
  }

  else if (upwardPull > 220){
    digitalWrite(Blue1, HIGH);
    digitalWrite(Blue2, HIGH);
    digitalWrite(Green1, HIGH);
    digitalWrite(Green2, HIGH);
    digitalWrite(Yellow1, HIGH);
    digitalWrite(Yellow2, HIGH);
  }


   //Red LED blinks if safety is not 'on'
   if (downwardPull < 100){
    digitalWrite(R1, HIGH);
    delay(100);
    digitalWrite(R1, LOW);
    delay(100);
   }else{
    digitalWrite(R1, LOW);
  }

  // debugging
//    Serial.print("   left side of belay device ");
    Serial.println(upwardPull);
//    Serial.print("   right side of belay device");
//    Serial.println(downwardPull);

}

 

Change of tune

Problem:
There are many factors that have to be considered in managing risk of contagion during the pandemic. One particularly difficult element to get a good gauge on and keep continuous track of is is how well ventilated the air around you is at any given time.

This is crucial and can counterbalance whether it is safe to remain where you are at any given point of time. It can significantly overcome the risk odds of being less than 6ft away from people, both indoors and outdoors.

Without ventilation, aerosols remain suspended in the air, becoming increasingly concentrated as time goes by. (Source)

Solution:
A concealed wind sensor housed in a lapel pin that gives you audio feedback about the air ventilation around you. Ideally, this would be able to connect to your phone or audio device wirelessly so that you can check on this discretely whenever needed.
To engage, push a button on the pin and the reading will be sent to your audio device.
Visual indicators:
For demo purposes, I have added LEDs to help visualize the invisible elements present in the solution.
White LED: visual feedback of wind sensor values changing
Green/yellow/red LED: indicator of what range of ventilation is safe or not.
Audio feedback:
I chose 3 different tunes for the 3 risk levels:
High risk – morse code for SOS
Medium risk – tense, suspenseful tune
Low risk – major key arpeggio as a simple, positive sounding indicator.
Demo:

At the start, the wind sensor reads a low level of ventilation, and starts playing the SOS tune. As the ventilation increases, it switches to the low risk tune of a major arpeggio. As that value falls slightly, it starts playing the suspenseful tune before reverting back to the major arpeggio as the values increase again.

Components:

  • 1x RGB Diffused Common Cathode
  • 3x LED (Red, Green, Yellow)
  • 1x button
  • 7x Resistor 220 ohm
  • Wind Sensor Rev. C
  • 35CSB speaker

Schematic:

Code:

/*************************************************
* Sound Sensor
*************************************************/
#define analogPinForRV    1   // blue jumper wire
#define analogPinForTMP   0   // yellow jumper wire

// to calibrate your sensor, put a glass over it, but the sensor should not be
// touching the desktop surface however.
// adjust the zeroWindAdjustment until your sensor reads about zero with the glass over it. 

const float zeroWindAdjustment =  .2; // negative numbers yield smaller wind speeds and vice versa.

int TMP_Therm_ADunits;  //temp termistor value from wind sensor
float RV_Wind_ADunits;    //RV output from wind sensor 
float RV_Wind_Volts;
unsigned long lastMillis;
int TempCtimes100;
float zeroWind_ADunits;
float zeroWind_volts;
float WindSpeed_MPH;

//LED Feedback
int redPin = 9; //Pin for the red RGB led pin
int greenPin = 10; //Pin for the green RGB led pin
int bluePin = 11; //Pin for the blue RGB led pin 

int writeValue_red; //declare variable to send to the red LED
int writeValue_green; //declare variable to send to the green LED
int writeValue_blue; //declare variable to send to the blue LED



/*************************************************
* Interrupt Button
*************************************************/
static const int togglePin = 5;
bool buttonState = false;
const bool isInterrupt = true;

/*************************************************
* Melodies
*************************************************/
#include "pitches.h"
int speakerPin = 8;

// notes in the major melody (9 notes):
int majorMelody[] = {
NOTE_C4, NOTE_D4,NOTE_G4, NOTE_C5, 0, NOTE_C4, NOTE_D4, NOTE_G4, NOTE_C5};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int majorNoteDurations[] = {
   4, 4, 4, 4, 4, 4, 4, 4, 4
};

// notes in the minor melody (9 notes):
int minorMelody[] = {
NOTE_FS4, NOTE_FS4, NOTE_FS4, NOTE_DS4, 0, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_CS4};
// note durations: 4 = quarter note, 8 = eighth note, etc.:

int minorNoteDurations[] = {
   8, 8, 8, 2, 4, 8, 8, 8, 2
};

// notes in morse code (11 notes):
int SOS[] = {
  NOTE_GS5, NOTE_GS5, NOTE_GS5, 0, NOTE_GS5, NOTE_GS5, NOTE_GS5, 0, NOTE_GS5, NOTE_GS5, NOTE_GS5
};

int SOSDurations[] = {
  8, 8, 8, 4, 2, 2, 2, 4, 8, 8, 8
};


/*************************************************
 * 
 * SETUP
 * 
*************************************************/

//void SwitchPressed(){
//  buttonState =! buttonState;
//}

void setup() {

  Serial.begin(57600);   // faster printing to get a bit better throughput on extended info
  // remember to change your serial monitor

  Serial.println("start");
  // put your setup code here, to run once:

  //   Uncomment the three lines below to reset the analog pins A2 & A3
  //   This is code from the Modern Device temp sensor (not required)
  pinMode(A2, INPUT);        // GND pin      
  pinMode(A3, INPUT);        // VCC pin
  digitalWrite(A3, LOW);     // turn off pullups

  //initialize button pin as input
//  pinMode(buttonPin, INPUT_PULLUP);

  //Risk feedback state
  int greenLED = 2;
  int yellowLED = 3;
  int redLED = 4;
  pinMode(greenLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  pinMode(redLED, OUTPUT);

//  if (isInterrupt){
//      attachInterrupt(digitalPinToInterrupt(togglePin), SwitchPressed, RISING);    
//  }
  
}

void loop() {
  
/*************************************************
* Reading Wind Sensor
*************************************************/
  if (millis() - lastMillis > 200){      // read every 200 ms - printing slows this down further
    
    TMP_Therm_ADunits = analogRead(analogPinForTMP);
    RV_Wind_ADunits = analogRead(analogPinForRV);
    RV_Wind_Volts = (RV_Wind_ADunits *  0.0048828125);

    // these are all derived from regressions from raw data as such they depend on a lot of experimental factors
    // such as accuracy of temp sensors, and voltage at the actual wind sensor, (wire losses) which were unaccouted for.
    TempCtimes100 = (0.005 *((float)TMP_Therm_ADunits * (float)TMP_Therm_ADunits)) - (16.862 * (float)TMP_Therm_ADunits) + 9075.4;  

    zeroWind_ADunits = -0.0006*((float)TMP_Therm_ADunits * (float)TMP_Therm_ADunits) + 1.0727 * (float)TMP_Therm_ADunits + 47.172;  //  13.0C  553  482.39

    zeroWind_volts = (zeroWind_ADunits * 0.0048828125) - zeroWindAdjustment;  

    // This from a regression from data in the form of 
    // Vraw = V0 + b * WindSpeed ^ c
    // V0 is zero wind at a particular temperature
    // The constants b and c were determined by some Excel wrangling with the solver.
    
   WindSpeed_MPH =  pow(((RV_Wind_Volts - zeroWind_volts) /.2300) , 2.7265);   
   
//    Serial.print("  TMP volts ");
//    Serial.print(TMP_Therm_ADunits * 0.0048828125);
//    
//    Serial.print(" RV volts ");
//    Serial.print((float)RV_Wind_Volts);
//
//    Serial.print("\t  TempC*100 ");
//    Serial.print(TempCtimes100 );
//
//    Serial.print("   ZeroWind volts ");
//    Serial.print(zeroWind_volts);

    Serial.print("   WindSpeed MPH ");
    Serial.println((float)WindSpeed_MPH);

    lastMillis = millis();
  }

/*************************************************
* Wind sensor LED feedback
*************************************************/

  writeValue_red = (255./10.)*WindSpeed_MPH; //Calculate the value to write on the red LED (add point to change to float point)
  writeValue_green = (255./10.)*WindSpeed_MPH; //Calculate the value to write on the green LED
  writeValue_blue = (255./10.)*WindSpeed_MPH; ///Calculate the value to write on the blue LED
  
  analogWrite(redPin,writeValue_red); //write value to set the brightness of the red LED
  analogWrite(greenPin,writeValue_green); //write value to set the brightness of the green LED
  analogWrite(bluePin,writeValue_blue); //write value to set the brightness of the blue LED



/*************************************************
* State + Sound
*************************************************/

//if (buttonState == true){
//  

    if(WindSpeed_MPH <= 2){
    // turn red LED on:
    digitalWrite(4, HIGH);
    // turn the rest off:
    digitalWrite(2, LOW);
    digitalWrite(3, LOW);

    // play SOS melody:
     for (int thisNote = 0; thisNote < 11; thisNote++) {
      int noteDuration = 1000/SOSDurations[thisNote];
      tone(8, SOS[thisNote],noteDuration);
      //pause for the note's duration plus 30 ms:
      delay(noteDuration +30);
      noTone(8);
    }
  }

    else if (WindSpeed_MPH >7) {
      //turn green LED on:
      digitalWrite(2, HIGH);
      // turn the rest off:
      digitalWrite(4, LOW);
      digitalWrite(3, LOW);

      //play major melody:
      for (int thisNote = 0; thisNote < 9; thisNote++) {
      int noteDuration = 1000/majorNoteDurations[thisNote];
      tone(8, majorMelody[thisNote],noteDuration);
      //pause for the note's duration plus 30 ms:
      delay(noteDuration +30);
      noTone(8);
    }
   }

    else {
      //turn yellow LED on:
      digitalWrite(3, HIGH);
      // turn the rest off:
      digitalWrite(2, LOW);
      digitalWrite(4, LOW);

      //play minor melody:
      for (int thisNote = 0; thisNote < 9; thisNote++) {
      int noteDuration = 1000/minorNoteDurations[thisNote];
      tone(8, minorMelody[thisNote],noteDuration);
      //pause for the note's duration plus 30 ms:
      delay(noteDuration +30);
      noTone(8);
      
    }
    }
//    }
//    else {
//      noTone(8);
//      //turn all LEDs off:
//      digitalWrite(3, LOW);
//      digitalWrite(2, LOW);
//      digitalWrite(4, LOW);
//    }
/*************************************************
* Wind sensor LED feedback on button press
*************************************************/
//buttonState = digitalRead(buttonPin);
//
//if (buttonState == HIGH){
//
//  if (flag == 0){
//    //light up white LED
//    analogWrite(redPin,writeValue_red); //write value to set the brightness of the red LED
//    analogWrite(greenPin,writeValue_green); //write value to set the brightness of the green LED
//    analogWrite(bluePin,writeValue_blue); //write value to set the brightness of the blue LED
//  flag = 1;
//  }
//
//  if (flag == 1){
//    //turn off white LED
//    analogWrite(redPin,0); //write value to set the brightness of the red LED
//    analogWrite(greenPin,0); //write value to set the brightness of the green LED
//    analogWrite(bluePin,0); //write value to set the brightness of the blue LED
//    flag = 0;
//  }
//}
//
//if (buttonState == HIGH){
//    //light up white LED
//    analogWrite(redPin,writeValue_red); //write value to set the brightness of the red LED
//    analogWrite(greenPin,writeValue_green); //write value to set the brightness of the green LED
//    analogWrite(bluePin,writeValue_blue); //write value to set the brightness of the blue LED
//}
//
//else if (buttonState == LOW){
//    //turn off white LED
//    analogWrite(redPin,0); //write value to set the brightness of the red LED
//    analogWrite(greenPin,0); //write value to set the brightness of the green LED
//    analogWrite(bluePin,0); //write value to set the brightness of the blue LED
//}

}

Reminder: include pitches.h for code to work

 

Should I stay or should I go?

Problem:

There are many factors that have to be considered in managing risk of contagion during the pandemic. These are not straightforward, and can vary wildly depending on the context and combination of variables. Constantly trying to analyze these can cause fatigue, anxiety and unnecessary worry about perceived risks.

One particularly difficult element to get a good gauge on whether it is safe to remain indoors in a particular space. While maintaining 6ft of distance is still a good rule of thumb, it doesn’t always guarantee low risk.

This can be counterbalanced by other risk factors, such as ventilation and speaking volume. Risky activity such as singing generates significantly more droplets and aerosols that increases infection risks significantly, even when keeping a distance of 6ft.

Often, in an enclosed space, even when in individuals are in close proximity, a powerful HVAC system is circulating the air can prevent significant spreading. For example, when looking at transmission patterns in airplanes, only a few individuals sitting directly next to, in front of and behind Patient Zero were infected. Others remained safe, even though they remained in the same (relatively small) enclosed area for a significant amount of time.

This further complicated by having to consider the volume of indoor space – how risky these factors are varies considerably based on that as well.

Without ventilation, aerosols remain suspended in the air, becoming increasingly concentrated as time goes by. (Source)

Solution:

Lots of these factors, such as ventilation and speaking volume, are invisible and hard to keep track to begin with. Compared to distance from the next person, it’s really difficult for the average person to estimate what’s safe and what’s not.

It is even more challenging and stressful to calibrate these against dynamic changes from people coming and leaving, and the type of activity or behavior they choose when in said space.

By developing a system that balances all these factors and aggregates an overall risk grade that is displayed in a public space, it’s much easier for incoming and outgoing individuals to gauge their risk and exposure, and take the necessary precautions immediately.

As the number of people increase, the tradeoff between ventilation and noise level changes. We monitor these different axes with a wind sensor, a sound sensor and an ultrasonic sensor.

Components:

  • 1x HC-SR04 ultrasonic sensor
  • 1x RGB Diffused Common Cathode
  • 3x LED (Red, Green, Yellow)
  • 6x Resistor 220 ohm
  • Wind Sensor Rev. C
  • 1x LCD1602 Module
  • Sound sensor (replaced by potentiometer)

Wind sensor

The wind sensor gives us an approximation of the rate of airflow, and thus some insight into the amount of ventilation that a space has.

 

Ultrasonic sensor

Using two ultrasonic sensors, we can keep count of how many people are in the room at any one time.

 

Sound sensor

The decibel level can give us an approximation of how loudly people in the room are speaking (and thus how likely they are to expel aerosols in the air). A library is ~40dB, and a cafe is ~80dB. I will be substituting this value with a potentiometer for demo purposes, with a range of 1-100 to represent dB levels.

Safety rating

Keeping track of all this and displaying this information to the individuals in a space is an overall safety rating that indicates the recommended course of action to remain safe.

 

What should you do, and when?

When the number of people in a room is small, the safety rating remains green regardless of how well ventilated and how loud people are.

But, if enough people gather, the safety rating will turn red.

In this case, you might choose not to enter the room, and keep walking. If you do need to be present, there are a few things you can do to improve the situation.

Reducing speaking levels and ventilation levels can shift the safety ratings. Depending on the extent of how much each of these fluctuate and in what direction, the overall effect could be either an increase or decrease in risk levels.

Another way to improve the safety rating is to just decrease the total number of people in a space.

If the safety rating does not change after making these adjustments, it’s a sign that you should leave in order to be safe.

Schematic:

Code:

/*************************************************
* Ultrasonic sensor + LCD screen
*************************************************/
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define trigPin 10
#define echoPin 9

int counter = 0;
int currentState1 = 0;
int previousState1 = 0;
int currentState2 = 0;
int previousState2 = 0;
int inside = 0;
int outside = 0;

/*************************************************
* Wind Sensor
*************************************************/
#define analogPinForRV    1   // blue jumper wire
#define analogPinForTMP   0   // yellow jumper wire

// to calibrate your sensor, put a glass over it, but the sensor should not be
// touching the desktop surface however.
// adjust the zeroWindAdjustment until your sensor reads about zero with the glass over it. 

const float zeroWindAdjustment =  .2; // negative numbers yield smaller wind speeds and vice versa.

int TMP_Therm_ADunits;  //temp termistor value from wind sensor
float RV_Wind_ADunits;    //RV output from wind sensor 
float RV_Wind_Volts;
unsigned long lastMillis;
int TempCtimes100;
float zeroWind_ADunits;
float zeroWind_volts;
float WindSpeed_MPH;

//LED Feedback
int redPin = A4; //Pin for the red RGB led pin
int greenPin = A3; //Pin for the green RGB led pin
int bluePin = A2; //Pin for the blue RGB led pin 

int writeValue_red; //declare variable to send to the red LED
int writeValue_green; //declare variable to send to the green LED
int writeValue_blue; //declare variable to send to the blue LED



/*************************************************
* Sound sensor
*************************************************/
#define soundSensorPin  5
int soundSensor = 0;
bool soundRisk = false;

/*************************************************
* Aggregated risk
*************************************************/
int riskLevel = 0;
int greenLED = 6;
int yellowLED = 7;
int redLED = 8;

/*************************************************
 * 
 * SETUP
 * 
*************************************************/

void setup(){
  Serial.begin (57600);

  //Ultrasonic sensor + LCD
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  lcd.begin(16,2);

  //wind sensor
  Serial.println("start");
  //   Uncomment the three lines below to reset the analog pins A2 & A3
  //   This is code from the Modern Device temp sensor (not required)
  pinMode(A2, INPUT);        // GND pin      
  pinMode(A3, INPUT);        // VCC pin
  digitalWrite(A3, LOW);     // turn off pullups

  //Risk feedback state
  int greenLED = 6;
  int yellowLED = 7;
  int redLED = 8;
  pinMode(greenLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  
}

void loop(){

/*************************************************
* Ultrasonic sensor + LCD screen
*************************************************/
//setting up LCD screen display
lcd.setCursor(0, 0);
lcd.print("IN: ");
lcd.setCursor(7, 0);
lcd.print("Wind: ");
lcd.setCursor(0, 1);
lcd.print("OUT: ");
lcd.setCursor(7, 1);
lcd.print("Total: ");
  
//sending ping + calculating distance using ultrasonic sensor
long duration;
float distance;
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration/2) / 29.1;

//going inside
if (distance<= 9){
  currentState1 = 1;
}
else {
  currentState1 = 0;
}

delay(100);

if(currentState1 != previousState1){
  if(currentState1 == 1){
    counter = counter + 1;
    inside = inside + 1;
    //update LCD screen
    lcd.setCursor(14, 1);
    lcd.print(counter);
    lcd.setCursor(4, 0);
    lcd.print(inside);
  }
  }

//returning outside
  if (distance > 14 && distance <=30){
    currentState2 = 1;
  }
  else {
    currentState2 = 0;
  }
  delay(100);
  
  if(currentState2 != previousState2){
    if(currentState2 == 1){
      counter = counter - 1;
      outside = outside + 1;
      //update LCD screen
      lcd.setCursor(14, 1);
      lcd.print(counter);
      lcd.setCursor(5, 1);
      lcd.print(outside);
    }
  }

//keeping tally based on   
    Serial.print("   IN:");
    Serial.println((float)inside);

    Serial.print("   OUT:");
    Serial.println((float)outside);

    Serial.print("   TOTAL INSIDE:");
    Serial.println((float)counter);
    
//debugging
//    Serial.println(duration);
//    Serial.println(duration/2);
//    Serial.println((duration/2) / 29.1);


/*************************************************
* Wind Sensor
*************************************************/
  if (millis() - lastMillis > 200){      // read every 200 ms - printing slows this down further
    
    TMP_Therm_ADunits = analogRead(analogPinForTMP);
    RV_Wind_ADunits = analogRead(analogPinForRV);
    RV_Wind_Volts = (RV_Wind_ADunits *  0.0048828125);

    // these are all derived from regressions from raw data as such they depend on a lot of experimental factors
    // such as accuracy of temp sensors, and voltage at the actual wind sensor, (wire losses) which were unaccouted for.
    TempCtimes100 = (0.005 *((float)TMP_Therm_ADunits * (float)TMP_Therm_ADunits)) - (16.862 * (float)TMP_Therm_ADunits) + 9075.4;  

    zeroWind_ADunits = -0.0006*((float)TMP_Therm_ADunits * (float)TMP_Therm_ADunits) + 1.0727 * (float)TMP_Therm_ADunits + 47.172;  //  13.0C  553  482.39

    zeroWind_volts = (zeroWind_ADunits * 0.0048828125) - zeroWindAdjustment;  

    // This from a regression from data in the form of 
    // Vraw = V0 + b * WindSpeed ^ c
    // V0 is zero wind at a particular temperature
    // The constants b and c were determined by some Excel wrangling with the solver.
    
   WindSpeed_MPH =  pow(((RV_Wind_Volts - zeroWind_volts) /.2300) , 2.7265);   

    
//debugging   
//    Serial.print("  TMP volts ");
//    Serial.print(TMP_Therm_ADunits * 0.0048828125);
//    
//    Serial.print(" RV volts ");
//    Serial.print((float)RV_Wind_Volts);
//
//    Serial.print("\t  TempC*100 ");
//    Serial.print(TempCtimes100 );
//
//    Serial.print("   ZeroWind volts ");
//    Serial.print(zeroWind_volts);

    Serial.print("   WindSpeed MPH ");
    Serial.println((float)WindSpeed_MPH);

    lastMillis = millis();

    lcd.setCursor(13, 0);
    lcd.print(WindSpeed_MPH);
  }

/*************************************************
* Wind sensor LED feedback
*************************************************/

  writeValue_red = (255./10.)*WindSpeed_MPH; //Calculate the value to write on the red LED (add point to change to float point)
  writeValue_green = (255./10.)*WindSpeed_MPH; //Calculate the value to write on the green LED
  writeValue_blue = (255./10.)*WindSpeed_MPH; ///Calculate the value to write on the blue LED
  
  analogWrite(redPin,writeValue_red); //write value to set the brightness of the red LED
  analogWrite(greenPin,writeValue_green); //write value to set the brightness of the green LED
  analogWrite(bluePin,writeValue_blue); //write value to set the brightness of the blue LED



/*************************************************
* Sound sensor
*************************************************/
int soundSensor = analogRead(soundSensorPin);

//assuming that there are two main buckets of sound levels - one is low, which conversational and not risky, and the other is high, such as singing, which is risky)
if (soundSensor >= 100){
  soundRisk = true;
}
  else {
    soundRisk = false;
  }

Serial.println(soundSensor);
Serial.println(soundRisk);

/*************************************************
* States + risk level display
*************************************************/

//small group
if (counter <= 3) {
  //sound and ventilation doesn't matter
  riskLevel = 0;  
}

//medium group
else if (counter > 3 && counter <=5){
  //sound risk is high, ventilation needs to be high
  if (soundRisk == true){
    if (WindSpeed_MPH >= 10){
      riskLevel = 0;
    } else if (WindSpeed_MPH < 10 && WindSpeed_MPH >= 7){
      riskLevel = 1;
    } else if (WindSpeed_MPH < 7){
      riskLevel = 2;
    }
    
  //sound risk is low, ventilation can be lower
  } else if (soundRisk == false){
      if (WindSpeed_MPH >= 7){
        riskLevel = 0;
      } else if (WindSpeed_MPH < 7 && WindSpeed_MPH >=2){
        riskLevel = 1;
      } else if (WindSpeed_MPH < 2){
        riskLevel = 2;
      }
  }
  
  //
}

//big group
else if (counter > 5){
  //sound risk is high, ventilation needs to be high
  if (soundRisk == true){
    if (WindSpeed_MPH >= 20){
      riskLevel = 0;
    }
    if (WindSpeed_MPH >= 10 && WindSpeed_MPH < 20){
      riskLevel = 1;
    } else if (WindSpeed_MPH <10){
      riskLevel = 2;
    }
  //sound risk is low, ventilation can be lower
  } else if (soundRisk == false){
    if (WindSpeed_MPH >= 12){
      riskLevel = 0;
    } else if (WindSpeed_MPH >= 8 && WindSpeed_MPH > 5){
      riskLevel = 1;
    } else if (WindSpeed_MPH <= 5){
      riskLevel = 2;
    }
  }
}

/*************************************************
* Overall risk level display
*************************************************/

if (riskLevel == 0){
  //Low risk
  //turn green LED on
  digitalWrite(greenLED, HIGH);
  //turn the rest off
  digitalWrite(redLED, LOW);
  digitalWrite(yellowLED, LOW);
}

else if (riskLevel == 1){
  //Medium risk
  //turn yellow LED on
  digitalWrite(yellowLED, HIGH);
  //turn the rest off
  digitalWrite(redLED, LOW);
  digitalWrite(greenLED, LOW);
}

else if (riskLevel == 2){
  //High risk
  //turn red LED on
  digitalWrite(redLED, HIGH);
  //turn the rest off
  digitalWrite(yellowLED, LOW);
  digitalWrite(greenLED, LOW);
}

}

Reflections on implementation

There are a few things which I tried and did not turn out so well for the prototype. For starters, I had initially attempted to operationalize the graph diagram I had drawn earlier indicating the relationship between sound volumes and ventilations. Not only were the resultant numbers difficult to predict and thus challenging to show in a demo, sound generally clusters around two median values – loud, or not too loud. Instead of a continuous range, I went ahead and made the sound value a boolean instead. Actual implementation of this would also require a much more nuanced look at the relationship between these variables as well as the risk tradeoffs along a continuum, unlike the buckets that I had created to assist accuracy in the demo.

There were also some unpredictability in the ultrasonic sensor’s readings and counting of people – on occasion, it would double or even triple count, while on others it missed it totally. This challenged the reproducibility of the demo. If this were to be implemented in real life, much more robust software would be needed in order to provide an accurate estimate of the number of people in the room.

I’m glad that in my second iteration of the circuits and code I was able to fit everything onto a single breadboard, as it made it much easier to see what was happening or changing altogether at once.

Growing this project further

If I had had more time, I would have liked to delve more into strategies in responsive architecture to help individuals respond more appropriately to the risk level. For example, if this was a public area such as an indoor mall, perhaps  public seating can transform to become more hostile to discourage loitering, or eliminate it by disappearing altogether.

One can imagine the benches shape-shifting into something uncomfortable. The example below illustrates this in action:

Or, they could disappear altogether into the ground until it is safe again, like the night time urinals in London.

Other ways we could encourage individuals to cut their time in a space short is to switch off alternate lights, and/or cut off background music to give people the message to conclude their affairs quickly and leave as it feels like things are closing.

If it is able to be actuated, the building could also enforce a base-level of ventilation by automatically switching on the fans and opening the windows when the safety indicator goes yellow or red. Throwing open the windows when necessary would also also inevitably expose individuals to the elements of nature, encouraging them to disperse and head home, particularly in wintertime.

 

Invisibility in covid

I’d like to expand on my sound crit idea to incorporate the invisible factors around covid prevention. Three sensors I’d use – wind sensor, ultrasonic sensor, volume sensor. Together, they can provide the count on the number of people in a room, ventilation speed and how loud people are talking. This would feed information into a p5.js sketch that shows a visualization of what’s in a green, orange or red zone. This can also be accompanied by a sound when the red zone is reached, and potentially windows can be set to open automatically.
Alternatively, I could also work on a shopping mall entrance checker with a temperature sensor and a ml5.js sketch that recognizes whether the person in front of it is wearing a mask. If they’re not wearing a mask, the door doesn’t open to allow them to enter (probably use a servo to simulate this), even if their temperature is within range.

Mini assignment 9

  • Elevators in my tall apartment building (22 floors) can take a long time to arrive because we’re limited to 1 household per elevator trip. If there are multiple people waiting for elevators during ‘rush hour’ (and yes there is still rush hour during covid times, many people walk their dogs at the same time) it’d be good if a chirping sound was made before I’m about to leave the house.
  • The recycling area in my apartment building is usually overflowing; I wish that a sound would be made if it’s empty so I can immediately bring my recyclables over.
  • It can be hard to stay on top of perishables in the fridge, both stocking what I need (e.g. eggs, bananas, milk etc.) and making sure that I consume food in an orderly manner (First in, First out) so if the fridge could make a sound to remind me if I need to replenish something or if I need to consume something soon.
  • My white whale in cooking is making toast in my oven. Unlike boiling / frying, there is little sensory feedback (smell, sound, sight, etc.) on how hot the burner is and I often end up burning my bread. I wish that the oven would tell me once it was done.
  • I have a humidifier and air purifier – I wish they would let me know when their filters needed changing.
  • When my keys are not in their appropriate storage place, I wish that they would make a sound to remind me to place them back.
  • If I wake up and we might have a few hours of sunshine that day, I wish my home would encourage me to go outside by playing a spritely tune when I get out of bed in the morning.
  • If I haven’t meditated that day, I wish my meditation chair could make a sound to call this to my attention when my roommate isn’t on work calls.

Assignment 8: Who’s there?

Problem:
I go to a circuit training gym, and since it reopened after covid, we’ve had to stand in line to take our temperature before we’re allowed to be admitted into class. This designated spot is obfuscated by a bunch of equipment, and it’s not always easy for the trainer on duty to be alerted whenever someone arrives. They are also likely to be re-setting the equipment for the next class, and unable to rely on visual cues.

Solution:
Whenever someone arrives at the door, the ultrasonic sensor initiates the speaker to start playing a tune to alert the trainer that someone is here. Once they take their temperature is taken and they leave that spot, the tune automatically stops because the distance sensed in the ultrasonic sensor returns back to baseline. This is less frustrating and annoying than a doorbell or buzzer, which can startle other people in the room and disrupt their pre-workout stretch.

https://vimeo.com/user80133951/review/472812721/a046f1c7f5

Sometimes, the trainer uses a vacuum cleaner to clean the gym between classes, and can’t hear soft ambient noises. In this case, the person waiting can use a button to sound a buzzer and stop the tune playing to attract the attention of the trainer more effectively.

//Setting up ultrasonic speaker pins
const int trigPin = 12;
const int echoPin = 13;

//Defines variables
long duration;
int distance;

//Setting up speaker
#include "pitches.h"

//Setting up melody:
int melody[] = {
  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  4, 8, 8, 4, 4, 4, 4, 4, 4
};

//buzzer interrupt
static const int togglePin = 2;
bool buzzerState = false;
const bool isInterrupt = true;

void SwitchPressed()
{
  //play buzzer
  }

void setup() {
//To read ultrasonic speaker
pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
pinMode(echoPin, INPUT); // Sets the echoPin as an Input
Serial.begin(9600); // Starts the serial communication
pinMode(togglePin, INPUT); // Button for buzzer

}

void loop() {

//Reading from ultrasonic speaker
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);

// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);

// calculate distance
distance= duration*0.034/2;

// print distance
Serial.print("Distance: ");
Serial.println(distance);

if(distance<100){
//  digitalWrite(11,HIGH);

  // iterate over the notes of the melody:

  for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second divided by the note type.

    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

    int noteDuration = 1000 / noteDurations[thisNote];

    tone(8, melody[thisNote], noteDuration);

    // to distinguish the notes, set a minimum time between them.

    // the note's duration + 30% seems to work well:

    int pauseBetweenNotes = noteDuration * 1.30;

    delay(pauseBetweenNotes);}

}
    else{
//  digitalWrite(11,LOW);
    // stop the tone playing:
    noTone(8);
}
}

 

Assignment 8

For me, sounds usually work in both culturally specific and universal ways, sometimes separately, sometimes simultaneously.

Something innocuous could feel ominous, for example, because it connotes an entirely different undertone in different circumstances. I grew up in a crowded city, so bustling areas meant high human traffic and more generally, areas which are safe. In contrast, sketchy streets usually have low foot traffic and are very quiet. When I moved to the SF, lots of my friends complained about the traffic noise and city sounds and preferred living in the quiet suburbs. My experience was completely the opposite – I found the suburban areas eerily quiet, and I struggled to feel relaxed in an environment so deathly quiet it felt like a horror movie. I felt way more at ease and fell asleep much more easily being surrounded by ambient city noises.

Sounds can also evoke emotions and feelings from remembrance of a place, or event. Food preparation is a huge undertaking in my family in preparation of the Lunar New Year, so the sound of deep-frying, chopping, mixing, crinkling of plastic and paper wrappers, etc. during that time of year brings up festive feelings of celebration. We used to get incredibly excited at the sound of deep-frying as kids because that was a signal that seafood and vegetable fritters were being made. I had a friend who was overseas so homesick on year that she asked her mum to bring the phone to the kitchen just so she could hear food in the wok frying. For someone who works as a fry cook, however, this sound might not be as energizing.

Even without intending to, mechanical sounds (such as from cars) often convey emotions. It’s possible to honk in a sequence to sound extra-angry, but near impossible to do so to honk in a way that conveys sadness, fear, joy etc. Arguably, when people get excited and honk when sports teams win, they might do so joyously, but the quality of the honk still sounds super angry to me. Lots of other machinery operating also carry an aggressive, hostile undertone. I often find myself feeling belligerent and irritable when I hear a leaf blower nearby. If taken out of context, I would struggle to identify / guess what the Star Trek sounds meant. Combined with the context of a scene, however, that would be much easier to parse. I usually am able to use these cues to infer what new sound effects are supposed to mean.

Sound also operates in universal ways – the fact that we can understand Pokémon perfectly when all they do is repeat their own species shows that tone, volume, pitch, etc. are great at conveying emotion. Music also achieves this on a universal level through different styles of play the same notes – staccato conveys tension and/or energy, while legato is much more soothing. Sleep machines also leverage the universally relaxing sounds in nature (the ocean, rainforest, thunderstorms, etc.) to help people relax and enter restful sleep. These universal sounds can still be experienced differently by individuals though. Rain is a special one for me – I grew up in a place with lots of thunderstorms, and when I first moved to California I was so unaccustomed to the lack of rain that I felt actual physical discomfort, as if I was holding my breath and couldn’t really exhale. Though many people would feel soothed if they hear rain, I doubt that their experience would be so adverse if they didn’t hear it regularly. Similarly, some people find silence deafening and find it much easier to focus against the backdrop of pleasant chatter in a café setting, while others abhor any sort of background noise when trying to concentrate.

Sound in Physical Interactions

  1. VR Art installation by Ray McClure

2. Microsoft Soundscape – Map delivered in 3D sound

Link for more information

3. Some interesting links:

Deep learning monitors human activity based on sound alone, able to parse whether a person in a room is sitting, standing, walking or falling.

Apple’s approach to harmonize sound with physical interactions by thinking of them as audio-haptic experiences

Attention during online meetings

Problem:
As we transition into a working from home setup during covid, we lose alot of the immediate feedback through body language and facial cues from people we are communicating with. In particular, this is pronounced in a presenting information to a large group of people, where a successful presentation relies on tailoring the speed, complexity and cadence to engage the audience sufficiently to keep them interested, but not go too fast that you leave them behind.

When presenting a deck through online meeting platforms, presenters are often unable to monitor the real-time vibe of the audience since participants are not always visible due to screen real-estate (assuming that they have their cameras on). Even with the hand-raise feature, it’s hard for presenters to respond to questions in a timely manner.

Solution:
I identified two main modes of feedback that are most important to a presenter:

    1. Are there any questions?
    2. Are people paying attention?

Raising your hand
To indicate if the audience has any questions, I used a servo as a physical indicator of the presence of questions. Typically, when participants raise their hands during a meeting virtually, this is missed. By mapping this state to the servo, presenters now have a better understanding of when exactly questions are being raised.

Paying attention
One of the indicators of restlessness in your audience is fidgeting. If people are bored and unable to pay attention, they are likely to be crossing their arms, rearranging their seating posture, etc. We can monitor this by observing the rate of change in their posture. Using the poseNet in p5.js, we can abstract this information by drawing keypoints of your posture in the webcam and do a comparison.

I wasn’t quite able to get the serial output from p5.js to work, so I replaced it with a potentiometer as an abstraction for the demo 🙁

Kinetic feedback
By translating this to a tapping pattern if the changes exceed a particular threshold, presenters are able to understand when their audience get restless.

It is hard to discern the nuances if the tapping pattern were to change incrementally, so I set up 3 states with a threshold rate of change that matches 3 different tapping states.

1 No need to worry
Some movement is to be expected in the audience, so presenters don’t need to be concerned about a non-zero level of movement.

2 Antsy
When the audience starts getting restless, the tapping starts with a lower frequency. The presenter can quickly re-engage the audience by speeding up and moving on. As the audience returns back to normal and pays attention, the tapping should cease.

3 I can’t stand this anymore
If the audience crosses the second threshold indicating restlessness, the frequency of taps doubles and conveys to the presenter that they should try to wrap up the presentation ASAP since they’ve likely already lost their audience.

Schematic

Will you shut up, man?

Problem: Aggressive, domineering speakers often create a hostile environment (both consciously and unconsciously) by interrupting others during discussions, meetings, and most recently, during the presidential debate.

Lots of such speakers are unaware that they are constantly speaking over others, and do not actually respond to intervention by others in-situ when they are exhibiting this behavior. They also distrust other’s claim that they are frequently being disruptive to others because of confirmation bias.

Solution: A tactile, haptic feedback in your pocket based on how long you have interrupted someone. Similar to someone kicking you under the table when you’re saying something you shouldn’t, it delivers a metaphorical “kick under the table” to provide a speaker awareness of their interruption.

When you first start speaking when someone else is speaking, you receive a simple tap as a reminder. As more time elapses, the taps increase in frequency (and optionally magnitude. The mini solenoid in our kit doesn’t allow for a change in force). This should impress upon the speaker the severity of their interruption (i.e. the longer it is, the more rude and disruptive it is and unlikely to be constructive). If the interruption time elapsed exceeds 5 mins, it should just switch off someone’s mic.

Schematic

Application: Outside of the presidential debate, this kinetic feedback is a discrete way to help speakers monitor their own speaking (and interrupting) behavior to create a more inclusive environment.

As we transitioned into entirely online meetings due to covid WFH, people have become more conscious about turn-taking when speaking since visual / auditory cues of when someone might want to interject are now invisible.