luni, 25 noiembrie 2013

un..gamepad cu Arduino Uno

Incontinuare la articolul precedent mi-am pus intrebarea :
Cum se poate face cel mai simplu gamepad cu Arduino ?
Să aiba un grup de 5 butoane , cu următorul rol :
 - dacă aps pe 4 din ele,  deplaseze intr-o fereastra de pe PC o figură geometrică, (un pătrat)  pe patru direcţii: sus; jos; dreapta; stânga
-  al cincilea buton, să fie "fire" - când îl apăs, să se schimbe culoarea pătratului;  
În primul rând, trebuie să construim  gamepad-ul nostru pe breadboard. Vom folosi 4 butoane pentru direcţii (dreapta, sus, stânga, jos), și un altul pentru foc.
O să le conectãm la pinii  Arduino și masă în mod direct, fără nici o rezistenţã pullup sau pulldawn: vom activa rezistenţe pull-up interne din cadrul programului.Folosind rezitorii pullup interni, circuitul va fi mult mai simplu. El  va arãta astfel :


 După asamblarea pe bredboard și cuplarea la pinii lui Arduino Uno, mai trebuie incrcat programul .  Exemplul l-am preluat de la :
http://www.varesano.net/blog/fabio/serial-communication-arduino-and-processing-simple-examples-and-arduino-based-gamepad-int   prezentat în paragraful  Interfacing Arduino with a program running on a PC: Arduino and Processing to implement a simple gamepad and videogame"
Şi aici a fost surpriza:     la compilare, a apãrut eroarea şi confuzia!





















Am fost nevoit sã reiau la rând funcţiile din Arduino IDE, şi totul despre tipurile de date BYTE ! ca sã pricep scriptul. A trebuit sã inlocuiesc ultima instrutiune, dar … macar am priceput “ce şi cum face“  programul. 

Am tradus comentariile , am corectat ultima instrucţiune şi scriptul meu este astfel :
-------------------------------------------------------------------------------------------------------
/** game1
* Utilizeaza  rezistoarele interne pullups pentru a citi starea
* celor 5 butoane.
* Apoi , declara variabila "unsigned int state" care este de fapt
* echivalenta cu tipul Byte. Prin operatii pe bit, (bitwise)
* incarca succesiv valorile citite de pe pini pe locurile 5; 4; 3; 2; 1 din variabila state;
*  variabila state ocupa 4 digiti; ea este expediata serial   
*/

#define IN1 2  // fire
#define IN2 3  // right
#define IN3 4  // up
#define IN4 5  // left
#define IN5 6  // down

int state1 = HIGH;
int state2 = HIGH;
int state3 = HIGH;
int state4 = HIGH;
int state5 = HIGH;

void setup() {
  Serial.begin(9600);
 
  pinMode(IN1, INPUT);
  digitalWrite(IN1, HIGH); // enable pullup resitor
 
  pinMode(IN2, INPUT);
  digitalWrite(IN2, HIGH); // enable pullup resitor
 
  pinMode(IN3, INPUT);
  digitalWrite(IN3, HIGH); // enable pullup resitor
 
  pinMode(IN4, INPUT);
  digitalWrite(IN4, HIGH); // enable pullup resitor
 
  pinMode(IN5, INPUT);
  digitalWrite(IN5, HIGH); // enable pullup resitor
}

void loop() {
  delay(10); // debounces switches
 
  int val1 = digitalRead(IN1);
  int val2 = digitalRead(IN2);
  int val3 = digitalRead(IN3);
  int val4 = digitalRead(IN4);
  int val5 = digitalRead(IN5);
 
  // verific daca s-a facut vre-o modificare in "state" 
  if(state1 != val1 || state2 != val2 || state3 != val3 || state4 != val4 || state5 != val5) {
    state1 = val1;
    state2 = val2;
    state3 = val3;
    state4 = val4;
    state5 = val5;
   
    /*
   acum, creem un cuvint pe  8 biti (1 byte) ai carui 5 biti din dreapta reprezinta -
   dela dreapta la stinga-  starea butoanelor fire, right, up, left, down :
   adica 0 daca e apasat, 1 daca nu e apasat not pressed
   Ceilalti  biti nu sint utilizati.
     */
    unsigned int state = (val5 << 4) | (val4 << 3) | (val3 << 2) | (val2 << 1) | val1 ;
    Serial.write(state);
   }

}
-------------------------------------------------------------------------------------------------------------------------------------------------------

Programul de mai sus este destul de simplu. Am văzut deja totul despre logica de comunicare aici.
Singurul lucru nou este faptul că am încercat să limitez cantitatea de date care călătoresc pe firul de serie. De aceea, am setat  variabila "state" cu ajutorul unor operații la nivel de bit. Se construiește un cuvânt de 8 biți ("state"), a cărui 5 biți din dreapta sunt setaţi la 1 în cazul în care butonul asociat nu este apăsat,  sau la 0, dacă este apăsat.
Fiecare bit este inserat pe un loc specificat in byte. Ceilalţi 3 biti rãmin neutilizaţi. astfel , se va expedia serial un singur byte.
Jocul video din Processing
Mai sus, am creat un program care este capabil sã trimitã datele  noastre de stare trimise de  gamepad pe firul serial la PC-ului.
 Acum avem nevoie de ceva pe partea de PC, care va citi aceste date și care va reacționa conform cu valorile situate în variabila “state”.Vom crea un program simplu care deplaseazã un pătrat pe o fereastră.
Folosind butoanele de pe gamepad vom deplasa caseta de pe ecran.
Când apăsați butonul foc, caseta se va schimba culoarea casetei.
Scriptul l-am copiat de la adresa specificată in link-ul de mai sus.
Atentie : şi partea asta de program am modificat-o pentru cã instrucţiunea din scriptul original care cautã  portul serial al lui Arduino, pentru ca  pe PC-ul meu nu a funcţionat.
Linia urmãtoare
             myPort = new Serial(this, "/dev/ttyUSB5", 9600);
a trebuit sã o inlocuiesc cu setul de instrucţiuni:
String portName = Serial.list()[portIndex];
println(" Connecting to -> " + Serial.list()[portIndex]);
myPort = new Serial(this, portName, 9600)
Scriptul corectat, editat in Processing 2.0 şi care merge , este acesta :

-------------------------------------------------------------------------------------------------------------
// game1 = Processing
 import processing.serial.*;
Serial myPort;  // Create object from Serial class
short portIndex = 1; // select the com port, 0 is the first port

int state = 31;
int fire = 1;
int right = 1;
int up = 1;
int left = 1;
int down = 1;

int x = 275;
int y = 275;
int c = 0;

void setup()
{
  size(600, 600);
String portName = Serial.list()[portIndex];
println(" Connecting to -> " + Serial.list()[portIndex]);
myPort = new Serial(this, portName, 9600);

}

void draw() {
  if(myPort.available() > 0) {
    state = myPort.read();
    println(state);
  println(binary(state));  //sa vedem efectul
    println(state);
    fire = state & 1;
    right = (state & 2) >> 1;
    up = (state & 4) >> 2;
    left = (state & 8) >> 3;
    down = (state & 16) >> 4;
   
    print(fire);
    print(right);
    print(up);
    print(left);
    println(down);
  }
   
  c = (fire == 0) ? 250 : 0;
 
  if(right == 0 && left == 1) {
    x = x + 2;
  }
  if(up == 0 && down == 1) {
    y = y - 2;
  }
  if(left == 0 && right == 1) {
    x = x - 2;
  }
  if(down == 0 && up == 1) {
    y = y + 2;
  }
 
  background(255, 255, 150);
  fill(c);
  rect(x, y, 50, 50);
}

Intrebarea retoricã ar fi : de ce  unii mai publicã , daca scripurile lor nu sint corectate, şi funcţionale !
Poate mai sânt şi începatori , chiar şi copii de 14 – 18 ani, care vor sã lucreze , şi sã reuşeascã ! 

Niciun comentariu:

Trimiteți un comentariu