Tap Happy

Final Presentation Setup:

 

^ Two patches, one has two tappable areas, the other has three.

^ Swapping out the patches to test.

^ Design samples on the various colors of canvas. The texture was a bit too rough for this application.

     

^ Process of making the patches

First Tests with taps + conductive ink:

Example of two user animations, created on presentation day:

Arduino Code:
//--------CAP1188 SETUP --------
#include
#include
#include
#define CAP1188_SENSITIVITY 0x1F // Setting the sensitivity....lower the number the more sensitive!!
static const int CAP1188_RESET = 12; // Reset Pin is used for I2C or SPI
Adafruit_CAP1188 cap = Adafruit_CAP1188();
//------------------------------

const int numOfCaps = 3;

//-----CAP TOUCH Variables-----
uint8_t touched;
uint8_t light;
int capState[numOfCaps] = {0, 0, 0};
int lastCapState[numOfCaps] = {0, 0, 0};
int capCurrent[numOfCaps] = {false, false, false};

//-----TIMING N@T------
int pinState[numOfCaps] = {1, 1, 1}; //debouncing
bool timing[numOfCaps] = {false, false, false};
uint32_t dbTime = 20;
uint32_t eTime[numOfCaps] = {};
uint32_t dbStart[numOfCaps] = {};
uint32_t start[numOfCaps] = {};
uint32_t sendTime[numOfCaps] = {};

//-----TIMING BREAK-----
static const int btnPin = 2;
int btnState;
int lastBtnState;

void setup() {
Serial.begin(115200);
Serial.println("CAP1188 test!");
pinMode(btnPin, INPUT);
// Initialize the sensor, if using i2c you can pass in the i2c address
if (!cap.begin()) {
//if (!cap.begin()) {
Serial.println("CAP1188 not found");
while (1);
}

Serial.println("CAP1188 found!");
cap.writeRegister(CAP1188_SENSITIVITY, 0x6F);

}

void loop() {
touches(); // sensing for touches
for (int i = 0; i < numOfCaps; i++) {
touchOnce(i);
debounceBtn(i);
timeTest(i);
}
endSession();
}

void touches() {
touched = cap.touched();
light = cap.readRegister(0x3);

if (touched == 0) {
// No touch detected
return;
}

for (uint8_t i = 0; i < 8; i++) {
if (touched &amp; (1 &lt;<i> dbTime) {// db timer has elapsed
capState[n] = pinState[n]; // button state is valid
}
//+++++++++++++++++++++++++++++++++++
}</i>

void timeTest(int t) {
if (capState[t] == 0 &amp;&amp; !timing[t]) { //if button state is NOT pressed and timing is NOT happening
sendTime[t] = 0;
start[t] = millis(); // start timing
timing[t] = true;
}

if (capState[t] == 1 &amp;&amp; timing[t]) { //if button state is pressed and timing is happening
eTime[t] = (millis() - start[t]); // record elapsed time
sendTime[t] = eTime[t];
timing[t] = false;
}
}

void endSession() {
// read the pushbutton input pin:
btnState = digitalRead(btnPin);

// compare the buttonState to its previous state
if (btnState != lastBtnState) {
// if the state has changed, increment the counter
if (btnState == HIGH) {
Serial.println("E");
}
// Delay a little bit to avoid bouncing
delay(50);
}
// save the current state as the last state, for next time through the loop
lastBtnState = btnState;
}

void serialButtonCount() {
Serial.print(capCurrent[2]);
Serial.print(",");
Serial.print(capCurrent[1]);
Serial.print(",");
Serial.print(capCurrent[0]);
// Serial.print(" ");
Serial.print(",");
Serial.print(sendTime[2]);
Serial.print(",");
Serial.print(sendTime[1]);
Serial.print(",");
Serial.println(sendTime[0]);
}
Processing Code Two Tap:
import gifAnimation.*;
import processing.serial.*;

float x, y;
color col1, col2, col3;
color[] colorVal = new color[10];
String[] values;

GifMaker gifExport;
int frames = 0;

int bOne; //button one
int bTwo; //button two
int bThree; //button three
int tOne; //time one
int tTwo; //time two
int tThree; //time three

float colMap1, colMap2, colMap3;
float growMap1, growMap2, growMap3;

int endState = 0;
int lastEndState;

int xpos1, ypos1, xpos2, ypos2, xpos3, ypos3;
float radius1 = 100;
float radius2 = 100;
float radius3 = 100;

int pos1x = 200;
int pos1y = 550;
int pos2x = 400;
int pos2y = 250;
int pos3x = 400;
int pos3y = 550;

Circle myCircle;

int num = 0;

void setup() {
size(800, 800);
frameRate(12);
noFill();
ellipseMode(CENTER);
//colorMode(RGB, 255);

//update the serial port index based on your setup
Serial arduino = new Serial(this, Serial.list()[3], 115200);
arduino.bufferUntil('\n');

ellipseMode(RADIUS);
myCircle = new Circle(pos1x, pos1y, pos2x, pos2y, pos3x, pos3y);

gifExport = new GifMaker(this, "export.gif");
gifExport.setRepeat(0); // make it an "endless" animation
//gifExport.setTransparent(0, 0, 0); // make black the transparent color. every black pixel in the animation will be transparent
}

void draw() {

background(240, 240, 240);
noFill();

tapEvent();

fill(col2);
noStroke();
ellipse(pos2x, pos2y, 100, 100);

fill(col3);
noStroke();
ellipse(pos3x, pos3y, 100, 100);
//println(gifExport);
gifExport.setDelay(1);
gifExport.addFrame();

saveGif();
}

void tapEvent() {
colMap1 = map(tOne, 100, 2000, 255, 150);
colMap2 = map(tTwo, 100, 2000, 255, 150);

if (bThree == 1) {
col3 = color(colMap1, colMap2, 255);
myCircle.display();
myCircle.grow();
} else if (bThree == 0) {
col3 = color(240, 240, 240);
}
if (bTwo == 1) {
col2 = color(colMap1, colMap2, 255);
myCircle.display();
myCircle.grow();
} else if (bTwo == 0) {
col2 = color(240, 240, 240);
}
if (bOne == 1) {
col1 = color(colMap1, colMap2, 255);
myCircle.display();
myCircle.grow();
} else if (bOne == 0) {
col1 = color(240, 240, 240);
}
}

void saveGif() {
if (endState != lastEndState) {
// if the state has changed, increment the counter
if (endState == 1) {
boolean finished = gifExport.finish(); //exports gif
num++;
println(finished);
if (finished == true) {
gifExport = new GifMaker(this, "export" +str(num)+ ".gif");
gifExport.setRepeat(0); // make it an "endless" animation
}
endState = 0;
}
// Delay a little bit to avoid bouncing
delay(50);
}
// save the current state as the last state, for next time through the loop
lastEndState = endState;
}

void serialEvent(Serial p) { // code from Rebecca Fienbrink
String rawString = p.readString(); //read the string from serial
rawString = rawString.trim(); //trim any unwanted empty spaces
try { //split the string into an array of 2 value (e.g. "0,127" will become ["0","127"]
values = rawString.split(",");
//for(int i =0; i 1) {
bOne = int(values[0]); //convert strings to int
bTwo = int(values[1]); //convert strings to int
bThree = int(values[2]); //convert strings to int
tOne = int(values[3]);
tTwo = int(values[4]);
tThree = int(values[5]);
//println(values[0] + "," + values[1] + ","+ values[2] );
//println(values[3] + "," + values[4] + ","+ values[5] );
} else if (values.length == 1) {
endState = 1;
//println(end);
} else {
endState = 0;
}
}
catch(Exception e) {
println("Error parsing string from Serial:");
e.printStackTrace();
}
}

class Circle {
// The Constructor is defined with arguments.
Circle(int x1, int y1, int x2, int y2, int x3, int y3) {

xpos1 = x1;
ypos1 = y1;
xpos2 = x2;
ypos2 = y2;
xpos3 = x3;
ypos3 = y3;
}

void display() {
stroke(0);
strokeWeight (3);
noFill();
//ellipse(xpos1, ypos1, radius1, radius1);
strokeWeight (3);
noFill();
ellipse(xpos2, ypos2, radius2, radius2);
strokeWeight (3);
noFill();
ellipse(xpos3, ypos3, radius3, radius3);
}

void grow() {
growMap1 = map(tOne, 100, 8000, 1, 4);
growMap2 = map(tTwo, 100, 8000, 1, 4);
growMap3 = map(tThree, 100, 8000, 1, 4);
//if (bOne == 1) {
// radius1 = radius1 + growMap1;
//}
if (bTwo ==1) {
radius2 = radius2 + growMap2;
}
else if (bThree == 1) {
radius3 = radius3 + growMap3;
}
}
}
Processing Code Three Tap:
import gifAnimation.*;
import processing.serial.*;

float x, y;
color col1, col2, col3;
color[] colorVal = new color[10];
String[] values;

GifMaker gifExport;
int frames = 0;

int bOne; //button one
int bTwo; //button two
int bThree; //button three
int tOne; //time one
int tTwo; //time two
int tThree; //time three

float colMap1, colMap2, colMap3;
float growMap1, growMap2, growMap3;

int endState = 0;
int lastEndState;

int xpos1, ypos1, xpos2, ypos2, xpos3, ypos3;
float radius1 = 100;
float radius2 = 100;
float radius3 = 100;

int pos1x = 200;
int pos1y = 550;
int pos2x = 400;
int pos2y = 250;
int pos3x = 600;
int pos3y = 550;

Circle myCircle;

int num = 0;

void setup() {
size(800, 800);
frameRate(12);
noFill();
ellipseMode(CENTER);
//colorMode(RGB, 255);

//update the serial port index based on your setup
Serial arduino = new Serial(this, Serial.list()[3], 115200);
arduino.bufferUntil('\n');

ellipseMode(RADIUS);
myCircle = new Circle(pos1x, pos1y, pos2x, pos2y, pos3x, pos3y);

gifExport = new GifMaker(this, "export.gif");
gifExport.setRepeat(0); // make it an "endless" animation
//gifExport.setTransparent(0, 0, 0); // make black the transparent color. every black pixel in the animation will be transparent
}

void draw() {

background(240, 240, 240);
noFill();

tapEvent();

fill(col1);
noStroke();
ellipse(pos1x, pos1y, 100, 100);

fill(col2);
noStroke();
ellipse(pos2x, pos2y, 100, 100);

fill(col3);
noStroke();
ellipse(pos3x, pos3y, 100, 100);
//println(gifExport);
gifExport.setDelay(1);
gifExport.addFrame();

saveGif();
}

void tapEvent() {
colMap1 = map(tOne, 100, 2000, 255, 150);
colMap2 = map(tTwo, 100, 2000, 255, 150);
colMap3 = map(tThree, 100, 2000, 255, 150);

if (bThree == 1) {
col3 = color(colMap1, colMap2, colMap3);
myCircle.display();
myCircle.grow();
} else if (bThree == 0) {
col3 = color(240, 240, 240);
}
if (bTwo == 1) {
col2 = color(colMap1, colMap2, colMap3);
myCircle.display();
myCircle.grow();
} else if (bTwo == 0) {
col2 = color(240, 240, 240);
}
if (bOne == 1) {
col1 = color(colMap1, colMap2, colMap3);
myCircle.display();
myCircle.grow();
} else if (bOne == 0) {
col1 = color(240, 240, 240);
}
}

void saveGif() {
if (endState != lastEndState) {
// if the state has changed, increment the counter
if (endState == 1) {
boolean finished = gifExport.finish(); //exports gif
num++;
println(finished);
if (finished == true) {
gifExport = new GifMaker(this, "export" +str(num)+ ".gif");
gifExport.setRepeat(0); // make it an "endless" animation
}
endState = 0;
}
// Delay a little bit to avoid bouncing
delay(50);
}
// save the current state as the last state, for next time through the loop
lastEndState = endState;
}

void serialEvent(Serial p) { // code from Rebecca Fienbrink
String rawString = p.readString(); //read the string from serial
rawString = rawString.trim(); //trim any unwanted empty spaces
try { //split the string into an array of 2 value (e.g. "0,127" will become ["0","127"]
values = rawString.split(",");
//for(int i =0; i 1) {
bOne = int(values[0]); //convert strings to int
bTwo = int(values[1]); //convert strings to int
bThree = int(values[2]); //convert strings to int
tOne = int(values[3]);
tTwo = int(values[4]);
tThree = int(values[5]);
//println(values[0] + "," + values[1] + ","+ values[2] );
//println(values[3] + "," + values[4] + ","+ values[5] );
} else if (values.length == 1) {
endState = 1;
//println(end);
} else {
endState = 0;
}
}
catch(Exception e) {
println("Error parsing string from Serial:");
e.printStackTrace();
}
}

class Circle {
// The Constructor is defined with arguments.
Circle(int x1, int y1, int x2, int y2, int x3, int y3) {

xpos1 = x1;
ypos1 = y1;
xpos2 = x2;
ypos2 = y2;
xpos3 = x3;
ypos3 = y3;
}

void display() {
stroke(0);
strokeWeight (3);
noFill();
ellipse(xpos1, ypos1, radius1, radius1);
strokeWeight (3);
noFill();
ellipse(xpos2, ypos2, radius2, radius2);
strokeWeight (3);
noFill();
ellipse(xpos3, ypos3, radius3, radius3);
}

void grow() {
growMap1 = map(tOne, 100, 8000, 1, 4);
growMap2 = map(tTwo, 100, 8000, 1, 4);
growMap3 = map(tThree, 100, 8000, 1, 4);
if (bOne == 1) {
radius1 = radius1 + growMap1;
} else if (bTwo ==1) {
radius2 = radius2 + growMap2;
} else if (bThree == 1) {
radius3 = radius3 + growMap3;
}
}
}

Future Considerations:

I want to continue to work on this project. Considerations for the patch include fabric type and design of the tappable areas. For the Processing end of things, I’d like to explore what else is possible with the data being sent and well as build a state machine and initialization sequence so that the system knows how many tappable areas are present when plugged in so that two Processing sketches are not needed. On the Arduino side of things, I would like use timing to save the GIF animations rather than a button press.

An interactive music box

An wireless music player with physical interaction to minimize distraction.

DESCRIPTIVE PARAGRAPH

Why a music box?

I like to put music on while i’m working, however in this digital age, the “recommendations” and changing between screens can be quite a distraction. A music box with physical interaction should be able to minize that as much as possible, while still kept the interaction entertaining.

How it works:

The music box would come with figurines with RFID tags. When different figurines are put on top of the music box,  different sets of music would play. Ideally, if it can be achieved, the user can put their own music album and make their own figurine.

 

YOUR PLAN

The final project would be based on the previous assignment where I used touch sensor and Arduino Uno with a simple p5js interface. Here is the basic outline of the project:

  1. Incorporate/test RFID onto the project
  2. Connect speaker to the box instead of using laptop
  3. Refine the music library
  4. Refine the p5js interface
  5. Add the feature where user are able to make their own input
  6. Switch Arduino Uno to a Particle Photon
  7. Add lipo battery and battery shield/battery baby sitter
  8. Make a shell with laser cutted plywood

MATERIALS NEEDED

  1. Arduino Uno/Particle Photon
  2. RFID Reader chip and RFID tags
  3. Wifi base (I might be able to just use my laptop)
  4. Plywood for laser cutted bounding box
  5. Music Pieces (20s-30s per sample)
  6. Lipo battery
  7. Battery shield/baby sitter
  8. Some LEDs for indication
  9. Some buttons for play buttons etc.
  10. A cheap speaker should workout

DELIVERABLES FOR SHOW

On the day of the show, I would need a outlet for charging the battery, and a wifi base, which potentially could be my laptop. The speaker won’t be too loud so it should be ok to be somewhat close to other projects.

MEDIA FOR SHOW

I probably would put up a slide show on my laptop or some print out handouts/flyers

Project Proposal: An assistant for a healthy mind

Title: An assistant to promote a healthy mind.

Existing home assistants improve interaction in the home by using speech recognition and intelligent natural language processing. However, they lack the emotional intelligence needed to listen, motivate and support the user and provide support towards a healthy mind. My project proposes the use of a custom google assistant through AIY voice kit to detect emotion, provide social support and generate empathy in the user.

Proposed plan

  • Build custom Google home
  • Provide an avatar to assistant through the hologram
  • Build custom intents to listen to music, contact social circles actively and passively.
  • Detect emotion from voice or NLP
  • Detect user and location (optional)
  • A private dashboard for others to monitor the mental health of the user. (optional)

MATERIALS NEEDED

  • Old phone/display for assistant
  • Laser-cut transparent plastic
  • AIY Voice kit
  • Hardware neopixels to display assistant mood. (optional)

DELIVERABLES FOR SHOW

  • Half a table.

MEDIA FOR SHOW

Slideshow on a laptop

Story – Music Box

I listen to a lot of musicals, and that is my background music when I work. Listen to the same thing over and over again is nice to some extent, however, stories might be told differently from different character’s perspective. What if the version of the story is from Elisa Schulyer instead of Alexander Hamilton? What if Cosette is the major character? Not that the music need to change (for a new show, probably).

It would be really interesting to make a interactive music box that have the same song but tells the same story from a different sequence based on the main character that you can choose. It should be mobile, with a rechargable battery, like a real music box (it would be a interesting gift). It doesn’t have to be just musical, it can get connected to wifi and search for certain artist that you wanna listen to. All you need to do is to put a figurine on top and click the play button.

What would be the advantage of having a music box than just use your phone or your computer? Well, say that you are using some music software like spotify. When you want to change a song, you have to go to that interface and click the music you want to listen to. Opening up another page could be really distracting, spotify might also recommend you this and that which doesn’t help. Having a physical interaction can potentially make you less distracted cause there is no “recommend based on history” at least.

 

 

Smart desk

Computers and technology are an integral part of most people’s everyday lives. Individuals push themselves to have maximum productivity every day. In many cases, this drive to work has caused many people to disregard their own health and self-care. A pattern I’ve noticed in the tech industry is workers changing their work station set-up to be more ergonomic. Standing desks and unconventional desks are increasingly popular. Yet, it seems these changes help workers spend even more time in front of the screen. My vision is to create a work station that not only helps increase productivity, but also reminds the user to be human. From the minute the user approaches the desk, they will have a intuitive and seamless work session experience. Using light, timers, motors, and various sensors, the user will be able to organize their tasks while remembering to follow best practices for physical health. Many of us get in a “zone” while we work, which causes us to focus for hours on end without moving an inch. This desk will remind users to walk around and take a break on timed intervals. It will monitor the environment to ensure users are comfortable. It will have small “helpers” that will make working feel less tedious so users can really focus on their work. The desk will also try to accommodate people with vision and hearing problems by implementing different methods of user alert systems.

Asssignment 8–Arduino Timer

#include

#ifdef __AVR__
#include
#endif

static const int PIN = 3;
static const int NUMPIXELS = 2;
int incomingByte;
char incomingLetter;
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
unsigned long interval = 10000;
unsigned long previousMillis = 0;

void setup() {
pixels.begin();
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
pixels.show();

}

void loop() {

unsigned long currentMillis = millis();

if (Serial.available() > 0) {
incomingByte = Serial.read();
incomingLetter = (char) incomingByte;
if (incomingLetter == ‘S’) {
while(currentMillis – previousMillis < interval) {
currentMillis = millis();
}

previousMillis = currentMillis;
pixels.setPixelColor(0, pixels.Color(255, 0, 0));
pixels.show();
}

else if (incomingLetter == ‘B’) {
while(currentMillis – previousMillis < interval){
pixels.setPixelColor(1, pixels.Color(0, 255, 0));
pixels.show();
currentMillis = millis();
}
previousMillis = currentMillis;
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
pixels.show();
}

else {
pixels.setPixelColor(0, pixels.Color(0, 0, 0));
pixels.setPixelColor(1, pixels.Color(0, 0, 0));
pixels.show();
}
}
}

Final project storyboarding – storytelling musicbox

The general idea of interaction goes like this:

  1. place some figurine on top of the box, assume the sequence being figurine1 (f1 for short),f2,f3……
  2. Hit play button to play music (where all figurine is a part of, from the very first story)
  3. remove some figurine and add more figurine or do nothing to play more music:
    1. if nothing change, go to the next song that all of them are in follow the story sequence
    2. if new figurine added, play the next song they all in
    3. if figurine got removed: (assume f1, f2,f3 was there)
      1. if f2 or f3 got removed, same as new figurine added
      2. if f1 got removed, f2 becomes the pivot*.

 

  • *Pivot in this case is that favour the pivot figurine, if no songs are really up to standards(eg: strictly solo/duet).

 

Touchy: A Game for Toddlers

Abstract: Touchy a game for toddlers.

Method: Ethnographic study,  Participatory Design with Parents

I spent a few months getting to know the participant, babysitting the kid. This helped me learn the interactions that the user was familiar with. I used the design elements that player was familiar with for e.g. Hey Dugee.

Playtesting: A demo of a player using the interface.

Parent Feedback:

Next steps: Add sound, use the yellow car a toy that

Demo: http://kmarkiv.com/a5/A5js/

[update] 

After seeking feedback from parents, I added a button to support to direct the child’s attention to the screen.