Final Project: Colour Detector for Visually Impaired Individuals

So my final project started with this as the inspiration: http://www.youtube.com/watch?v=xQx6YeoKVwU

I went through various different brainstorming sessions on what to do for visually impaired individuals. At one point I was looking at creating a remote that controlled music players using clapping sounds, which reminded my professor, Matthew, about the “Clap on, clap off” remote: http://www.youtube.com/watch?v=0viUrw0hEKg

In the end of the day, my final project ended up being a colour detector that reads out the colour being analysed. This has many applications in a society where we are so heavily reliant on colours to categorise and describe things i.e. wear a white shirt to a funeral, pick up the blue box, throw things in the green bin etc.

The basic idea for this machine would be to first sort out the output aspect: a music player with predetermined songs/tracks.  Then the input aspect would be a colour sensor.

The output I built using this tutorial: http://apcmag.com/arduino-project-5-digital-audio-player.htm

The result was the following: [INSERT VIDEO]

To create the music player, I used the Wifi shield and its SD card reader to store my tracks. The tracks were made using Garage Band on Mac and converted using this audio converter: http://www.online-convert.com/result/b2ef4b4410687036be375f113f071358

The settings for converting a successful file are: Mono, 8 bit, 16,000 Hz.

Using the above tutorial one gets a working music player, basically a simple version of an iPod. But I wanted more. So then the aspect was to get a working colour sensor to do my colour readings. I used a I2C Grove Colour Sensor. I used the following Wiki tutorial to set it up: http://www.seeedstudio.com/wiki/Grove_-_I2C_Color_Sensor

I then tried to map the colours using the CIE colour space and the x,y readings given by the colour sensor: [INSERT COLOUR MAP]

However, this did not work very well in the end of the day, and I ended up using a simple version, using primarily only x-coordinates and only coding for Red, Green and Blue. The final colour sensor code that I used was provided by this tutorial: http://eiseler.synology.me/wordpress/?p=432

When the both the output and the input were working separately, I wanted to combine the two codes and connect the input and the output together. However, this proved a little difficult, and I ended up having to use two arduino boards and link them. This was a solution developed thanks to Matthew. Then one arduino took care of the output whereas the other took care of the input.

The working project can be seen here: [INSERT VIDEO]

I also decided to create a box using the laser cutter. The box was developed using the template provided by: http://www.makercase.com

Getting the template from MakerCase, I then edited the file in Illustrator to create wholes for buttons and cables. The final project looked like this:

[INSERT PHOTOS OF FINAL PRODUCT]

===========================================

Here are my final codes:

MUSIC PLAYER:

// ———————————————————————————
// DO NOT USE CLASS-10 CARDS on this project – they’re too fast to operate using SPI
// ———————————————————————————

#include <SD.h>
#include <TMRpcm.h>
#include <SoftwareSerial.h>

SoftwareSerial mySerial(3, 2); // RX, TX
TMRpcm tmrpcm;

File root;
File entry;

// ———————————————————————————
// set chipSelect to ’10’ if using the $2 SD card module or ‘4’ if using the
// Ethernet shield’s microSD card instead.
const int chipSelect = 4;
// ———————————————————————————

//const int oldCard = SPI_HALF_SPEED;
const int newCard = SPI_QUARTER_SPEED;

// ———————————————————————————
// set cardType to ‘oldCard’ if using an old SD card (more than a few years old) or
// to ‘newCard’ if using a newly-purchase Class-4 card.
int cardType = newCard;
// ———————————————————————————

int wasPlaying = 0;
int inSwitch = 7;
int finished = 0;
int start = 0;
int pauseOn = 0;
unsigned long timeDiff = 0;
unsigned long timePress = 0;

char ser;

void setup() {
Serial.begin(9600);
Serial.print(“\nInitializing SD card…”);
pinMode(chipSelect, OUTPUT);
if (!SD.begin(chipSelect,cardType)) {
Serial.println(“failed!”);
return;
}
Serial.println(“done.”);

tmrpcm.speakerPin = 9;

pinMode(inSwitch,INPUT_PULLUP);
digitalWrite(inSwitch,HIGH);

root = SD.open(“/”);

mySerial.begin(9600);

//playTrackByTitle(“song.wav”);
}

void loop(void) {

if (mySerial.available()) {
ser = mySerial.read();
} else {
ser = 0;
}
//Serial.write(ser);
//if (Serial.available())
//mySerial.write(Serial.read());

if (ser == 103) {
Serial.println(“g”);
playTrackByTitle(“green.wav”);
}

else if (ser == 98) {
Serial.println(“b”);
playTrackByTitle(“blue.wav”);
}

else if (ser == 114) {
Serial.println(“r”);
playTrackByTitle(“red.wav”);

//Serial.println(“not g”);
/*if(!tmrpcm.isPlaying() && wasPlaying == 1) {
tmrpcm.stopPlayback();
playTrackByTitle(“song.wav”);
}*/
}

if(!tmrpcm.isPlaying() && wasPlaying == 1) {
tmrpcm.stopPlayback();
//playNext();
}

if (millis() – timeDiff > 50) { // check switch every 100ms
timeDiff = millis(); // get current millisecond count

if(digitalRead(inSwitch) == LOW) {

if(start==0) {
start=1;
//playNext();
delay(200);

}
else {

timePress = millis();
while(digitalRead(inSwitch)==LOW) {
delay(50);
}
if (millis() – timePress < 1000 && start == 1) {
tmrpcm.pause();
if (pauseOn == 0) {
pauseOn = 1;
}
else {
pauseOn = 0;
}
}
if (millis() – timePress > 1000 && start == 1) {
if (pauseOn == 1) {
pauseOn = 0;
tmrpcm.pause();
}
tmrpcm.stopPlayback();
timePress = millis();
if (finished == 0) {
//playNext();
}
else {
finished = 0;
Serial.println(“Restarting.”);
root.rewindDirectory();
//playNext();
}
}
}
}
}
}

void playTrackByTitle(char* title) {
tmrpcm.play(title);
}

void playNext() {
entry = root.openNextFile();
if (entry) {
entry.close();
tmrpcm.play(entry.name());
wasPlaying = 1;
}
else {
if (wasPlaying == 1) {
Serial.println(“Completed playback.”);
wasPlaying = 0;
finished = 1;
start = 0;
root.rewindDirectory();
}
}
}

 

COLOUR SENSOR:

/*
Groove Color Sensor and DX LCD Shield

http://www.arduino.cc/en/Tutorial/LiquidCrystal

*/

// include the library code:

#include <Wire.h>
#include <math.h>

#define COLOR_SENSOR_ADDR 0x39//the I2C address for the color sensor
#define REG_CTL 0x80
#define REG_TIMING 0x81
#define REG_INT 0x82
#define REG_INT_SOURCE 0x83
#define REG_ID 0x84
#define REG_GAIN 0x87
#define REG_LOW_THRESH_LOW_BYTE 0x88
#define REG_LOW_THRESH_HIGH_BYTE 0x89
#define REG_HIGH_THRESH_LOW_BYTE 0x8A
#define REG_HIGH_THRESH_HIGH_BYTE 0x8B
#define REG_BLOCK_READ 0xCF
#define REG_GREEN_LOW 0xD0
#define REG_GREEN_HIGH 0xD1
#define REG_RED_LOW 0xD2
#define REG_RED_HIGH 0xD3
#define REG_BLUE_LOW 0xD4
#define REG_BLUE_HIGH 0xD5
#define REG_CLEAR_LOW 0xD6
#define REG_CLEAR_HIGH 0xD7
#define CTL_DAT_INIITIATE 0x03
#define CLR_INT 0xE0
//Timing Register
#define SYNC_EDGE 0x40
#define INTEG_MODE_FREE 0x00
#define INTEG_MODE_MANUAL 0x10
#define INTEG_MODE_SYN_SINGLE 0x20
#define INTEG_MODE_SYN_MULTI 0x30

#define INTEG_PARAM_PULSE_COUNT1 0x00
#define INTEG_PARAM_PULSE_COUNT2 0x01
#define INTEG_PARAM_PULSE_COUNT4 0x02
#define INTEG_PARAM_PULSE_COUNT8 0x03
//Interrupt Control Register
#define INTR_STOP 40
#define INTR_DISABLE 0x00
#define INTR_LEVEL 0x10
#define INTR_PERSIST_EVERY 0x00
#define INTR_PERSIST_SINGLE 0x01
//Interrupt Souce Register
#define INT_SOURCE_GREEN 0x00
#define INT_SOURCE_RED 0x01
#define INT_SOURCE_BLUE 0x10
#define INT_SOURCE_CLEAR 0x03
//Gain Register
#define GAIN_1 0x00
#define GAIN_4 0x10
#define GAIN_16 0x20
#define GANI_64 0x30
#define PRESCALER_1 0x00
#define PRESCALER_2 0x01
#define PRESCALER_4 0x02
#define PRESCALER_8 0x03
#define PRESCALER_16 0x04
#define PRESCALER_32 0x05
#define PRESCALER_64 0x06

int readingdata[20];
int i,green,red,blue,clr,ctl;
double X,Y,Z,x,y,z;

int inSwitch = 2;

#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //DX Shield uses other pins

void setup() {
pinMode(inSwitch,INPUT);
digitalWrite(inSwitch,HIGH);
// set up the LCD’s number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print(“hello, world!”);
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600);
}

void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
lcd.setCursor(0, 0);
lcd.print(millis()/1000);

setTimingReg(INTEG_MODE_FREE);//Set trigger mode.Including free mode,manually mode,single synchronizition mode or so.
setInterruptSourceReg(INT_SOURCE_GREEN); //Set interrupt source
setInterruptControlReg(INTR_LEVEL|INTR_PERSIST_EVERY);//Set interrupt mode
setGain(GAIN_1|PRESCALER_4);//Set gain value and prescaler value
setEnableADC();//Start ADC of the color sensor
//while(1){

if (digitalRead(inSwitch) == HIGH) {
lcd.setCursor(0, 0);
lcd.print(” “);
lcd.setCursor(0, 0);
lcd.print(millis()/1000);

readRGB();
calculateCoordinate();

lcd.setCursor(0, 1);
lcd.print(” “);

if (x<0.25) {
//blue
lcd.setCursor(0, 1);
lcd.print(“blau”);
Serial.print(“b”);
}
if (x>0.25 && x<0.36) {
//blue
lcd.setCursor(0, 1);
lcd.print(“gruen”);
Serial.print(“g”);
}

if (x>=0.36 ) {
//blue
lcd.setCursor(0, 1);
lcd.print(“rot”);
Serial.print(“r”);
}

delay(1000);
clearInterrupt();
}
}

/************************************/
void setTimingReg(int x)
{
Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(REG_TIMING);
Wire.write(x);
Wire.endTransmission();
delay(100);
}
void setInterruptSourceReg(int x)
{
Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(REG_INT_SOURCE);
Wire.write(x);
Wire.endTransmission();
delay(100);
}
void setInterruptControlReg(int x)
{
Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(REG_INT);
Wire.write(x);
Wire.endTransmission();
delay(100);
}
void setGain(int x)
{
Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(REG_GAIN);
Wire.write(x);
Wire.endTransmission();
}
void setEnableADC()
{

Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(REG_CTL);
Wire.write(CTL_DAT_INIITIATE);
Wire.endTransmission();
delay(100);
}
void clearInterrupt()
{
Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(CLR_INT);
Wire.endTransmission();
}
void readRGB()
{
Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.write(REG_BLOCK_READ);
Wire.endTransmission();

Wire.beginTransmission(COLOR_SENSOR_ADDR);
Wire.requestFrom(COLOR_SENSOR_ADDR,8);
delay(500);
if(8<= Wire.available()) // if two bytes were received
{
for(i=0;i<8;i++)
{
readingdata[i]=Wire.read();
//Serial.println(readingdata[i],BIN);
}
}
green=readingdata[1]*256+readingdata[0];
red=readingdata[3]*256+readingdata[2];
blue=readingdata[5]*256+readingdata[4];
clr=readingdata[7]*256+readingdata[6];

}

void calculateCoordinate()
{
X=(-0.14282)*red+(1.54924)*green+(-0.95641)*blue;
Y=(-0.32466)*red+(1.57837)*green+(-0.73191)*blue;
Z=(-0.68202)*red+(0.77073)*green+(0.56332)*blue;
x=X/(X+Y+Z);
y=Y/(X+Y+Z);
if((X>0)&&(Y>0)&&(Z>0))
{
/*
Serial.println(“The x,y value is”);
Serial.print(“(“);
Serial.print(x,2);
Serial.print(” , “);
Serial.print(y,2);
Serial.println(“)”);
Serial.println(“Please reference the figure(Chromaticity Diagram) in the wiki “);
Serial.println(“so as to get the recommended color.”);
*/
lcd.setCursor(4, 0);
lcd.print(x);
lcd.setCursor(10, 0);
lcd.print(y);

}
else
{
//Serial.println(“Error,the value overflow”);
lcd.setCursor(4, 0);
lcd.print(“Error”);

}
}

 

Amy Xu’s Final Project Documentation

For my final project, I decided to improve on my paint application from my midterm project. There were some limitations to my original program, such as a small selection of color available and no way to erase. Additionally, my prior paint application’s interface was not very aesthetically pleasing, so I wanted to design a better looking user interface because research has shown that people are happier when using interfaces that are more pleasing to the eye. To solve these two problems, I created the present color wheel that is both very aesthetically pleasing and offers a wide selection of color. I also incorporated sliders which help the user choose the stroke weight and opacity with precision. Before I simply had blank buttons which were not helpful for allowing the user to know the current relative weight and opacity. The slider helps solve this interface defect.

Furthermore, I wanted to create a paint program that can interact and understand the user’s current mood. I realized that users mostly communicate with the paint tool using the mouse. Oftentimes if someone is mad, they would move the mouse fast and furiously. If they are sad, they might drag the mouse. Thus, I created two special interactive paintbrush effects. The first one allows the user to draw bubbles when they move the mouse –depending on how fast they the user moves the mouse, the color will change. The second brush follows a similar concept. Depending on how fast the user moves, the mouse the bubbles that come out of the brush will change in size.

Lastly, I added a save button that enables the user to save his or her artwork. In my previous application, I did not have such a function, so I felt as if my application was more of a fun thing to play around with rather than a serious tool used to create artwork.

Challenges faced: 1)   The first challenge I faced was writing the code which allowed the user to hover over the color wheel to choose a stroke color. I resolved this issue by using several variables. I first used the “get” function to track the color of the pixel the mouse is hovering over. I then stored the color of the paint in a circle next to the color wheel, and proceeded to get the color of a pixel in the circle to use as the brush color.

2)   Another challenge I faced is getting the sliders to work properly. When I pressed the mouse down to draw lines in the empty space, I also inadvertently moved the slider.

Thus Matt helped me write some code that put a bound on what constituted as moving as moving the slider. This only if the mouse is dragged in the area dictated below, will the stroke weight or opacity value change.

  • if (mouseX > sliderX && mouseX < sliderX+sliderWidth && mouseY > sliderY && mouseY < sliderY+sliderSize) {
  • sliderDragging = true;
  •         sliderValue = temp;
  •         rect(mouseX, sliderY,sliderSize, sliderSize);
  •           sliderMouse = mouseX;
  •   }Screen shot 2013-12-16 at 10.09.33 PM

BELOW IS AN INSTRUCTION VIDEO:

Possible additions:

If I had time to improve my project I definitely would have incorporated music into it. For instance, I might have made a function that allows one to draw with their voice (depending on different pitches, volume, etc. ) different images will appear.

Additionally, the show on Thursday was a good way for me to get user feedback. After the show I created a function that allows the user to clear the screen with the space bar instead of having to re-run the program.

BELOW IS MY CODE

  • //this program seeks to create a one of a kind paint tool! 🙂
  • float speed;
  • float radius;
  • float color1;
  • float color2;
  • float color3;
  • float position;
  • float opacity;
  • float opacity2;
  • boolean bubble = false;
  • boolean point = false;
  • int clickCounter = 0;
  • int pointillize = 3;
  • Slider slider = new Slider(30, 350, 120, 10);
  • Slideropacity slider2 = new Slideropacity(30, 450, 120, 10);
  • boolean mouseDown = false;
  • PImage paintinterface;
  • color c;
  • color b = 255;
  • color e;
  • PImage savebutton;
  • PImage img2;
  • void setup() {
  • background(255);
  • paintinterface = loadImage(“toolbar.png”);
  • savebutton = loadImage(“save.png”);
  • size(1200, 800);
  • }
  • void draw() {
  • radius = random(50);
  • position = random (20);
  • color1 = random (256);
  • color2= random (100);
  • color3 = random (100);
  • opacity = random (256);
  • radius = radius + 10;
  • image(paintinterface, 0, 0, 240, 790);
  • image(savebutton, 1150,10,50,50);
  • color c = paintinterface.get(mouseX, mouseY);
  • if ((mouseX>0)&&(mouseY>50)&&(mouseX<230)&&(mouseY<300)) {
  • fill(c);
  • shapeMode(CENTER);
  • ellipse(mouseX, mouseY, 30, 30);
  • }
  • stroke(0);
  • strokeWeight(2);
  • fill(b);
  • ellipse(200, 40, 40, 40);
  • fill(255);
  • rect(50, 550, 10, 10);
  • rect(70, 550, 20, 20);
  • rect(100, 550, 30, 30);
  • line(240, 0, 240, 800);
  • rect(50, 650, 100, 20);
  • rect(50, 700, 100, 20);
  • slider.sliderSet();
  • slider2.sliderSet();
  • img2 = get(240, 0, 1200, 800);
  • int x = int(random(img2.width));
  • int y = int(random(img2.height));
  • int loc = x + y*img2.width;
  • loadPixels();
  • float r = red(img2.pixels[loc]);
  • float g = green(img2.pixels[loc]);
  • float b = blue(img2.pixels[loc]);
  • noStroke();
  • if (point == true) {
  • fill(r, g, b, 100);
  • ellipse(x, y, pointillize, pointillize);
  • }
  • else {
  • point = false;
  • }
  • strokeWeight(2);
  • if (mousePressed) {
  • stroke(e);
  • if (slider.sliderValue > 0) {
  • strokeWeight(slider.sliderValue);
  • }
  • if (slider2.sliderValue >0) {
  • stroke(e, slider2.sliderValue);
  • }
  • line(pmouseX, pmouseY, mouseX, mouseY);
  • }
  • }
  • void mousePressed() {
  • if ((mouseX>50)&&(mouseY>550)&&(mouseX<60)&&(mouseY<560)) {
  • e = 255;
  • slider.sliderValue = 2;
  • }
  • if ((mouseX>70)&&(mouseY>550)&&(mouseX<90)&&(mouseY<570)) {
  • e = 255;
  • slider.sliderValue = 10;
  • }
  • if ((mouseX>100)&&(mouseY>550)&&(mouseX<130)&&(mouseX<580)) {
  • e = 255;
  • slider.sliderValue = 30;
  • }
  • if ((mouseX>1150)&&(mouseY>10)&&(mouseX<1200)&&(mouseY<60)) {
  • PImage img = get(240, 0, 940, 800);
  • img.save(“image”+clickCounter+”.jpg”);
  • }
  • if ((mouseX>0)&&(mouseY>50)&&(mouseX<230)&&(mouseY<300)) {
  • c = paintinterface.get(mouseX, mouseY);
  • b = c;
  • e = c;
  • bubble = false;
  • }
  • if ((mouseX>50)&&(mouseY>650)&&(mouseX<150)&&(mouseY<670)) {
  • bubble = true;
  • }
  • else {
  • bubble = false;
  • }
  • if ((mouseX>50)&&(mouseY>700)&&(mouseX<150)&&(mouseY<720)) {
  • point = true;
  • }
  • else {
  • point = false;
  • }
  • }
  • void mouseMoved() {
  • if (bubble == true) {
  • noStroke();
  • float speed = abs(mouseX-pmouseX)+abs(mouseY-pmouseY);
  • if (speed <= 10) {
  • fill(color3, 255, color2, opacity);
  • }
  • if ( (10< speed) && (speed <= 60)) {
  • fill (color2, color3, 255, opacity);
  • }
  • if (speed > 60) {
  • fill (255, color2, color3, opacity);
  • }
  • ellipse(mouseX+position, mouseY+position, radius, radius);
  • }
  • if (point == true) {
  • float speed = abs(mouseX-pmouseX)+abs(mouseY-pmouseY);
  • if (speed <= 10) {
  • pointillize = 5;
  • }
  • if ( (10< speed) && (speed <= 60)) {
  • pointillize = 20;
  • }
  • if (speed > 60) {
  • pointillize = 40;
  • }
  • }
  • }
  • void mouseReleased() {
  • mouseDown = false;
  • slider.sliderDragging = false;
  • slider2.sliderDragging = false;
  • }
  • void mouseDragged() {
  • slider.sliderDrag();
  • slider2.sliderDrag();
  • }
  • void keyPressed() {
  • println(“keyPressed = “+keyCode);
  • if (keyCode == 32) {
  • fill(255);
  • rect(0, 0, 1200, 800);
  • }
  • }
  • —————————————————————————

FOR STROKE WEIGHT

class Slider {

  • float sliderX, sliderY, sliderWidth, sliderSize, sliderValue, sliderMouse;
  • boolean sliderDragging = false;
  • float temp;
  • Slider(int x, int y, int w, int s) {
  • sliderX = x;
  • sliderY = y;
  • sliderWidth = w;
  • sliderSize = s;
  • sliderValue = 0;
  • println(“Slider”);
  • }
  • void sliderDrag() {
  • temp = map(mouseX,sliderX, sliderX+(sliderWidth-sliderSize),1,40);
  • if (mouseX > sliderX && mouseX < sliderX+sliderWidth && mouseY > sliderY && mouseY < sliderY+sliderSize) {
  • sliderDragging = true;
  • sliderValue = temp;
  • rect(mouseX, sliderY,sliderSize, sliderSize);
  • sliderMouse = mouseX;
  • }
  • }
  • void sliderSet() {
  • if (sliderDragging) {
  • sliderDraw(mouseX);
  • }else{
  • sliderDraw(sliderMouse);
  • }
  • }
  • void sliderDraw(float x) {
  • stroke(0);
  • fill(0);
  • line(sliderX, sliderY+(sliderSize/2), sliderX+sliderWidth, sliderY+(sliderSize/2));
  • if (x > sliderX && x < sliderX+(sliderWidth-sliderSize)) {
  • rect(x, sliderY,sliderSize, sliderSize);}
  • //} else */
  • if (x<= sliderX) {
  • rect(sliderX, sliderY, sliderSize, sliderSize);
  • } else if (x>= sliderX+(sliderWidth-sliderSize)) {
  • rect(sliderX+(sliderWidth-sliderSize), sliderY, sliderSize, sliderSize);
  • }
  • }
  • }
  • ——————————————————————————————–
  • class Slideropacity {
  • float sliderX, sliderY, sliderWidth, sliderSize, sliderValue, sliderMouse;
  • boolean sliderDragging = false;
  • float temp;
  • Slideropacity(int x, int y, int w, int s) {
  • sliderX = x;
  • sliderY = y;
  • sliderWidth = w;
  • sliderSize = s;
  • sliderValue = 0;
  • println(“Slider”);
  • }
  • void sliderDrag() {
  • temp = map(mouseX,sliderX, sliderX+(sliderWidth-sliderSize),1,255);
  • if (mouseX > sliderX && mouseX < sliderX+sliderWidth && mouseY > sliderY && mouseY < sliderY+sliderSize) {
  • sliderDragging = true;
  • sliderValue = temp;
  • rect(mouseX, sliderY,sliderSize, sliderSize);
  • sliderMouse = mouseX;
  • }
  • }
  • void sliderSet() {
  • if (sliderDragging) {
  • sliderDraw(mouseX);
  • }else{
  • sliderDraw(sliderMouse);
  • }
  • }
  • void sliderDraw(float x) {
  • stroke(0);
  • fill(0);
  • line(sliderX, sliderY+(sliderSize/2), sliderX+sliderWidth, sliderY+(sliderSize/2));
  • if (x > sliderX && x < sliderX+(sliderWidth-sliderSize)) {
  • rect(x, sliderY,sliderSize, sliderSize);}
  • //} else */
  • if (x<= sliderX) {
  • rect(sliderX, sliderY, sliderSize, sliderSize);
  • } else if (x>= sliderX+(sliderWidth-sliderSize)) {
  • rect(sliderX+(sliderWidth-sliderSize), sliderY, sliderSize, sliderSize);
  • }
  • }
  • }
  • —————————————————————————————————

FOR STROKE OPACITY:

  • class Slideropacity {
  • float sliderX, sliderY, sliderWidth, sliderSize, sliderValue, sliderMouse;
  • boolean sliderDragging = false;
  • float temp;
  • Slideropacity(int x, int y, int w, int s) {
  • sliderX = x;
  • sliderY = y;
  • sliderWidth = w;
  • sliderSize = s;
  • sliderValue = 0;
  • println(“Slider”);
  • }
  • void sliderDrag() {
  • temp = map(mouseX,sliderX, sliderX+(sliderWidth-sliderSize),1,255);
  • if (mouseX > sliderX && mouseX < sliderX+sliderWidth && mouseY > sliderY && mouseY < sliderY+sliderSize) {
  • sliderDragging = true;
  • sliderValue = temp;
  • rect(mouseX, sliderY,sliderSize, sliderSize);
  • sliderMouse = mouseX;
  • }
  • }
  • void sliderSet() {
  • if (sliderDragging) {
  • sliderDraw(mouseX);
  • }else{
  • sliderDraw(sliderMouse);
  • }
  • }
  • void sliderDraw(float x) {
  • stroke(0);
  • fill(0);
  • line(sliderX, sliderY+(sliderSize/2), sliderX+sliderWidth, sliderY+(sliderSize/2));
  • if (x > sliderX && x < sliderX+(sliderWidth-sliderSize)) {
  • rect(x, sliderY,sliderSize, sliderSize);}
  • //} else */
  • if (x<= sliderX) {
  • rect(sliderX, sliderY, sliderSize, sliderSize);
  • } else if (x>= sliderX+(sliderWidth-sliderSize)) {
  • rect(sliderX+(sliderWidth-sliderSize), sliderY, sliderSize, sliderSize);
  • }
  • }
  • }

Alicja’s Final

indonesia-808

My initial idea for the final was to work with the webcam.  However, as I tried to go through with the project, I encountered a lot of technical  difficulties, and I decided that I wanted to change the project, so that I can continue exploring Processing.

I decided it would be fun to work with images and create something that is not limited to the files that are on my computer, but use the internet instead. When I started out, I did not have a very clear idea of what I wanted to make. I was thinking of creating a new kind of dictionary – one, that defines words by images, and not by word definitions. Another idea was to create a card-maker, that would download images, write messages on them and export them.

The end result was a search engine, similar to Google Images, but slightly different, in that it randomly chooses the image it shows out of the results of the search.

Those are sample images generated by the program for the search of the word “food”:

food-337 food-479

The program, even though it seems fairly simple, was not that easy to create. I wanted to use Google Images library at first, but then I decided Flickr would work better, as there are less differences in the size and the quality of the pictures on Flickr than on Google Images. I found a code online that allows for the search of images, and then modified it to search the term that was typed in by the user:

query = api + “tag=”+searchTerm+”&cluster_id=1&format=rest&extras=url_o&api_key=bd7435660785b923332b224789d33c93”;

That, along with allowing the user to type in the word he/she wants to search, was one of the most important parts of the project. I found a code for typing online as well, and then modified it a bit to fit my purpose.

That was the basis. The next step was making the program look better and be more user-friendly. That is why I created an interface:

apple-25738

I also designed a screen, that appeared when there were no search results for the query on Flickr:

enkoko-8687

To make the images themselves look better, I found a way to analyze the dominant color in the picture and then used the inverse of this color to be the color of the font used to write out the word searched for over the image. The result of the addition of that code to the program can be clearly observed looking at these images:

sunset-12723

mariposa-48394

When it came to the font, I could not decide which one to use. I also did not want all of the pictures to look the same, so I added a code that randomizes the font used. I incorporated 15 different fonts in the program, those are examples of some of them:

poland-339   poland-399

poland-350

poland-398

I was really happy when I finally got everything to work together. However, during the Final Show it became clear that the program has its weaknesses. Firstly, almost all of the users wanted to click the white slot meant for the typing before starting to actually type. That is because they subconsciously expected a cursor to be displayed, which I had not created. Secondly, the program does not have one clear function.

Other changes that could be made to the application would then depend on the purpose of the program. If it was to become a dictionary, perhaps it would be useful to enable the user to see more than one image. If it was to become a search engine, that, for example, searches private photo galleries, then the word written over the image would need to be removed. If it was to be a card-maker, perhaps the text on the image should be different from the text searched… There are many ways in which this application, with simple modifications, could be used, but so far, that’s the code of the program:

String api = “http://api.flickr.com/services/rest/?method=flickr.tags.getClusterPhotos&”;
PImage[] images;
int imageIndex;
XML xml;
float n1=0;
int n=0;
PFont font;
PFont[] fonts = new PFont[15];
float f1;
int f;
int d=0;
String typing = “”;
String searchTerm = “”;
String currentTerm = “”;
String query=””;
XML[] photoTag;
int m=0;
boolean restart=true;
color myColor;

void setup() {
noCursor();
frame.setResizable(true);
//^this allows the size of the window to be dependant on the size of the image
noStroke();
fonts();
}
void keyPressed() {
if (key == ‘\n’ ) {
searchTerm = typing;
//int l2 = searchTerm.length();
//println(l2);
// println(searchTerm);
typing = “”;
restart=false;
}
else if (keyCode == BACKSPACE) {
typing = typing.substring(0, typing.length() – 1);
}
else if (keyCode == TAB) {
restart=true;
}
else if (key != CODED) {
typing += key;
//int l1 = typing.length();
//println(l1);
//print(typing);
}
}
void draw() {
if (restart) {
frame.setSize(500, 500);
rectangles();
textAlign(CENTER, CENTER);
text(“What would you like to search for today?”, width/2, height/2-30);
input();
d=0;
randomizeFont();
}else if (searchTerm.equals(“”)==false) {
//println(“image_loading”);
query = api + “tag=”+searchTerm+”&cluster_id=1&format=rest&extras=url_o&api_key=bd7435660785b923332b224789d33c93”;
xml = loadXML(query);
photoTag = xml.getChildren(“photos/photo”);
//println(photoTag.length + ” images”);
images = new PImage[photoTag.length];
n1 =random(0, photoTag.length);
//println(“n1=”+n1);
n=int(n1);
// println(“n=”+n);
int h=0;
for (int i = 0; i < photoTag.length; i++) {
String farm = photoTag[i].getString(“farm”);
String server = photoTag[i].getString(“server”);
String id = photoTag[i].getString(“id”);
String secret = photoTag[i].getString(“secret”);
String img = “http://farm”+farm+”.static.flickr.com/”+server+”/”+id+”_”+secret+”.jpg”;
images[i] = requestImage(img);
}
currentTerm=searchTerm;
searchTerm=””;
} else if (query.equals(“”)==false) {
println(images.length);
/*for (int i = 0; i < images.length; i++) {
println(images[i]);
}*/
if (photoTag.length == 0) {
rectangles();
fill(250, 182, 10);
rect(50, height/2-115, 400, 50, 3, 3, 12, 12);
textFont(fonts[1], 18);
fill(255);
textAlign(CENTER, CENTER);
text(“Sorry, no images available…”, width/2, height/2-90);
text(“What would you like to search for instead?”, width/2, height/2-30);
input();
}
else {
image(images[n], 0, 0);
frame.setSize(images[n].width, images[n].height);
color_analysis();
fill(myColor);
textFont(fonts[f]);
textAlign(CENTER, CENTER);
text(currentTerm, width/2, height/2);
}
}
}

void mousePressed() {
saveFrame(“”+currentTerm+”-##.png”);
}

void color_analysis() {
int index = 1;
int rtot = 0;
int gtot = 0;
int btot = 0;
for (int a = 0; a < images[n].height; a++) {
for (int c = 0; c < images[n].width; c++) {
int pixelColor=images[n].pixels[c+images[n].width*images[n].height/2];
int r=(pixelColor >> 16) & 0xff;
int g = (pixelColor >> 8) & 0xff;
int b = pixelColor & 0xff;
rtot = rtot + r;
gtot = gtot + g;
btot = btot + b;
index++;
}
}
//println(“rtot=”+rtot);
//println(“gtot=”+gtot);
//println(“btot=”+btot);
rtot = rtot/index;
gtot = gtot/index;
btot = btot/index;
myColor = color(255-rtot, 255-gtot, 255-btot);
}

void fonts(){
fonts[0]=createFont(“LetterGothicStd-BoldSlanted”, 80);
fonts[1]=createFont(“MyanmarMN-Bold”,100);
fonts[2]=createFont(“SinhalaMN-Bold”,100);
fonts[3]=createFont(“AmericanTypewriter-CondensedBold”, 90);
fonts[4]=createFont(“SourceSansPro-SemiboldIt”,90);
fonts[5]=createFont(“ArialRoundedMTBold”,80);
fonts[6]=createFont(“BirchStd”, 130);
fonts[7]=createFont(“BrushScriptStd”,105);
fonts[8]=createFont(“Chalkduster”,70);
fonts[9]=createFont(“Silom”, 80);
fonts[10]=createFont(“NanumPen”,120);
fonts[11]=createFont(“OCRAStd”,50);
fonts[12]=createFont(“HoboStd”, 70);
fonts[13]=createFont(“Kefa-Bold”,90);
fonts[14]=createFont(“MarkerFelt-Wide”,100);

}

void randomizeFont(){
if (d==0){
f1 =random(0,14);
//println(“f1=”+f1);
f=int(f1);
//println(“f=”+f);
d++;
}
}

void input(){
fill(211,28,77);
textAlign(CENTER, CENTER);
text(typing, width/2, height/2+30);
text(searchTerm, width/2, height/2+30);
}

void rectangles() {
background(237, 118, 74);
fill(211,28,77);
rect(50, height/2-55, 400, 50, 3, 3, 12, 12);
fill(255);
rect(50, height/2+5, 400, 50, 3, 3, 12, 12);
textFont(fonts[1], 18);
}

Xiaoyue’s Awesome Final Final Project DanceJ

屏幕快照 2013-12-13 上午9.22.10屏幕快照 2013-12-13 上午9.22.35

My DanceJ is built on my mid-term project Rhythmania.

It has the features Rhythmania had: DanceJ tells the computer to play songs of the right rhythm according to the beat the user stomps on the pedal (and the pedal is a high class fancy laser-cut one now!) But the codes for accomplishing this feature is quite different now.

DanceJ hasmore features: DanceJ can play sound effects based on the intensity of the user’s dance moves. This feature is realized by using web-cam. There is a window on the screen that displays a cool dark version of the video showing the difference between different moments of the user’s gesture.

DanceJ is much more organic than Rhythmania. You can add new songs to your playlist by just dragging your mp3 files onto the display screen. Then the computer starts playing this new song you added. Now you can listen to this song and tap on keys to define the rhythm of the song. There are texts on the screen saying “define ur music by 10 taps”. When you are done, press ENTER then the information about this song’s rhythm and name will be saved into a CSV file. So a new song is added to the user’s playlist and is ready to be called upon by stepping on the pedal at its defined rhythm.

The hardest part in this project was for me to integrate the different parts of DanceJ together. Each different function was completed separately. When the individual programs are put together, the whole thing becomes a big big mess. At that moment it was so important to believe that it was all going to work out. After this semester’s experience with coding, I have found one very important psychology a coder must have is to believe there is always a solution to all the problems and errors. Only when he or she have that faith can he or she struggle through the difficulties.

Video !

DanceJ

 

 

 

Amy Xu’s Lab 8

For lab 8, I decided to create my own screen based interface. I inspired by the color slider we created in class so I decided to create my own volume slider using the sonia library.

Below is an image of the user interface. I decided to keep it simple and intuitive. If the user slides to the right, the volume will became louder.

volumeadj

 

Below is my code. I had to create a new class for the slider.

MAIN CODE: 

  • import pitaru.sonia_v2_9.*;
  • Slider slider = new Slider(100,300,600,20);
  • Sample player;
  • PImage volumebutton;
  • void setup() {
  • volumebutton = loadImage(“volume.jpg”);
  • size(800,400);
  • background(0);
  • Sonia.start(this);
  • player = new Sample(“Caramel2.wav”);
  • player.setVolume(slider.sliderVolume);
  • }
  • void draw() {
  • background(0);
  • noStroke();
  • image(volumebutton,320,50,200,200);
  • //rect(100,100,600,100);
  • slider.sliderSet();
  • }
  • void mouseDragged() {
  • slider.sliderDrag();
  • }
  • void mouseReleased() {
  • slider.sliderDragging = false;
  • }
  • public void stop() {
  • Sonia.stop();
  • super.stop();
  • }

CODE FOR class Slider:

I did not change much in the slider class except this line:

float temp = map(mouseX,sliderX, sliderX+(sliderWidth-sliderSize),0,2.0);

I made the range 0-2.0 instead of 0-255, as it would be for color, because volume is on a different scale.

  • class Slider {
  • float sliderX, sliderY, sliderWidth, sliderSize, sliderVolume, sliderMouse;
  • boolean sliderDragging = false;
  • Slider(int x, int y, int w, int s) {
  • sliderX = x;
  • sliderY = y;
  • sliderWidth = w;
  • sliderSize = s;
  • sliderVolume = 1;
  • println(“Slider”);
  • }
  • void sliderDrag() {
  • sliderDragging = true;
  • float temp = map(mouseX,sliderX, sliderX+(sliderWidth-sliderSize),0,2.0);
  • sliderVolume = temp;
  • sliderMouse = mouseX;
  • println(temp);
  • }
  • void sliderSet() {
  • if (sliderDragging) {
  • sliderDraw(mouseX);
  • }else{
  • sliderDraw(sliderMouse);
  • }
  • }
  • void sliderDraw(float x) {
  • stroke(255);
  • fill(255);
  • line(sliderX, sliderY+(sliderSize/2), sliderX+sliderWidth, sliderY+(sliderSize/2));
  • if (x > sliderX && x < sliderX+(sliderWidth-sliderSize)) {
  • rect(x, sliderY,sliderSize, sliderSize);
  • } else if (x<= sliderX) {
  • rect(sliderX, sliderY, sliderSize, sliderSize);
  • } else if (x>= sliderX+(sliderWidth-sliderSize)) {
  • rect(sliderX+(sliderWidth-sliderSize), sliderY, sliderSize, sliderSize);
  • }
  • }
  • }

Amy Xu’s Lab 1

For the first lab we had to build three circuit boards. The first circuit uses a buzzer and a switch. The second circuit uses a resistor, LED and a switch. The third circuit uses a potentiometer, LED and switch.

I followed the schematics below to construct the three circuits. I did not run into any trouble, except for the third circuit board I lacked a potentiometer at the time.

week1Schematics

 

CIRCUIT 1:

lab1.3.1 lab1.2.2

CIRCUIT 2:lab1.2.1 lab1.1.1

 

CIRCUIT 3:

 

lab1.1.2

 

Fred Wu’s Midterm Reloaded

 

Hello, my name is Fred Wu and today I want to introduce my mid-term project: virtual color mixer. Basically, it needs three potentiometers to control RGB colors, and what I’ve done is to program Arduino so that it can output  the value according to the resistance. By using the serial fuction in Processing, we can vividly demonstrate how the values change.IMG_0508IMG_0509

const int redPin = A0;      // sensor to control red color

const int greenPin = A1;    // sensor to control green color

const int bluePin = A2;     // sensor to control blue color

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

void loop() {    Serial.print(analogRead(redPin));

Serial.print(“,”);

Serial.print(analogRead(greenPin));

Serial.print(“,”);

Serial.println(analogRead(bluePin));

}

Processing code

//  This example code is in the public domain.

import processing.serial.*;

float redValue = 0;        // red value

float greenValue = 0;      // green value

float blueValue = 0;       // blue value

Serial myPort;

void setup() {

size(200, 200);

// List all the available serial ports

println(Serial.list());

// I know that the first port in the serial list on my mac

// is always my  Arduino, so I open Serial.list()[0].

// Open whatever port is the one you’re using.

myPort = new Serial(this, Serial.list()[0], 9600);

// don’t generate a serialEvent() unless you get a newline character:

myPort.bufferUntil(‘\n’);

}

void draw() {

// set the background color with the color values:

background(redValue, greenValue, blueValue);

}

void serialEvent(Serial myPort) {

// get the ASCII string:

String inString = myPort.readStringUntil(‘\n’);

if (inString != null) {

// trim off any whitespace:

inString = trim(inString);

// split the string on the commas and convert the

// resulting substrings into an integer array:

float[] colors = float(split(inString, “,”));

// if the array has at least three elements, you know

// you got the whole thing.  Put the numbers in the

// color variables:

if (colors.length >=3) {

// map them to the range 0-255:

redValue = map(colors[0],978, 1023, 0, 255);

greenValue = map(colors[1], 978, 1023, 0, 255);

blueValue = map(colors[2],978, 1023, 0, 255);

}

}

}

*/

 

 

Fred Wu’s Final Project: One-Man Band

This project is roughly a sound remixer. It is based on Arduino and Processing. By pressing the buttons (three) on the interface board, the operator can remix any sound effect or music he want. In my final project proposal, I describe my project as a band. However, the result may seem more like a music remixer than a band. Anyway, if you change the tracks, it can still sound like a band.

By the way, this is my very first time to design something with our super cool laser cutter. I enjoyed this progress so much. Thank you Vivian Xu for your generous help and always, I want to express my gratitude to our beloved professors, Prof. Matthew Belanger and Prof. Marianne Petit. Thank you all for your support!

Codes:

/*  * Firmata is a generic protocol for communicating with microcontrollers  * from software on a host computer. It is intended to work with  * any host computer software package.  *  * To download a host software package, please clink on the following link  * to open the download page in your default browser.  *  * http://firmata.org/wiki/Download  */

/*   Copyright (C) 2006-2008 Hans-Christoph Steiner.  All rights reserved.   Copyright (C) 2010-2011 Paul Stoffregen.  All rights reserved.   Copyright (C) 2009 Shigeru Kobayashi.  All rights reserved.   Copyright (C) 2009-2011 Jeff Hoefs.  All rights reserved.     This library is free software; you can redistribute it and/or   modify it under the terms of the GNU Lesser General Public   License as published by the Free Software Foundation; either   version 2.1 of the License, or (at your option) any later version.     See file LICENSE.txt for further informations on licensing terms.

formatted using the GNU C formatting and indenting */

/*  * TODO: use Program Control to load stored profiles from EEPROM  */

#include <Servo.h> #include <Wire.h> #include <Firmata.h>

// move the following defines to Firmata.h? #define I2C_WRITE B00000000 #define I2C_READ B00001000 #define I2C_READ_CONTINUOUSLY B00010000 #define I2C_STOP_READING B00011000 #define I2C_READ_WRITE_MODE_MASK B00011000 #define I2C_10BIT_ADDRESS_MODE_MASK B00100000

#define MAX_QUERIES 8 #define MINIMUM_SAMPLING_INTERVAL 10

#define REGISTER_NOT_SPECIFIED -1

/*==============================================================================  * GLOBAL VARIABLES  *============================================================================*/

/* analog inputs */ int analogInputsToReport = 0; // bitwise array to store pin reporting

/* digital input ports */ byte reportPINs[TOTAL_PORTS];       // 1 = report this port, 0 = silence byte previousPINs[TOTAL_PORTS];     // previous 8 bits sent

/* pins configuration */ byte pinConfig[TOTAL_PINS];         // configuration of every pin byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else int pinState[TOTAL_PINS];           // any value that has been written

/* timer variables */ unsigned long currentMillis;        // store the current value from millis() unsigned long previousMillis;       // for comparison with currentMillis int samplingInterval = 19;          // how often to run the main loop (in ms)

/* i2c data */ struct i2c_device_info {   byte addr;   byte reg;   byte bytes; };

/* for i2c read continuous more */ i2c_device_info query[MAX_QUERIES];

byte i2cRxData[32]; boolean isI2CEnabled = false; signed char queryIndex = -1; unsigned int i2cReadDelayTime = 0;  // default delay time between i2c read request and Wire.requestFrom()

Servo servos[MAX_SERVOS]; /*==============================================================================  * FUNCTIONS  *============================================================================*/

void readAndReportData(byte address, int theRegister, byte numBytes) {   // allow I2C requests that don’t require a register read   // for example, some devices using an interrupt pin to signify new data available   // do not always require the register read so upon interrupt you call Wire.requestFrom()    if (theRegister != REGISTER_NOT_SPECIFIED) {     Wire.beginTransmission(address);     #if ARDUINO >= 100     Wire.write((byte)theRegister);     #else     Wire.send((byte)theRegister);     #endif     Wire.endTransmission();     delayMicroseconds(i2cReadDelayTime);  // delay is necessary for some devices such as WiiNunchuck   } else {     theRegister = 0;  // fill the register with a dummy value   }

Wire.requestFrom(address, numBytes);  // all bytes are returned in requestFrom

// check to be sure correct number of bytes were returned by slave   if(numBytes == Wire.available()) {     i2cRxData[0] = address;     i2cRxData[1] = theRegister;     for (int i = 0; i < numBytes; i++) {       #if ARDUINO >= 100       i2cRxData[2 + i] = Wire.read();       #else       i2cRxData[2 + i] = Wire.receive();       #endif     }   }   else {     if(numBytes > Wire.available()) {       Firmata.sendString(“I2C Read Error: Too many bytes received”);     } else {       Firmata.sendString(“I2C Read Error: Too few bytes received”);     }   }

// send slave address, register and received bytes   Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); }

void outputPort(byte portNumber, byte portValue, byte forceSend) {   // pins not configured as INPUT are cleared to zeros   portValue = portValue & portConfigInputs[portNumber];   // only send if the value is different than previously sent   if(forceSend || previousPINs[portNumber] != portValue) {     Firmata.sendDigitalPort(portNumber, portValue);     previousPINs[portNumber] = portValue;   } }

/* —————————————————————————–  * check all the active digital inputs for change of state, then add any events  * to the Serial output queue using Serial.print() */ void checkDigitalInputs(void) {   /* Using non-looping code allows constants to be given to readPort().    * The compiler will apply substantial optimizations if the inputs    * to readPort() are compile-time constants. */   if (TOTAL_PORTS > 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);   if (TOTAL_PORTS > 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);   if (TOTAL_PORTS > 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);   if (TOTAL_PORTS > 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);   if (TOTAL_PORTS > 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);   if (TOTAL_PORTS > 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);   if (TOTAL_PORTS > 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);   if (TOTAL_PORTS > 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);   if (TOTAL_PORTS > 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);   if (TOTAL_PORTS > 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);   if (TOTAL_PORTS > 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);   if (TOTAL_PORTS > 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);   if (TOTAL_PORTS > 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);   if (TOTAL_PORTS > 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);   if (TOTAL_PORTS > 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);   if (TOTAL_PORTS > 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false); }

// —————————————————————————– /* sets the pin mode to the correct state and sets the relevant bits in the  * two bit-arrays that track Digital I/O and PWM status  */ void setPinModeCallback(byte pin, int mode) {   if (pinConfig[pin] == I2C && isI2CEnabled && mode != I2C) {     // disable i2c so pins can be used for other functions     // the following if statements should reconfigure the pins properly     disableI2CPins();   }   if (IS_PIN_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) {     servos[PIN_TO_SERVO(pin)].detach();   }   if (IS_PIN_ANALOG(pin)) {     reportAnalogCallback(PIN_TO_ANALOG(pin), mode == ANALOG ? 1 : 0); // turn on/off reporting   }   if (IS_PIN_DIGITAL(pin)) {     if (mode == INPUT) {       portConfigInputs[pin/8] |= (1 << (pin & 7));     } else {       portConfigInputs[pin/8] &= ~(1 << (pin & 7));     }   }   pinState[pin] = 0;   switch(mode) {   case ANALOG:     if (IS_PIN_ANALOG(pin)) {       if (IS_PIN_DIGITAL(pin)) {         pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver         digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups       }       pinConfig[pin] = ANALOG;     }     break;   case INPUT:     if (IS_PIN_DIGITAL(pin)) {       pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver       digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups       pinConfig[pin] = INPUT;     }     break;   case OUTPUT:     if (IS_PIN_DIGITAL(pin)) {       digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable PWM       pinMode(PIN_TO_DIGITAL(pin), OUTPUT);       pinConfig[pin] = OUTPUT;     }     break;   case PWM:     if (IS_PIN_PWM(pin)) {       pinMode(PIN_TO_PWM(pin), OUTPUT);       analogWrite(PIN_TO_PWM(pin), 0);       pinConfig[pin] = PWM;     }     break;   case SERVO:     if (IS_PIN_SERVO(pin)) {       pinConfig[pin] = SERVO;       if (!servos[PIN_TO_SERVO(pin)].attached()) {           servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));       }     }     break;   case I2C:     if (IS_PIN_I2C(pin)) {       // mark the pin as i2c       // the user must call I2C_CONFIG to enable I2C for a device       pinConfig[pin] = I2C;     }     break;   default:     Firmata.sendString(“Unknown pin mode”); // TODO: put error msgs in EEPROM   }   // TODO: save status to EEPROM here, if changed }

void analogWriteCallback(byte pin, int value) {   if (pin < TOTAL_PINS) {     switch(pinConfig[pin]) {     case SERVO:       if (IS_PIN_SERVO(pin))         servos[PIN_TO_SERVO(pin)].write(value);         pinState[pin] = value;       break;     case PWM:       if (IS_PIN_PWM(pin))         analogWrite(PIN_TO_PWM(pin), value);         pinState[pin] = value;       break;     }   } }

void digitalWriteCallback(byte port, int value) {   byte pin, lastPin, mask=1, pinWriteMask=0;

if (port < TOTAL_PORTS) {     // create a mask of the pins on this port that are writable.     lastPin = port*8+8;     if (lastPin > TOTAL_PINS) lastPin = TOTAL_PINS;     for (pin=port*8; pin < lastPin; pin++) {       // do not disturb non-digital pins (eg, Rx & Tx)       if (IS_PIN_DIGITAL(pin)) {         // only write to OUTPUT and INPUT (enables pullup)         // do not touch pins in PWM, ANALOG, SERVO or other modes         if (pinConfig[pin] == OUTPUT || pinConfig[pin] == INPUT) {           pinWriteMask |= mask;           pinState[pin] = ((byte)value & mask) ? 1 : 0;         }       }       mask = mask << 1;     }     writePort(port, (byte)value, pinWriteMask);   } }

// —————————————————————————– /* sets bits in a bit array (int) to toggle the reporting of the analogIns  */ //void FirmataClass::setAnalogPinReporting(byte pin, byte state) { //} void reportAnalogCallback(byte analogPin, int value) {   if (analogPin < TOTAL_ANALOG_PINS) {     if(value == 0) {       analogInputsToReport = analogInputsToReport &~ (1 << analogPin);     } else {       analogInputsToReport = analogInputsToReport | (1 << analogPin);     }   }   // TODO: save status to EEPROM here, if changed }

void reportDigitalCallback(byte port, int value) {   if (port < TOTAL_PORTS) {     reportPINs[port] = (byte)value;   }   // do not disable analog reporting on these 8 pins, to allow some   // pins used for digital, others analog.  Instead, allow both types   // of reporting to be enabled, but check if the pin is configured   // as analog when sampling the analog inputs.  Likewise, while   // scanning digital pins, portConfigInputs will mask off values from any   // pins configured as analog }

/*==============================================================================  * SYSEX-BASED commands  *============================================================================*/

void sysexCallback(byte command, byte argc, byte *argv) {   byte mode;   byte slaveAddress;   byte slaveRegister;   byte data;   unsigned int delayTime;     switch(command) {   case I2C_REQUEST:     mode = argv[1] & I2C_READ_WRITE_MODE_MASK;     if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {       Firmata.sendString(“10-bit addressing mode is not yet supported”);       return;     }     else {       slaveAddress = argv[0];     }

switch(mode) {     case I2C_WRITE:       Wire.beginTransmission(slaveAddress);       for (byte i = 2; i < argc; i += 2) {         data = argv[i] + (argv[i + 1] << 7);         #if ARDUINO >= 100         Wire.write(data);         #else         Wire.send(data);         #endif       }       Wire.endTransmission();       delayMicroseconds(70);       break;     case I2C_READ:       if (argc == 6) {         // a slave register is specified         slaveRegister = argv[2] + (argv[3] << 7);         data = argv[4] + (argv[5] << 7);  // bytes to read         readAndReportData(slaveAddress, (int)slaveRegister, data);       }       else {         // a slave register is NOT specified         data = argv[2] + (argv[3] << 7);  // bytes to read         readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data);       }       break;     case I2C_READ_CONTINUOUSLY:       if ((queryIndex + 1) >= MAX_QUERIES) {         // too many queries, just ignore         Firmata.sendString(“too many queries”);         break;       }       queryIndex++;       query[queryIndex].addr = slaveAddress;       query[queryIndex].reg = argv[2] + (argv[3] << 7);       query[queryIndex].bytes = argv[4] + (argv[5] << 7);       break;     case I2C_STOP_READING:    byte queryIndexToSkip;            // if read continuous mode is enabled for only 1 i2c device, disable       // read continuous reporting for that device       if (queryIndex <= 0) {         queryIndex = -1;              } else {         // if read continuous mode is enabled for multiple devices,         // determine which device to stop reading and remove it’s data from         // the array, shifiting other array data to fill the space         for (byte i = 0; i < queryIndex + 1; i++) {           if (query[i].addr = slaveAddress) {             queryIndexToSkip = i;             break;           }         }                 for (byte i = queryIndexToSkip; i<queryIndex + 1; i++) {           if (i < MAX_QUERIES) {             query[i].addr = query[i+1].addr;             query[i].reg = query[i+1].addr;             query[i].bytes = query[i+1].bytes;           }         }         queryIndex–;       }       break;     default:       break;     }     break;   case I2C_CONFIG:     delayTime = (argv[0] + (argv[1] << 7));

if(delayTime > 0) {       i2cReadDelayTime = delayTime;     }

if (!isI2CEnabled) {       enableI2CPins();     }         break;   case SERVO_CONFIG:     if(argc > 4) {       // these vars are here for clarity, they’ll optimized away by the compiler       byte pin = argv[0];       int minPulse = argv[1] + (argv[2] << 7);       int maxPulse = argv[3] + (argv[4] << 7);

if (IS_PIN_SERVO(pin)) {         if (servos[PIN_TO_SERVO(pin)].attached())           servos[PIN_TO_SERVO(pin)].detach();         servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);         setPinModeCallback(pin, SERVO);       }     }     break;   case SAMPLING_INTERVAL:     if (argc > 1) {       samplingInterval = argv[0] + (argv[1] << 7);       if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) {         samplingInterval = MINIMUM_SAMPLING_INTERVAL;       }          } else {       //Firmata.sendString(“Not enough data”);     }     break;   case EXTENDED_ANALOG:     if (argc > 1) {       int val = argv[1];       if (argc > 2) val |= (argv[2] << 7);       if (argc > 3) val |= (argv[3] << 14);       analogWriteCallback(argv[0], val);     }     break;   case CAPABILITY_QUERY:     Serial.write(START_SYSEX);     Serial.write(CAPABILITY_RESPONSE);     for (byte pin=0; pin < TOTAL_PINS; pin++) {       if (IS_PIN_DIGITAL(pin)) {         Serial.write((byte)INPUT);         Serial.write(1);         Serial.write((byte)OUTPUT);         Serial.write(1);       }       if (IS_PIN_ANALOG(pin)) {         Serial.write(ANALOG);         Serial.write(10);       }       if (IS_PIN_PWM(pin)) {         Serial.write(PWM);         Serial.write(8);       }       if (IS_PIN_SERVO(pin)) {         Serial.write(SERVO);         Serial.write(14);       }       if (IS_PIN_I2C(pin)) {         Serial.write(I2C);         Serial.write(1);  // to do: determine appropriate value       }       Serial.write(127);     }     Serial.write(END_SYSEX);     break;   case PIN_STATE_QUERY:     if (argc > 0) {       byte pin=argv[0];       Serial.write(START_SYSEX);       Serial.write(PIN_STATE_RESPONSE);       Serial.write(pin);       if (pin < TOTAL_PINS) {         Serial.write((byte)pinConfig[pin]);  Serial.write((byte)pinState[pin] & 0x7F);  if (pinState[pin] & 0xFF80) Serial.write((byte)(pinState[pin] >> 7) & 0x7F);  if (pinState[pin] & 0xC000) Serial.write((byte)(pinState[pin] >> 14) & 0x7F);       }       Serial.write(END_SYSEX);     }     break;   case ANALOG_MAPPING_QUERY:     Serial.write(START_SYSEX);     Serial.write(ANALOG_MAPPING_RESPONSE);     for (byte pin=0; pin < TOTAL_PINS; pin++) {       Serial.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127);     }     Serial.write(END_SYSEX);     break;   } }

void enableI2CPins() {   byte i;   // is there a faster way to do this? would probaby require importing   // Arduino.h to get SCL and SDA pins   for (i=0; i < TOTAL_PINS; i++) {     if(IS_PIN_I2C(i)) {       // mark pins as i2c so they are ignore in non i2c data requests       setPinModeCallback(i, I2C);     }   }      isI2CEnabled = true;     // is there enough time before the first I2C request to call this here?   Wire.begin(); }

/* disable the i2c pins so they can be used for other functions */ void disableI2CPins() {     isI2CEnabled = false;     // disable read continuous mode for all devices     queryIndex = -1;     // uncomment the following if or when the end() method is added to Wire library     // Wire.end(); }

/*==============================================================================  * SETUP()  *============================================================================*/

void systemResetCallback() {   // initialize a defalt state   // TODO: option to load config from EEPROM instead of default   if (isI2CEnabled) {    disableI2CPins();   }   for (byte i=0; i < TOTAL_PORTS; i++) {     reportPINs[i] = false;      // by default, reporting off     portConfigInputs[i] = 0; // until activated     previousPINs[i] = 0;   }   // pins with analog capability default to analog input   // otherwise, pins default to digital output   for (byte i=0; i < TOTAL_PINS; i++) {     if (IS_PIN_ANALOG(i)) {       // turns off pullup, configures everything       setPinModeCallback(i, ANALOG);     } else {       // sets the output to 0, configures portConfigInputs       setPinModeCallback(i, OUTPUT);     }   }   // by default, do not report any analog inputs   analogInputsToReport = 0;

/* send digital inputs to set the initial state on the host computer,    * since once in the loop(), this firmware will only send on change */   /*   TODO: this can never execute, since no pins default to digital input         but it will be needed when/if we support EEPROM stored config   for (byte i=0; i < TOTAL_PORTS; i++) {     outputPort(i, readPort(i, portConfigInputs[i]), true);   }   */ }

void setup() {   Firmata.setFirmwareVersion(FIRMATA_MAJOR_VERSION, FIRMATA_MINOR_VERSION);

Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);   Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);   Firmata.attach(REPORT_ANALOG, reportAnalogCallback);   Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);   Firmata.attach(SET_PIN_MODE, setPinModeCallback);   Firmata.attach(START_SYSEX, sysexCallback);   Firmata.attach(SYSTEM_RESET, systemResetCallback);

Firmata.begin(57600);   systemResetCallback();  // reset to default config }

/*==============================================================================  * LOOP()  *============================================================================*/ void loop() {   byte pin, analogPin;

/* DIGITALREAD – as fast as possible, check for changes and output them to the    * FTDI buffer using Serial.print()  */   checkDigitalInputs();

/* SERIALREAD – processing incoming messagse as soon as possible, while still    * checking digital inputs.  */   while(Firmata.available())     Firmata.processInput();

/* SEND FTDI WRITE BUFFER – make sure that the FTDI buffer doesn’t go over    * 60 bytes. use a timer to sending an event character every 4 ms to    * trigger the buffer to dump. */

currentMillis = millis();

if (currentMillis – previousMillis > samplingInterval) {     previousMillis += samplingInterval;     /* ANALOGREAD – do all analogReads() at the configured sampling interval */     for(pin=0; pin<TOTAL_PINS; pin++) {       if (IS_PIN_ANALOG(pin) && pinConfig[pin] == ANALOG) {         analogPin = PIN_TO_ANALOG(pin);

if (analogInputsToReport & (1 << analogPin)) {           Firmata.sendAnalog(analogPin, analogRead(analogPin));         }       }     }     // report i2c data for all device with read continuous mode enabled     if (queryIndex > -1) {       for (byte i = 0; i < queryIndex + 1; i++) {         readAndReportData(query[i].addr, query[i].reg, query[i].bytes);       }     }   } }

Processing codes:

import ddf.minim.*;

Minim minim1; Minim minim2; Minim minim3;

AudioPlayer track1_melody; AudioPlayer track2_carol; AudioPlayer track3_happyXmas;

AudioPlayer[] soundArray = new AudioPlayer[13];

boolean[] playedOnceArray = new boolean[13];

boolean playedOnce = false;

 

import processing.serial.*;

import cc.arduino.*;

Arduino arduino;

 

void setup() {   size(200, 200);

println(Arduino.list());

arduino = new Arduino(this, Arduino.list()[0], 57600);

for (int i = 8; i < 12; i++)     arduino.pinMode(i, Arduino.INPUT);

minim1 = new Minim(this);  // initiate minim library   minim2 = new Minim(this);   minim3 = new Minim(this);

track1_melody = minim1.loadFile(“WW.mp3”);  // attach track to minim variable   track2_carol = minim2.loadFile(“COTB.mp3”);   track3_happyXmas = minim3.loadFile(“Ave Maria (Schubert).mp3”);

soundArray[8] = track1_melody;  // assign track to array   soundArray[9] = track2_carol;   soundArray[10] = track3_happyXmas;

}

void draw() {   background(0);

for (int i = 8; i < 12; i++) {     if( i > 7 || i < 11){     if (arduino.digitalRead(i) == Arduino.HIGH) {       if (playedOnceArray[i] != true) {         playTrack(i);         playedOnceArray[i] = true;       }     }     else {       stopTrack(i);       playedOnceArray[i] = false;     }   }   } }

void playTrack(int t) {   println(t);   soundArray[t].play(); }

void stopTrack(int t) {   if (soundArray[t] != null) {     soundArray[t].pause();   } }

void stop() {   track1_melody.close();   minim1.stop();   track2_carol.close();   minim2.stop();   track3_happyXmas.close();   minim3.stop();   super.stop(); }

01e8699856de3eb08aee81a74ce5c29443e836f646 01ef1f1207e86817d0e46d83ef2cd91541b8c09e4f