Projekt Arduino Painstation: Porovnání verzí
| Řádek 41: | Řádek 41: | ||
== Odkazy na knihovnu arduina a vlastní zdrojový kód == | == Odkazy na knihovnu arduina a vlastní zdrojový kód == | ||
| + | |||
| + | === Arduino kód === | ||
| + | Náš finální kód využíval hlavně ukázkové projekty [https://www.arduino.cc/en/Tutorial/AnalogReadSerial https://www.arduino.cc/en/Tutorial/AnalogReadSerial] a [https://www.arduino.cc/en/Tutorial/SwitchCase https://www.arduino.cc/en/Tutorial/SwitchCase]. Níže uvedený kód je verze z průběhu vývoje a nejde tedy o kód finální s funkcionalitou plně odpovídající prezentačnímu videu. Ten byly bohužel ztracen v důsledku selhání HDD a systému záloh. | ||
| + | |||
| + | '''Upozornění! Vzhledem k omezení syntaxe wikisofia kopírujte prosím zdrojový kód přímo z editace zdroje stránky, nikoliv z podoby, do jaké je intepretována při zobrazení na stránce. Přislušný úsek je v souladu se syntaxí české wikipedie umístěn mezi značkou <nowiki><tt> a </tt></nowiki>.''' | ||
| + | |||
| + | <tt> | ||
| + | int led = 3; | ||
| + | int led2 = 5; | ||
| + | int kejs = 0; | ||
| + | int brightness = 0; | ||
| + | int sensorMin; | ||
| + | int sensorMax; | ||
| + | const long interval = 1000; | ||
| + | unsigned long previousMillis = 0; | ||
| + | |||
| + | void setup() { | ||
| + | // initialize serial communication at 9600 bits per second: | ||
| + | Serial.begin(9600); | ||
| + | pinMode(led,OUTPUT); | ||
| + | } | ||
| + | void loop() { | ||
| + | |||
| + | if (Serial.available()) { | ||
| + | // read the most recent byte (which will be from 0 to 255): | ||
| + | kejs = Serial.read(); | ||
| + | |||
| + | switch (kejs) { | ||
| + | case 0: // | ||
| + | |||
| + | unsigned long currentMillis = millis(); | ||
| + | |||
| + | if(currentMillis - previousMillis >= interval) { | ||
| + | |||
| + | brightness = 0; | ||
| + | analogWrite(led, brightness); | ||
| + | analogWrite(led2, brightness); | ||
| + | } | ||
| + | break; | ||
| + | case 1: // | ||
| + | brightness = 255; | ||
| + | analogWrite(led, brightness); | ||
| + | break; | ||
| + | case 2: // | ||
| + | brightness = 255; | ||
| + | analogWrite(led2, brightness); | ||
| + | break; | ||
| + | } | ||
| + | } | ||
| + | int sensorValue = analogRead(A0); | ||
| + | int sensorValue2 = analogRead(A1); | ||
| + | Serial.print(sensorValue); | ||
| + | Serial.print(","); | ||
| + | Serial.println(sensorValue2); | ||
| + | delay(1); | ||
| + | |||
| + | } | ||
| + | </tt> | ||
| + | |||
| + | === Processing kód === | ||
| + | |||
| + | Náš finální kód byl přizpůsobenou verzí kódu volně dostupného ze stránky [http://teachmetomake.com/wordpress/arduino-and-processing-pong-example http://teachmetomake.com/wordpress/arduino-and-processing-pong-example], který jsme obohatili například o postupné zrychlování a přizpůsobili jej pro fullscreen zobrazení. Následuje ukázka podoby kódu z průběhu vývoje (zatím bez zrychlování): | ||
| + | |||
| + | '''Upozornění! Vzhledem k omezení syntaxe wikisofia kopírujte prosím zdrojový kód přímo z editace zdroje stránky, nikoliv z podoby, do jaké je intepretována při zobrazení na stránce. Přislušný úsek je v souladu se syntaxí české wikipedie umístěn mezi značkou <nowiki><tt> a </tt></nowiki>.''' | ||
| + | |||
| + | <tt> | ||
| + | import processing.serial.*; // import the Serial library | ||
| + | import java.awt.Rectangle; // import Java's Rectangle class | ||
| + | |||
| + | Serial myPort; // The serial port | ||
| + | |||
| + | Rectangle leftPaddle, rightPaddle; // Rectangles for the paddles | ||
| + | |||
| + | int paddleHeight = 65; // vertical dimension of the paddles | ||
| + | int paddleWidth = 10; // horizontal dimension of the paddles | ||
| + | |||
| + | int ballSize = 10; // the size of the ball | ||
| + | int xDirection = 3; // the ball's horizontal direction. left is –2, right is 2. | ||
| + | int yDirection = 3; // the ball's vertical direction. up is –2, down is 2. | ||
| + | int xPos, yPos; // the ball's horizontal and vertical positions | ||
| + | |||
| + | int leftScore = 0; // score for left player | ||
| + | int rightScore = 0; // score for right player | ||
| + | |||
| + | int fontSize = 30; // size of the fonts on the screen | ||
| + | |||
| + | void setup() { | ||
| + | |||
| + | size(1280, 720); // set the size of the applet window | ||
| + | |||
| + | // List all the available serial ports | ||
| + | println(Serial.list()); | ||
| + | // 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'); | ||
| + | |||
| + | // initialize the paddles: | ||
| + | leftPaddle = new Rectangle(50, height/2, paddleWidth, paddleHeight); | ||
| + | rightPaddle = new Rectangle(width-50, height/2, paddleWidth, paddleHeight); | ||
| + | |||
| + | noStroke(); // no borders on drawn shapes | ||
| + | fill(255); // drawn shapes are white | ||
| + | |||
| + | resetBall(); | ||
| + | |||
| + | // create a font with the third font available to the system: | ||
| + | PFont myFont = createFont(PFont.list()[2], fontSize); | ||
| + | textFont(myFont); | ||
| + | |||
| + | } | ||
| + | void draw() { | ||
| + | // clear the screen: | ||
| + | background(0); | ||
| + | myPort.write(0); | ||
| + | |||
| + | // draw the paddles: | ||
| + | rect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height); | ||
| + | rect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height); | ||
| + | |||
| + | // calculate the ball's position and draw it | ||
| + | animateBall(); | ||
| + | |||
| + | // print the scores | ||
| + | text(leftScore, fontSize, fontSize); | ||
| + | text(rightScore, width-fontSize, fontSize); | ||
| + | } | ||
| + | // serialEvent method is run automatically whenever the buffer | ||
| + | // reaches the byte value set by bufferUntil(): | ||
| + | void serialEvent(Serial thisPort) { | ||
| + | |||
| + | // read the serial buffer: | ||
| + | String inputString = thisPort.readStringUntil('\n'); | ||
| + | |||
| + | if (inputString != null) | ||
| + | { | ||
| + | // trim the carrige return and linefeed from the input string: | ||
| + | inputString = trim(inputString); | ||
| + | |||
| + | // split the input string at the commas | ||
| + | // and convert the sections into integers: | ||
| + | int sensors[] = int(split(inputString, ',')); | ||
| + | |||
| + | // if we have received all the sensor values, use them: | ||
| + | if (sensors.length == 2) { | ||
| + | // scale the sliders' results to the paddles' range: | ||
| + | leftPaddle.y = int(map(sensors[0], 0, 903, 0, height - leftPaddle.height)); | ||
| + | rightPaddle.y = int(map(sensors[1], 0, 895, 0, height - rightPaddle.height)); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | void animateBall() { | ||
| + | |||
| + | if (leftPaddle.contains(xPos, yPos) || // if the ball pos is inside the left paddle | ||
| + | rightPaddle.contains(xPos, yPos)) { // or the ball pos is inside the right paddle | ||
| + | xDirection = -xDirection; // reverse the ball's X direction | ||
| + | } | ||
| + | // if the ball goes off the screen left: | ||
| + | if (xPos < 0) | ||
| + | { | ||
| + | rightScore++; | ||
| + | //Serial port; | ||
| + | //port = new Serial(this, Serial.list()[0], 9600); | ||
| + | myPort.write(1); | ||
| + | |||
| + | myPort.write(0); | ||
| + | |||
| + | resetBall(); | ||
| + | } // if the ball goes off the screen right: | ||
| + | |||
| + | if (xPos > width) | ||
| + | { | ||
| + | leftScore++; | ||
| + | myPort.write(2); | ||
| + | |||
| + | myPort.write(0); | ||
| + | resetBall(); | ||
| + | } | ||
| + | if (leftScore >= 10) | ||
| + | { | ||
| + | exit(); | ||
| + | } | ||
| + | if (rightScore >= 10) | ||
| + | { | ||
| + | exit(); | ||
| + | } | ||
| + | // stop the ball going off the top or the bottom of the screen: | ||
| + | if ((yPos <= 0) || (yPos >=height)) { | ||
| + | // reverse the y direction of the ball: | ||
| + | yDirection = -yDirection; | ||
| + | } | ||
| + | // update the ball position: | ||
| + | xPos = xPos + xDirection; | ||
| + | yPos = yPos + yDirection; | ||
| + | |||
| + | // Draw the ball: | ||
| + | rect(xPos, yPos, ballSize, ballSize); | ||
| + | } | ||
| + | void resetBall() | ||
| + | { | ||
| + | // initialize the ball in the center of the screen: | ||
| + | xPos = width/2; | ||
| + | yPos = height/2; | ||
| + | } | ||
| + | </tt> | ||
| + | |||
== Popis vývoje a konečné verze vlastního produktu == | == Popis vývoje a konečné verze vlastního produktu == | ||
== Videodokumentace == | == Videodokumentace == | ||
Verze z 23. 12. 2015, 16:10
Obsah
Členové projektu
Jakub Sedláček, Studia nových médií, e-mail: jakubsed@email.cz
David Vávra, Studia nových médií, e-mail: vavra.dav@gmail.com
Karel Nejedlý, Studia nových médií, e-mail: karel.nejedly@email.cz
Motivace vzniku projektu a jeho význam
Projekt Arduino Painstation vychází z původního díla umělecké skupiny "/////////fur//// art entertainment interfaces" s názvem Painstation. Originální projekt je založen na arkádové hře Pong, která je však obohacena o prvek zpětné vazby ve formě mírné bolesti působené hráčům . K tomuto účelu slouží tzv. PEU (Pain execution unit), na které má hráč položenu ruku dlaní dolů a která využívá elektrických šoků, tepelných impulzů a miniaturního mechanického "biče". Bolest je použita jako trest za hráčův neúspěch a její intenzita a podoba se mění v závislosti na postupu ve hře.
Smyslem zařízení je vnést do triviální interakce člověka a počítače, která se odehrává především v rovině symbolů, prvek reality. V digitálním světě jsou naše činy často zcela oproštěny od přímé, skutečné a hmatatelné zpětné vazby, která nás jinak motivuje k přemýšlení o našich činech a jejich důsledcích. Projekt Painstation tak tomuto pohodlnému způsobu komunikace nastavuje zrcadlo k čemuž využívá populárního a přístupného formátu arkádové hry Pong, kde je strojem zprostředkovaná interakce mezi hráči konfrontována s realitou tímto, sice netradičním, ale symbolickým způsobem. Naším cílem je tento projekt napodobit a s využitím možností open hardware i zpřístupnit dalším zájemcům.
V naší verzi tohoto uměleckého herního zařízení budeme rovněž vycházet z arkádové hry Pong a využívat bolest jako formu zpětné vazby za hráčův neúspěch. Zásadní rozdíl bude představovat použitý hardware - tím je v našem případu Arduino Uno s přidaným shieldem Tinker Kit. Samotná hra bude ovládána prostřednictvím dvou analogových slide faderů a bude probíhat na notebooku Lenovo Y500.
Zpětná vazba bude tvořena elektrošoky, které vytvoří připojené zařízení na elektrickou stimulaci svalstva a čtyři elektrody (2 pro každého hráče), které budou umístěny na hráčově předloktí. Součástí zpětné vazby jsou také svítící LED diody, které indikují, který hráč právě dostal šok / gól.
Použitý hardware a software
Vývojová deska Arduino UNO
Shield TinkerKit Sensor Shield V.2
Tinker Kit relé
Baterie 2200-3S (11,1V)
Arduino TinkerKit T000150 lineární tahový potenciometr
6x 2Pin Propojovací Kabel F-F 30cm RepRap Arduino
Elektronický svalový stimulátor HT-329L
1x Žlutá LED Dioda
1x Zelená LED Dioda
Odkazy na knihovnu arduina a vlastní zdrojový kód
Arduino kód
Náš finální kód využíval hlavně ukázkové projekty https://www.arduino.cc/en/Tutorial/AnalogReadSerial a https://www.arduino.cc/en/Tutorial/SwitchCase. Níže uvedený kód je verze z průběhu vývoje a nejde tedy o kód finální s funkcionalitou plně odpovídající prezentačnímu videu. Ten byly bohužel ztracen v důsledku selhání HDD a systému záloh.
Upozornění! Vzhledem k omezení syntaxe wikisofia kopírujte prosím zdrojový kód přímo z editace zdroje stránky, nikoliv z podoby, do jaké je intepretována při zobrazení na stránce. Přislušný úsek je v souladu se syntaxí české wikipedie umístěn mezi značkou <tt> a </tt>.
int led = 3; int led2 = 5; int kejs = 0; int brightness = 0; int sensorMin; int sensorMax; const long interval = 1000; unsigned long previousMillis = 0;
void setup() {
// initialize serial communication at 9600 bits per second: Serial.begin(9600); pinMode(led,OUTPUT);
} void loop() {
if (Serial.available()) {
// read the most recent byte (which will be from 0 to 255):
kejs = Serial.read();
switch (kejs) {
case 0: //
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
brightness = 0;
analogWrite(led, brightness);
analogWrite(led2, brightness);
}
break;
case 1: //
brightness = 255;
analogWrite(led, brightness);
break;
case 2: //
brightness = 255;
analogWrite(led2, brightness);
break;
}
}
int sensorValue = analogRead(A0);
int sensorValue2 = analogRead(A1);
Serial.print(sensorValue);
Serial.print(",");
Serial.println(sensorValue2);
delay(1);
}
Processing kód
Náš finální kód byl přizpůsobenou verzí kódu volně dostupného ze stránky http://teachmetomake.com/wordpress/arduino-and-processing-pong-example, který jsme obohatili například o postupné zrychlování a přizpůsobili jej pro fullscreen zobrazení. Následuje ukázka podoby kódu z průběhu vývoje (zatím bez zrychlování):
Upozornění! Vzhledem k omezení syntaxe wikisofia kopírujte prosím zdrojový kód přímo z editace zdroje stránky, nikoliv z podoby, do jaké je intepretována při zobrazení na stránce. Přislušný úsek je v souladu se syntaxí české wikipedie umístěn mezi značkou <tt> a </tt>.
import processing.serial.*; // import the Serial library import java.awt.Rectangle; // import Java's Rectangle class
Serial myPort; // The serial port
Rectangle leftPaddle, rightPaddle; // Rectangles for the paddles
int paddleHeight = 65; // vertical dimension of the paddles int paddleWidth = 10; // horizontal dimension of the paddles
int ballSize = 10; // the size of the ball int xDirection = 3; // the ball's horizontal direction. left is –2, right is 2. int yDirection = 3; // the ball's vertical direction. up is –2, down is 2. int xPos, yPos; // the ball's horizontal and vertical positions
int leftScore = 0; // score for left player int rightScore = 0; // score for right player
int fontSize = 30; // size of the fonts on the screen
void setup() {
size(1280, 720); // set the size of the applet window
// List all the available serial ports
println(Serial.list());
// 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');
// initialize the paddles:
leftPaddle = new Rectangle(50, height/2, paddleWidth, paddleHeight);
rightPaddle = new Rectangle(width-50, height/2, paddleWidth, paddleHeight);
noStroke(); // no borders on drawn shapes
fill(255); // drawn shapes are white
resetBall();
// create a font with the third font available to the system: PFont myFont = createFont(PFont.list()[2], fontSize); textFont(myFont);
} void draw() {
// clear the screen: background(0); myPort.write(0); // draw the paddles: rect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height); rect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height);
// calculate the ball's position and draw it animateBall();
// print the scores text(leftScore, fontSize, fontSize); text(rightScore, width-fontSize, fontSize);
} // serialEvent method is run automatically whenever the buffer // reaches the byte value set by bufferUntil(): void serialEvent(Serial thisPort) {
// read the serial buffer:
String inputString = thisPort.readStringUntil('\n');
if (inputString != null)
{
// trim the carrige return and linefeed from the input string:
inputString = trim(inputString);
// split the input string at the commas // and convert the sections into integers: int sensors[] = int(split(inputString, ','));
// if we have received all the sensor values, use them:
if (sensors.length == 2) {
// scale the sliders' results to the paddles' range:
leftPaddle.y = int(map(sensors[0], 0, 903, 0, height - leftPaddle.height));
rightPaddle.y = int(map(sensors[1], 0, 895, 0, height - rightPaddle.height));
}
}
} void animateBall() {
if (leftPaddle.contains(xPos, yPos) || // if the ball pos is inside the left paddle
rightPaddle.contains(xPos, yPos)) { // or the ball pos is inside the right paddle
xDirection = -xDirection; // reverse the ball's X direction
}
// if the ball goes off the screen left:
if (xPos < 0)
{
rightScore++;
//Serial port;
//port = new Serial(this, Serial.list()[0], 9600);
myPort.write(1);
myPort.write(0);
resetBall();
} // if the ball goes off the screen right:
if (xPos > width)
{
leftScore++;
myPort.write(2);
myPort.write(0);
resetBall();
}
if (leftScore >= 10)
{
exit();
}
if (rightScore >= 10)
{
exit();
}
// stop the ball going off the top or the bottom of the screen:
if ((yPos <= 0) || (yPos >=height)) {
// reverse the y direction of the ball:
yDirection = -yDirection;
}
// update the ball position:
xPos = xPos + xDirection;
yPos = yPos + yDirection;
// Draw the ball: rect(xPos, yPos, ballSize, ballSize);
} void resetBall() {
// initialize the ball in the center of the screen: xPos = width/2; yPos = height/2; }