Stressed Out? – Final Project

Link to sketch: https://editor.p5js.org/Hazpaz/full/1zy63dQBC

Concept

The finals week is here and stress is something that comes along with it. To soothe everyone’s nerves, I have come up with this project through which anyone can unleash their stress. Simple yet powerful. Inspired by one of the previous works related to stress done by previous students, I’ve decided to take advantage of the stress and anger of the students to create beautiful patterns. The idea is to use a stress ball to measure stress levels. When you squeeze the ball, it tells you how much stress you’re letting out. The project then shows a spiral pattern in action according to the pressure applied.

Images

User testing Video

How does the implementation work

Materials Used

  1. Arduino Uno
  2. Breadboard
  3. flex sensor
  4. jumper wires
  5. 330 ohms resistors
  6. Cardboards
Description of interaction design

When you squeeze the the stress ball, the spiral pattern in p5 starts animating and when the pressure applied on the stress ball increases, the Arduino reads it using the flex sensor and the speed of the spiral animation increases and the color changes to a dark red from a soft color, thus making it feel intense.

Arduino code
const int flexPin = A0;  // Analog pin for the flex sensor

int flexValue = 0; // Variable to store flex sensor value
int pressure = 0;  // Variable to store pressure level (mapped from flex sensor value)
int pressureMin = 10; // Minimum pressure value
int pressureMax = 1023; // Maximum pressure value

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

void loop() {
  // Read flex sensor value
  flexValue = analogRead(flexPin);
  
  // Map flex sensor value to pressure scale (0-100)
  pressure = map(flexValue, pressureMin, pressureMax, 0, 100);
  
  // Send pressure value to p5.js
  Serial.println(pressure);

  
  delay(100); // Adjust delay as needed
}

The A0 pin is used as the analog pin for the flex sensor.

// Map flex sensor value to pressure scale (0-100) 
pressure = map(flexValue, pressureMin, pressureMax, 0, 100);

This code maps the flex sensor reading to a range between 0-100.

// Send pressure value to p5.js 
Serial.println(pressure);

the above code is the communication from Arduino to p5.

p5.js code
function draw() {
  
  if (!patternsPage) {
    
    // Display starting page
    image(startingPage, 0, 0, windowWidth, windowHeight);
    
  } else {
    
    // Display patterns page
    image(bg, 0, 0, windowWidth, windowHeight); // Background image

    // Update and draw circles
    for (let i = 0; i < cols; i++) {
      
      for (let j = 0; j < rows; j++) {
        
        circles[i][j].display();
        circles[i][j].move(circleSpeed * pressure); // Update speed based on pressure
        
      }
    }

    
    // Draw pressure scales
    pressureMeterLeft.draw(pressure);
    pressureMeterRight.draw(pressure);

    
    // Draw texts
    fill('#704402'); // Set text color
    textAlign(CENTER, CENTER); // Align text to center
    text("Pressure: " + pressure, width / 2, 30); // Display pressure value
    

    // Display connection status
    if (!serialActive) { // If serial connection is not active
      fill('red'); // Set text color to red
      text("Press Space Bar to select Serial Port", windowWidth / 2, windowHeight - 20); // Display message to select serial port
    } 
    else {
      
      // If serial connection is active
      fill(0, 255, 0); // Set text color to green
      text("Connected", windowWidth / 2, windowHeight - 20); // Display connected message
      
    }
  }
}

This function controls the visualization and text elements. If on the starting page, it displays the `startingPage` image, and if on the patterns page, it shows the `bg` image. It updates and draws stress-relief circles, adjusting their movement speed according to the pressure level. Pressure scales are drawn on the left and right sides using `pressureMeterLeft` and `pressureMeterRight`, representing the pressure level visually. The pressure value is displayed at the top center, and a message about the status of the serial port connection is shown at the bottom center. If the serial connection is inactive, it prompts the user to press the space bar to select the serial port; otherwise, it indicates that the connection is established.

// Circle class for moving circles
class Circle {
  
  constructor(cx, cy, angle) {
    
    this.angle = angle;
    this.cx = cx;
    this.cy = cy;
    this.baseColor = color('#F0D6B0'); // Green color for low pressure
    this.highPressureColor = color('rgb(179,1,1)'); // Red color for high pressure/
    
  }

  display() {
    
    push();
    translate(this.cx, this.cy);
    noFill();
    let c = map(abs(this.angle % TWO_PI), 0, TWO_PI, 0, 255);
    c -= map(pressure, 0, maxPressure, 0, 100); // Darken color based on pressure
    c = constrain(c, 0, 255);
    let currentColor = lerpColor(this.baseColor, this.highPressureColor, pressure / maxPressure); // Interpolate between green and red based on pressure
    
    noStroke();
    fill(currentColor);
    let x = r * cos(this.angle);
    let y = r * sin(this.angle);
    arc(x, y, size, size, this.angle, this.angle + PI / 2);
    pop();
    
  }

  move(speed) {
    
    this.angle -= speed;
    
  }
}

The `Circle` class defines the behavior and appearance of the patterns. Each circle is constructed with parameters for its center position (`cx` and `cy`) and its starting angle (`angle`). It has properties for colors representing low and high pressure, where green indicates low pressure and red indicates high pressure. The `display()` method draws the circle, adjusting its color based on the current pressure level. The circle’s position is translated to its center, and its color is determined by interpolating between the base color and high pressure color. The `move()` method updates the angle of the circle, causing it to rotate at a speed determined by the pressure level.

// PressureMeter class for the pressure scale
class PressureMeter {
  
  constructor(x, y, width, height) {
    
    this.x = x; // X position
    this.y = y; // Y position
    this.width = width; // Width
    this.height = height; // Height
    
  }

  draw(pressure) {
    
    // Draw pressure scale box
    noFill();
    stroke(10);
    rect(this.x, this.y, this.width, this.height); // Draw the rectangle outline\
    

    // Fill the pressure scale rectangle based on pressure
    let fillAmount = map(pressure, 0, maxPressure, 0, this.height); // Map pressure to the height of the rectangle
    
    fill('#985B00'); // Set fill color to brown
    noStroke();
    rect(this.x, this.y + this.height - fillAmount, this.width, fillAmount); // Draw the filled rectangle
    
  }
}

The `PressureMeter` class contains the visual representation of the pressure scale. It is initialized with parameters for its position (`x` and `y`), as well as its width and height. The `draw()` method is responsible for rendering the pressure scale on the canvas. It first draws the outline of the pressure scale box using the provided position, width, and height. Then, it calculates the fill amount based on the current pressure level, mapping it to the height of the rectangle. The fill color is set to brown, and a filled rectangle is drawn inside the outline, representing the current pressure level.

Embedded sketch

Description of communication between Arduino and p5.js

A stress ball is connected to a flex sensor, which is connected to A0 pin in Arduino Uno. The Arduino is connected to p5 using serial connection. When the stress ball is squeezed, the Arduino reads it with the help of the flex sensor and then maps the sensor reading between 0-100. The Arduino sends this mapped sensor readings to p5 through serial connection and the p5 uses this sensor readings to adjust the speed and color of the animation of the spiral pattern.

// Send pressure value to p5.js 
Serial.println(pressure);

the above code is the communication from Arduino to p5.

Aspects of the project I’m proud of

I’m especially proud of the pressure meter scale and the responsive pattern animation in the project.

// Initializing the two pressure scales
let scaleRectXLeft = 100; // X position of the pressure scale rectangle for the left side
let scaleRectXRight = windowWidth - 100 - rectWidth; // X position of the pressure scale rectangle for the right side

pressureMeterLeft = new PressureMeter(scaleRectXLeft, scaleRectY, rectWidth, rectHeight);
pressureMeterRight = new PressureMeter(scaleRectXRight, scaleRectY, rectWidth, rectHeight);

This block of code shows the initialization two pressure scales, one for the left side and one for the right side of the canvas. I like this because it allows for a visually balanced presentation of the pressure levels on both sides of the screen, creating a sense of symmetry and organization. By positioning the scales at specific x-coordinates, we ensure they are consistently placed regardless of the canvas size. This contributes to the overall aesthetic appeal and user experience of the project.

// Set initial positions
 scaleRectY = height / 2 - rectHeight / 2; // Y position of the pressure scale rectangle
 cols = floor(width / (size * 1.5)); // Number of columns
 rows = floor(height / (size * 1.5)); // Number of rows

 
 // Initialize circles array
 circles = [];
 
 // Loop for the columns
 for (let i = 0; i < cols; i++) {
   
   // Initialize an array for each column
   circles[i] = [];
   
   // Loop for the rows
   for (let j = 0; j < rows; j++) {
     
     // Calculate the x and y positions for the circle
     let x = size / 2 + i * (size * 1.5);
     let y = size / 2 + j * (size * 1.5);
    
     // calculate the distance from the center of the canvas
     let d = dist(x, y, width / 2, height / 2);
     
     // Calculate the angle based on the distance and constant k
     let angle = d / k;
     
     //storing in the array
     circles[i][j] = new Circle(x, y, angle);

This block of code sets the initial positions for the pressure scale and initializes the circles for the pattern animation. I like it because it ensures that the pattern is evenly distributed across the canvas, regardless of its size. By calculating the number of columns and rows based on the canvas dimensions and the size of the circles, it dynamically adjusts to fit the available space. This allows for a consistent and visually pleasing pattern layout, enhancing the overall aesthetic appeal of the project.

Schematics

resources used

Challenges faced and overcoming them

I’ve faced a number of challenges with this project.

  • Initially, there were challenges with implementing a circle pattern. The code for the circle pattern didn’t function correctly and didn’t respond to the sensor readings as intended.
  • To address this issue, I decided to draw inspiration from a spiral pattern which I found online, which proved to be more responsive to the sensor readings.
  • Another challenge was adding the option for users to choose from a variety of patterns. Despite attempts, I encountered difficulties in running multiple patterns simultaneously.

future improvements

  • Offer a variety of stress-relief patterns for users to choose from, catering to different preferences and moods.
  • Enhance the experience by adding soothing music or relaxing sounds to accompany the patterns. For example, if users select a pattern resembling waves or clouds, they can listen to calming nature sounds that match the theme.

IM show documentation

Leave a Reply