As computing becomes more ubiquitous in our objects, designers need to be more aware of how to design meaningful interactions into electronically enhanced objects. At the University of Washington, a class of junior Interaction Design majors is exploring this question. These pages chronicle their efforts.

Tuesday, June 11, 2013

Friday Night Tights: All Wound Up — Mallika Simone, Sarah Churng



Boards:





Visual compendium:


Friday Night Tights from sarah churng on Vimeo.

The Anatomy:

The device: 

Friday Night Tights is wearable technology that addresses the dangers of drunk walking. Many Americans have a false sense of security that, by avoiding driving under the influence, they are safe to walk while intoxicated. Yet fatalities are in fact high among drunk pedestrians who walk into traffic.

The system houses an Arduino device inside a fanny pack attached to the tights' hip, and runs wires down to pressure sensors which slip into the wearer's shoes.

When the system detects walking patterns that indicate severe levels of intoxication, it emits a subtle LED light up in the pouch, and syncs wirelessly with an Android phone, to send a text message to a predesignated friend or cab company of the wearer's whereabouts.


The algorithm: 

We define an inebriate's type of imbalance as marked by quick, light steps, followed rapidly by long, heavy compensating steps. 2 warnings are allowed before, on the 3rd round, the response is triggered.

When the Arduino has access to video output, it can provide serial monitoring results like:


the device's serial output 




The Code:

// Our algorithm detects drunk walking by tracking sudden shifts of imbalance.
// We define an inebriate's type of imbalance as marked by quick, light steps, 
// followed rapidly by long, heavy compensating steps. 2 warnings are allowed 
// before, on the 3rd round, the response is triggered.

// LIBRARIES
#include <QueueArray.h>                                 // FIFO stack 

// ANALOG DATA: Left Foot
const int analogInPin1 = A0;                            // Force Pressure Sensor at A0
//const int analogOutPin1 = 9;                          // LED at variable pin 10
int sensorValue1 = 0;                                   // value from variable sensor
int LeftValue = 0;                                      // value from output to analog out

// ANALOG DATA: Right Foot
const int analogInPin2 = A1;                            // Force Pressure Sensor at A1
//const int analogOutPin2 = 10;                         // LED at variable pin 9
int sensorValue2 = 0;                                   // value from variable sensor
int RightValue = 0;                                     // value from output to analog out


// QUEUE: a custom First-In-First-Out array
class QueueNode
{
  public:
    QueueNode(int val, QueueNode* next)
    {
     _val = val;
     _next = next;
    } 
    
    int _val;
    QueueNode* _next;
};

class Queue
{
    public:
      Queue()
      {
        head = NULL; 
      }
      
      void Append(int val)                         // ~ QueueArray.push()
      {
        QueueNode* qn = new QueueNode(val, head);
        head = qn;
      };
      
      void Clear()                                 // ~ QueueArray.clear()
      {
        QueueNode* cur = head;
        head = NULL;
        while (cur != NULL)
        {
         QueueNode* next = cur->_next;
         delete cur;
         cur = next;
        }
      }
      
      QueueNode* Pull()                            // ~ QueueArray.pop()
      {
        QueueNode* cur = head;
        head = NULL;  
        QueueNode* next = cur->_next;
        cur = next;
        delete cur;
        return cur;
      }
      
      float Average()                              // return a numerical average 
      {
        int cnt = 0;
        int sum = 0;
        QueueNode* cur = head;
        while (cur != NULL)
        {
          cnt++;
          sum += cur->_val;
          cur = cur->_next;
        }
        
        if (cnt == 0)
          return 0.0;
        else
          return (float)sum/(float)cnt;
      }
      
      int Count()                                 // return the total items in Queue
      {
        int cnt = 0;
        QueueNode* cur = head;
        while (cur != NULL)
        {
          cnt++;
          cur = cur->_next;
        }
        return (int)cnt;
      }

    private:
      QueueNode* head;
};



// GLOBALS
Queue PressureQ = Queue();                 // Queue of current step's pressure values 
Queue DrunkTrackerQ = Queue();             // Instances of drunk-like behavior before drunk alarm goes off.

float currPressureAvg = 0.00;              // float of current step's average pressure
float prevPressureAvg = 0.00;              // float of previous step's average pressure

int currPace = 0;                          // int of current step's pace length
int prevPace = 0;                          // int of previous step's pace length

int loopNum = 0;
int drunkSteps = 3;
int warnBlinks = 0;

//LED OUTS:
int ledPin1 = 9;
int ledPin2 = 10;



// DETECT: This function collects the pressure and timing of each step. One step is the combination of pressure that is greater than 0 followed by 0 pressure. Pacing is measured as the duration of each step.
void DetectStep(int outputValue)
{
  if ( outputValue != 0 )                              // Record pressure values during a foot step
  {
    PressureQ.Append(outputValue);
    Serial.print("\tpressure: \t");
    Serial.println(outputValue);
  }
  else                                                  // At the end of the footstep, calculate the step's average pressure
  {
    currPace = PressureQ.Count();                       // And record the footstep's length
    currPressureAvg = PressureQ.Average();          
    
    PressureQ.Clear();
    
    Serial.print("\tpressure average: \t");
    Serial.println(currPressureAvg);
    Serial.print("\tpace: \t");
    Serial.println(currPace);
  }
}

// FLAG: Function flags instances of a very short and light step being followed immediately by a long and heavy step
void Flag( float currPressureAvg, int currPace )
{
  if ( (prevPace >= (currPace/2)) && ( prevPace != 0 ) )
  {
    if ( currPressureAvg >= (prevPressureAvg/2) )
    {
      drunkSteps--;                                      // Give a set number of warnings
      warnBlinks++;
    
      Serial.print("you kinda drunk...but we gonna give you ");
      Serial.print(drunkSteps);
      Serial.println(" more chances.");
      
      // Blink with # blinks = # warnings
      for (int i=0; i++; i<warnBlinks){
        digitalWrite(ledPin1, HIGH);                     // Turn the LED on (HIGH is the voltage level)
        delay(750);                                      // Wait
        digitalWrite(ledPin1, LOW);                      // Turn the LED off by making the voltage LOW
        delay(750);                                      // Wait
      }
    }
  }
}

// RESPOND: This function calls DETECT and FLAG and triggers a response if no warnings are left.
void respond(int outputValue)
{
  pinMode(ledPin1, OUTPUT);                             // LED pins to output
  pinMode(ledPin2, OUTPUT);

  DetectStep(outputValue);                              // Detect steps 
  Flag(currPressureAvg, currPace);                      // Flag for drunkenness
  prevPressureAvg = currPressureAvg;                    // Step forward on pressure 
  prevPace = currPace;                                  // Step forward on pace
    
  // RESPONSE NECESSARY
  if (drunkSteps == 0)
  {
    // Just blink like cray
    Serial.println("YEAH YOU SHITFACED...LEDs and whatever");
    digitalWrite(ledPin1, HIGH);                        // Turn the LED on (HIGH is the voltage level)
    delay(500);                                         // Wait for half a second
    digitalWrite(ledPin1, LOW);                         // Turn the LED off by making the voltage LOW
    delay(500);                                         // Wait for half a second
  }
}


// SETUP WITH SERIAL MONITORING
void setup(){ 
  Serial.begin(9600);                                   
}
  

// MAIN LOOP: 
void loop()
{
  QueueArray <int> LeftRight;
  Serial.print("loop: \t");
  Serial.println(loopNum);

  loopNum++;
  
  // READ: analog values
  sensorValue1 = analogRead(analogInPin1);              // Read in input from left foot
  LeftValue = map(sensorValue1, 0, 256, 0, 50);         // Map the lower-bound 0-256 to the range 0-50
  //analogWrite(analogOutPin1, LeftValue);              // Change the analog out value

  sensorValue2 = analogRead(analogInPin2);              // Read in input from right foot
  RightValue = map(sensorValue2, 0, 256, 0, 50);        // Map the lower-bound 0-256 to the range 0-50
  //analogWrite(analogOutPin2, RightValue);             // Change the analog out value
  
  LeftRight.push(LeftValue);
  LeftRight.push(RightValue);

  //while ( !LeftRight.isEmpty() )
  //{
    int outputValue = LeftRight.pop();
    respond(LeftRight.pop());
  //}

  delay (1000);                                         // Half second cycles
}


References:

Traffic Safety Facts 2009 Data. U.S. Department of Transportation, National Highway Traffic Safety Administration

The Perils of Drunk Walking: A New Marketplace Podcast

Freakonomics: What Went Right? Responding to Wrong-Headed Attacks

Flexiforce Sensors Screening Athlete's Feet at the Special Olympics. These are the force pressure sensors we researched and used.


Android Bluetooth to Arduino. For the text messaging response.

Investigator: Breathalyzer found to be inaccurate, unreliable. An investigative article on the inaccuracies of breathalyzers, which contributed to our decision to stay away from using alcohol breath detection.


Flexiforce Quickstart Guide. For working with flexiforce and Arduino

QueueArray. library for FIFO queue.

C++

Artefact Wearable Tech Movement. The wearable tech movement look and appeal that inspired our attention to chic over kitsch.

Look Around You. The BBC parody that inspired the direction of our video. 

No comments:

Post a Comment