FINAL PROJECT: no more stress

💫Reminder

My final project is about creating art works and relieving stress simultaneously!

📌Links to go through my previous posts:

Week 13: Final Project Progress

Final Project Progress [week 14]

🔧Setup

I wanted to beat something soft, so I decided on sponges. The only ones in the immediate vicinity were kitchen yellow sponges, and I used them. In order to attach them to the table, I needed to thread a thread through them and attach them with tape. To make the distance sensor at the same height with them, I had to spend another sponge and a lot of tape.

Changes

  • I moved the buttons in the Processing on the Arduino so that I wouldn’t move my hands from the top of the table while I was working. I also added an LED as an identifier of when the session started and when it ended.
  • I added a song to the background, as well as a final screen that suggests starting the session again.
  • Generative art spots don’t appear until the user selects a color, so I was able to make them transparent.

Code

Full code and all additional data are on my gitHub.

  • In my handshake from Arduino side I sent 5 values and received 1
    Serial.println("0,0,0,0,0");
    
  • I added screenshot feature
void keyReleased() {
  if (keyCode == TAB) saveFrame(timestamp()+"_##.png");
}
  • There is a separate class for spots
  • Generative art letters code is partially taken from this website (thank you, Professor!)

💡Testing

Now my project is much more user-friendly, especially after previous week testing. During testing we were choosing the song that in our opinion suits this purpose and decided to use Linkin Park – Numb.

🐋Result

Final Project Progress [week 14]

I’ve been working on the final project for the last few days, and I’ve run into a few problems along the way. Now I will share the progress at the moment, as well as the main challenges. I finished the main part of the code, where I established serial communication between Arduino and Processing. You can read the whole code here. Some challenges I encountered: it took time to establish serial communication for two variables for several reasons. One example is how I forgot to change “println” to “print” when sending the information from the distance sensor. Little mistake caused 30 minutes of checking every single line of code. Also, making generative art of different colors was challenging because the order of lines could change everything.

Here you can see the progress since the last week:

>>>

>>>>>>

>>>

In order to demonstrate how my program works, I asked my friend to take a video of me, you can watch it here.

Testing was done by my friend, who helped to identify several shortcomings. First, the interface is not clear enough, so I had to explain some of the details myself, for example, where to click to take a screenshot. Now I know where to add any signifiers. Secondly, clicking on the buttons in the processing was not very convenient, and I’m thinking of moving them to the Arduino. Third, there is a lack of a final screen where you can go back and start again or end the session.

I need to work with the interface base on the feedback I received during testing, add music, work with the representation of Arduino (make it more convenient to use and intuitive), maybe add LEDs as a signifier.

Week 13: Final Project Progress

After finalizing the idea for this project, I started to turn it into reality step by step. First of all, I wrote the rough plan so I can follow it and track my progress:

1.  Make distance sensor work

2. Connect it to the Processing (easiest ellipses appear on the screen)

3. Work on the Processing interface: spots that will appear on the screen by generative art, words written

4. Add buttons to Arduino to choose colors in Processing

5. Add potentiometer to Arduino to change the intensity of the color/shape of the generative art

6. Add backgrounds and transitions – make interface user-friendly.

Now I am on the second-third step, where I am still working on the distance sensor and doing generative art in Processing. To learn how to work with ultrasonic distance sensor, I made its readings control the radius of the ellipse appearing on the  Processing screen. I am thinking of adding its readings to the ArrayList which will serve as a dynamic array of spots appearing on the screen. For this I need to add class for spots/particles, and I am currently implementing it.

Talking about generative art, I am playing with shapes and colors, as user will do too. Here you can see how it looks for now:

The code I have for now is on my GitHub.

Final Project Preliminary Idea [week 11]

For the final project, I want to create a game that will help improve a player’s mental health. In this game, a player should destroy objects to maintain the stress level of the character and simultaneously by releasing their stress by hitting 3D buttons. Moreover, this game requires concentration which is proved to be helpful in combating mental health problems.

[UPDATE]

  • The finalized concept for the project

I modified the idea I had a few days ago, but the goal stayed the same – create a project that will help to cope with stress. Stress is one of the most common problems people face in their daily lives, and as a student before final exams, I realize it especially strongly.

This time it won’t be a game but a way to create a work of art while releasing the stress. The algorithm of creating an art piece will be following:

  1. Choose the word that causes your stress. It could be just “stress,” “assignment,” “Calculus” or I would’ve written “C++” last semester. Write it, and it will appear on the screen.
  2. Now the most fascinating part begins: you will hit this word! Obviously, not the word itself, but the latex stretched over the frame. Behind it, there will be a distance sensor to understand where you hit this time and display your actions on the screen.
  3. Each hit will be seen as a spot in a certain color and a certain place. It will be possible to change the color of the spots by pressing the buttons on the Arduino, as well as the intensity of the applied color with a potentiometer.
  4. Also, there will be few songs to choose from to hit the word that causes your stress with even more satisfaction.
  5. I am thinking of doing these colored spots using generative art to make each piece even more unique.

The process of hitting will probably help to reduce the stress, and the result like the unique artwork will help to elevate the mood. 

  • Arduino

The Arduino program will receive inputs from buttons, potentiometer, and ultrasonic distance sensor, and will send them to Processing. Distance sensor will determine the distance to the hand hit through the latex. 

  • Processing

The Processing program will receive data from Arduino, and use it in the art piece creation, by adding colored spots of different intensity on the chosen word. Pressed buttons’ colors will determine the colors used in Processing, potentiometer readings will change the intensity of the color. The distance values will affect the location of spots appearing on the screen. 

serial communication exercises [week 11]

Exercise 1:  make something that uses only one sensor  on arduino and makes the ellipse in processing move on the horizontal axis, in the middle of the screen, and nothing on arduino is controlled by processing;

Arduino:

const int knobLed = A0; //sign up variable
 
void setup() {
  Serial.begin(9600); //set the baud rate
  Serial.println("0"); //start of the handshake, start the call-response pattern
  pinMode(A0, INPUT);
}
 
void loop() {
  while (Serial.available()) { //whenever there is anything available in serial buffer, do
    if (Serial.read() == '\n') { //whenever there is new line, it means that all info came correctly
      int sensor = analogRead(knobLed); //read from potentiometer
      delay(1);
      Serial.println(sensor); //send info to the processing
    }
  }
}

Processing:

import processing.serial.*; //import library
Serial myPort; //create serial object
int xPos=0; 
int yPos;
 
void setup() {
  size(960, 720);
  yPos=height/2;
  printArray(Serial.list()); //printing array
  String portname=Serial.list()[1]; //choose the correct port number!
  println(portname);
  myPort = new Serial(this, portname, 9600); //set the baud rate
  myPort.clear(); //clear the buffer
  myPort.bufferUntil('\n'); //wait until new line
}
 
void draw() {
  background(255);
  ellipse(xPos, yPos, 30, 30);
}
 
void serialEvent(Serial myPort) { //whenever receives incoming serial message, this function is called 
  String s=myPort.readStringUntil('\n'); //read the string until new line character
  s=trim(s); //safeguard to trim extra characters (whitespaces)
  if (s!=null) { //check to make sure it's not null
    println(s); 
    int value = int(s);  //sending value s
    xPos=(int)map(value, 0, 1023, 0, width); 
  }
  myPort.write("\n"); //other side of the handshake, send back over to arduino
}

Exercise 2: make something that controls the LED brightness from processing;

Arduino:

float brightness;

void setup() {
  Serial.begin(9600); //set the baud rate
  Serial.println("0"); //start the handshake
  pinMode(5, OUTPUT);
}

void loop() {
  while (Serial.available()) {
    brightness = Serial.parseFloat(); //receive brightness info in a string from processing
    if (Serial.read() == '\n') { //whenever there is new line, it means that all info came correctly
       analogWrite(5, brightness); //change the brightness of LED accordingly
       Serial.print("\n"); //send  new line to processing
    }
  }
}

Processing:

import processing.serial.*;
Serial myPort; //create serial object
float brightness; //variable to send to arduino
 
void setup(){
  size(960,720);
  printArray(Serial.list());
  String portname=Serial.list()[1]; //choose the correct port number!
  println(portname);
  myPort = new Serial(this,portname,9600); //set the baud rate
  myPort.clear(); //clear the buffer
  myPort.bufferUntil('\n'); //wait until new line
}

void draw(){
  background(255);
  ellipse(mouseX,mouseY,30,30);
  brightness = map(height-mouseY, 0, height, 0, 255); 
}

void serialEvent(Serial myPort){ //whenever receives incoming serial message, this function is called 
  println(brightness); 
  myPort.write(int(brightness)+"\n"); //other side of the handshake, send back over to arduino
}

Exercise 3: take the gravity wind example and make it so every time the ball bounces one led lights up and then turns off, and you can control the wind from one analog sensor;

Arduino:

const int knobLed = A0;
const int led1 = 3;
int onOff = 0;

void setup() {
  Serial.begin(9600); //set the baud rate
  Serial.println("0"); //start the handshake
}

void loop() {
  while (Serial.available()) {
    onOff = Serial.parseInt(); //receive onOff info from processing
    if (Serial.read() == '\n') { //whenever there is new line, it means that all info came correctly
      int sensor = analogRead(knobLed); //read from potentiometer
      delay(1);
      Serial.println(sensor); //send info to processing
            if (onOff == 1) { 
              analogWrite(led1, 255);//turn on LED
            } else {
              analogWrite(led1, 0); //turn off LED
            }
    }
  }
}

Processing:

PVector velocity;
PVector gravity;
PVector position;
PVector acceleration;
PVector wind;
float drag = 0.99;
float mass = 50;
float hDampening;

import processing.serial.*;
Serial myPort; //create serial object
int knob;

void setup() {
  size(640,360);
  noFill();
  position = new PVector(0, 0);
  velocity = new PVector(0,0);
  acceleration = new PVector(0,0);
  gravity = new PVector(0, 0.5*mass);
  wind = new PVector(0,0);
  hDampening=map(mass,15,80,.98,.96);
  
  String portname=Serial.list()[1]; //choose the correct port number!
  myPort = new Serial(this,portname,9600); //set the baud rate
  myPort.clear(); //clear the buffer
  myPort.bufferUntil('\n'); //wait until new line
}

void draw() {
  background(255);
  if (!keyPressed){
    wind.x=knob;
    velocity.x*=hDampening;
  }
  applyForce(wind);
  applyForce(gravity);
  velocity.add(acceleration);
  velocity.mult(drag);
  position.add(velocity);
  acceleration.mult(0);
  ellipse(position.x,position.y,mass,mass);
  if (position.y > height-mass/2) {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      position.y = height-mass/2;
    }
}
  
void applyForce(PVector force){
  // Newton's 2nd law: F = M * A
  // or A = F / M
  PVector f = PVector.div(force, mass);
  acceleration.add(f);
}

void keyPressed(){
  if (keyCode==LEFT){
    wind.x=-1;
  }
  if (keyCode==RIGHT){
    wind.x=1;
  }
  if (key==' '){
    mass=random(15,80);
    position.y=-mass;
    velocity.mult(0);
  }
}

void serialEvent(Serial myPort){ //whenever receives incoming serial message, this function is called 
  String s=myPort.readStringUntil('\n');  //read the string until new line character
  s=trim(s);  //safeguard to trim extra characters (whitespaces)
  if (s!=null){ //check to make sure it's not null
    println(s);
    int values[]=int(split(s,',')); //sending value s
    knob=(int)map(values[0],0,1023,0, width*.01);
  }
  if (round(velocity.y) < 0) {
    myPort.write(1 + "\n");
  } else {
    myPort.write(0 + "\n");
  }
}

Video:

“Musical Instrument”

This week we worked with the sound on Arduino, and I played around and tried to create a “musical instrument.” To mention before looking at my work: I have (had) zero knowledge of musical theory, and I had a hard time checking whether notes that play are the right ones. Also, I didn’t know how tempo works.

Let’s look at my “instrument”!
It consists of 4 buttons, buzzer, photoresistor, and servo. Initially, there were three buttons, but I added the yellow one so “twinkle twinkle little star” can play. I spent too much time figuring out how to make these arrays work not to include them. However, I still haven’t figured out how to use it without “delay” and gave up.
The other three buttons are responsible for one note each, namely C4, F4, and G4. Simultaneously, by blocking the light from the photoresistor (this example was cool), the servo moves accordingly. I tried to make it sound louder by placing it on my RedBull can.

Result:

Code:

#include "pitches.h"
#include <Servo.h>

const int tonePin = 4;
const int buttonPin1 = 12;
const int buttonPin2 = 2;
const int buttonPin3 = 5;
const int buttonPin4 = 3;
const int servoPin = 9;
Servo servo;

const int rows = 6;
const int columns = 7;

int notes[rows][columns] = {{NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4},
  {NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4}, {NOTE_G4, NOTE_G4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4},
  {NOTE_G4, NOTE_G4, NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4}, {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4},
  {NOTE_F4, NOTE_F4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4}
};

int tempo[rows][columns] = {4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2};

void setup() {
  Serial.begin (9600);
  servo.attach(servoPin);
  pinMode(tonePin, OUTPUT);
  pinMode(buttonPin1, INPUT);
  pinMode(buttonPin2, INPUT);
  pinMode(buttonPin3, INPUT);
  pinMode(buttonPin4, INPUT);

}

void loop() {
  int prValue = analogRead (A1);
  int mapValue = map(prValue, 185, 610, 0, 180);
  servo.write(mapValue);

  if (digitalRead (buttonPin1) == HIGH) {
    int noteLength = 250;
    tone(4, NOTE_C4, noteLength);
  }

  if (digitalRead (buttonPin2) == HIGH) {
    int noteLength = 250;
    tone(4, NOTE_F4, noteLength);
  }
  
  if (digitalRead (buttonPin3) == HIGH) {
    int noteLength = 250;
    tone(4, NOTE_G4, noteLength);
  }

  if (digitalRead (buttonPin4) == HIGH){
  for (int i = 0; i < rows; ++i) {
      for (int j = 0; j < columns; ++j) {
        int noteLength = 1000 / tempo[i][j];
        int pausee = noteLength * 1.30;
        tone(4, notes[i][j], noteLength);
        delay(pausee);
      }
      }
}

}

#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494

 

 

(late) memory game on Arduino

Inspiration:

This week I decided to make a game, and the memory game came to mind first. I remember that in high school, when I found out about the existence of Arduino, I wanted to make it but then I didn’t have enough time or knowledge. I’m glad I did it now as part of this course.

Game Rules:

The game starts with three flashing LEDs. To start the sequence, user should turn on red LED with potentiometer (analog input). Then, the random LED starts to light up, and the player must press the button of the same color within 2.5 seconds after it goes out. Then another LED lights up, and so their number increases. If the player messes up the order or does not have time for 2.5 seconds, the game ends, the LEDs light up again until the game starts again automatically.

Problems and Solutions:

  • It took me much longer than I expected. I started from doing the easiest circuits from the class until I did my own circuit with 3 LEDs, 3 button, 1 potentiometer, and 8 resistors. After that, I started coding the game. I visited multiple tutorials/examples and this, this, and this were he most helpful. To be fair, I discovered these useful resources very late, so I’ve done most of the coding myself and only then changed it to much more convenient ways. Learned a lot, honestly.
  • I used randomSeed() to make the random LED light up
  • To add analog input, I did it as a start for the game and added to the first if statement
  • Resistors and buttons are hard to add to the board…

Final result!!!

Code: https://github.com/Alima2104/first-arduino-game

Midterm game

For the midterm project, I did a platform game named. The complete code and all resources can be found here.
This game is for 2 players. The goal is to go through “trials”, collect crystals, and get to the airplane to fly away as soon as possible. There is a small problem – you only have 60 seconds.
I wanted to try to create a game myself, similar to the one I played as a child. I always liked the opportunity to play with a friend at the same time, so I decided to add this opportunity to my game.
For this game, I created several classes: 2 character classes, a platform class, a platform class that cannot be stepped on aka platform enemies, a timer class, an airplane class, and a crystal class. Use the A, W, D keys to move Player 2 and the arrow keys to move Player 1.

Problems:
1. It was not easy to create interaction between the character and the rest of the classes – for the game to be interactive, the player must see everything that his actions in the game lead to. I spent a lot of time trying to figure out what kind of response should follow from the game to various interactions, whether this is a “game over” window or, on the contrary, congratulations on winning.
2. It took time to figure out what the distance will be between the objects at the moment of collision and from which side. It was even harder to understand when the picture of the character was in relation to the center, not the edge.
3. For the crystal to disappear when the character touches it, I had to try several options and go through dozens of “null pointer exception” errors. I am pleased with the result, where the crystals are neatly collected in the corner of the screen.
4. Having two players in parallel is not easy: it was necessary to understand the key pressed and released functions, and also not to get confused with them later. Because I named some variables the same for different classes, the character of the second player did not want to appear for a long time.

Final thoughts:
There is no limit to perfection, and you can modify the game indefinitely. I already have dozens of ideas on how I will improve this game in the future, from a progress bar to numerous levels. However, I am happy with how this game has turned out for me at the moment.
While writing the code for this assignment, I have used all the knowledge gained from the last 6 weeks and I have a certain sense of satisfaction with the result. Also, coding is very time-consuming, and it’s best to start as early as possible.

Result:

Midterm Progress

For my midterm, I got inspired by one of my favorite childhood games – “Fireboy and Watergirl.” It is an arcade game with two players where characters need to reach the portal avoiding obstacles and collecting diamonds.

Fireboy and Watergirl in the Forest Temple - Play it now at  CoolmathGames.com

I want to draw the sprite sheet for characters myself and make them representatives of other elements. I began the drawing process using Krita software, but it is not ready yet.

For now, I have decided to begin with the instruction screen, game screen, play again screen, and how to navigate through them. For this, I used If statements and the variable “state.” This tutorial was extremely helpful.

I also implemented a timer so how much time left is displayed on the screen. I want to make time left more visually appealing so it won’t be just a text. When time is up, the game is over.

I also added images as backgrounds.

You can see how it looks like here:

Week 4: data visualization

Inspiration:

I think that it is useful to know how to visualize data and generate text in Processing. That’s why this week I explored them both, not focusing only on one of them. To be honest, before this version I had many other attempts to make something creative (lyric video, for example). However, I gave up for now and decided to represent and compare the interest overtime for a few of my favorite TV-shows, Game of Thrones, Sherlock, and Grey’s Anatomy.

Process:

I took the data from Google Trends Explore and had 3 CSV files, which graphs you can see below. I also added text at the top of my work using the geomerative library. I played with sizes, figures, colors, and actually understood what the functions of this library mean using this resource.

I displayed 3 graphs under each other and added titles to them. I also had the timeline that shows which years are displayed. All titles are typewritten (kinda), so they appear letter by letter.

Problems:

This assignment was not as easy as I thought, so I spend hours understanding how things work here. The order of functions is extremely important because few times elements didn’t display only because I messed up the order.

Result:

Code:

import geomerative.*;
RFont font;
RPoint[] pnts;
String phrase = "TV-shows";
String word1 = "Game of Thrones";
String word2 = "Grey's Anatomy ";
String word3 = "Sherlock       ";
float xOffset = 0;
Table table1, table2, table3;
float spacing;
PFont f; 
int counter;
  
float[] intervals = new float[11];

void setup() {
  size(600, 720);
  loadData();
  stroke(255, 0, 0, 75);
  smooth();
  frameRate(5);
  
  f = createFont("Courier New", 10);
  textFont(f);
  
  RG.init(this);
  font = new RFont("Franklin Goth Ext Condensed.ttf", 85, RFont.LEFT);
  
  RCommand.setSegmentLength(2);
  RCommand.setSegmentator(RCommand.UNIFORMLENGTH);
  RGroup grp;
  grp = font.toGroup(phrase);
  grp = grp.toPolygonGroup();
  pnts = grp.getPoints();
  xOffset = width - grp.getBottomRight().x - grp.getBottomLeft().x;
  xOffset = xOffset/2;
  
  for (int i=0; i<10; i++) {
    intervals[i]=width/10*i;
  }

  noFill();
  stroke(255, 255, 255, 85);
}

void loadData() {
  
  // Load CSV file into a Table object
  // "header" option indicates the file has a header row
  table1 = loadTable("GameOfThrones.csv", "csv");
  spacing = float(width)/(table1.getRowCount()-3);
  println(table1.getRowCount()+" "+spacing);
  
  table2 = loadTable("GreysAnatomy.csv", "csv");
  spacing = float(width)/(table2.getRowCount()-3);
  println(table2.getRowCount()+" "+spacing);
  
  table3 = loadTable("Sherlock.csv", "csv");
  spacing = float(width)/(table3.getRowCount()-3);
  println(table3.getRowCount()+" "+spacing);
}

void draw() {
  background(10, 10, 20, 80);

  line(0, height/4, width, height/4);

  
  for (int i=0; i<10; i++) {
    line(intervals[i], height/4+10, intervals[i], height/4-10);
    fill(0, 102, 153, 204);
    textAlign(CENTER);
    textFont(f);
    fill(255);
    text("201"+i, intervals[i], height/4+20);
  }
  
  textAlign(LEFT);
  textSize(20);
  fill(255);
  typewriteText(word1,  width/10, height/4+50);
  
  typewriteText(word2, width/10, height/2+50);
  
  typewriteText(word3, width/10, height/4*3+50);
  
  text("some of my favorite", width/10, 30);
  text("through the years", width-width/2, height/4-55);
  
  noFill();
   
  beginShape();
  for (int i = 3; i < table1.getRowCount(); i++) {
    TableRow row0 = table1.getRow(i);
    float interest = row0.getFloat(1);
    float x = (i-3)*spacing ;
    float y = map(interest, 0, 100, height/2, height/4+50);
    curveVertex(x, y);
  }
  endShape();
  
   beginShape();
  for (int i = 3; i < table2.getRowCount(); i++) {
    TableRow row0 = table2.getRow(i);
    float interest = row0.getFloat(1);
    float x = (i-3)*spacing ;
    float y = map(interest, 0, 100, height*3/4, height/2+50);
    curveVertex(x, y);
  }
  endShape();
  
   beginShape();
  for (int i = 3; i < table3.getRowCount(); i++) {
    TableRow row0 = table3.getRow(i);
    float interest = row0.getFloat(1);
    float x = (i-3)*spacing ;
    float y = map(interest, 0, 100, height, height*3/4+50);
    curveVertex(x, y);
  }
  endShape();
  
  for (int i=0; i< pnts.length; i++) {
    float x = pnts[i].x + xOffset;
    float y = pnts[i].y + height/4-80;

    float diam = 7;
    ellipse(x, y, diam, diam);
  }
}

void typewriteText(String word, float start, float finish){
  
  if (counter < word.length())
    counter++;
  text(word.substring(0, counter), start, finish, width, height);
}