Game

Jogando Super Nintendo no Android com controle original

Posted in Android, Arduino, C++, Electronics, Game on abril 21st, 2011 by Bruno Soares – 70 Comments

Jogando SNES com controle original via bluetooth Logo que comprei meu HTC Desire HD (sistema operacional Android) instalei um emulador de Super Nintendo (SNES), este foi um dos poucos videogames que tive, e era viciado (quando tinha 10 / 11 anos) em Super Mario World, assim que instalei o Mario me veio aquela nostalgia! Mas jogar com o controle touch screen é péssimo, não tem experiência tátil e o seu dedo fica encima do jogo.
Então pensei, é claro que alguém já ligou o controle original no celular, e estava certo,  encontrei este vídeo: “http://www.youtube.com/watch?v=_FZTz2KO9vU“. Mas tem um detalhe muito importante este projeto, é para NES e não o SNES (mais novo), ou seja, sabia que era possível fazer e tinha os materiais para isto, então só esperei sobrar um tempo para fazer (o único componente que não tinha era o controle, mas consegui um paralelo por R$ 8,00 no Mercado Livre).


vimeo.com/bsoares/snes-on-android-with-controller-and-bluesmirf

Como funciona:
O controle do SNES esta ligado a Arduino, é bem fácil obter as teclas pressionadas, mas existe uma biblioteca para tornar esta tarefa ainda mais simples (NESpad/SNESpad). Cada tecla pressionada liga um bit dentro do número que representa o estado das teclas do controle, e este estado (número) é enviado via Bluetooth (uso o BlueSMiRF) para o Android.
No Android, quem recebe este número é o Amarino, mas ele apenas recebe o número, ainda é necessário um App (que foi modificado a partir deste exemplo “SoftKeyboard“) para converter este número em teclas pressionadas (uso Bitwise) como um teclado do Android.
Por fim é só configurar o emulador para entender as teclas pressionadas como os comandos dentro do mesmo (pular, andar, girar, etc…)

Desta forma o controle pode ser usado como o teclado do seu Android, e ainda ser configurado como o controle de outros emuladores.

Sobre o módulo bluetooth BlueSMiRF:
Depois de configurado ele será o seu “cabo USB virtual” pois da mesma forma que usamos o comando Serial.print(“…”) para enviar dados via porta serial, o mesmo dado será enviado via bluetooth.
A App Amarino utiliza 57.600 de baudrate, e os módulos BlueSMiRF normalmente vem com 9.600.
Para configurar o blueSMiRF utilizei alguns tutoriais:
- http://todbot.com/blog/2006/02/23/howto-mac-os-x-bluetooth-serial-port/
- http://www.sparkfun.com/tutorials/67

Sobre o Soft Keyboard (App para Android):
Modifiquei o exemplo de Keyboard App disponível no Android Developers para obter os dados via Amarino e agir como teclado. Me baseei neste projeto criado para NES, porém criei outro código para a Arduino e alterei 95% do código para o Android.

O código fonte do projeto para Androi, Arduino e as versões das bibliotecas que foram utilizadas, podem ser baixadas do meu Github:
https://github.com/BSoares/SNES-on-Android-with-original-controller

Conteúdo Relacionado:
Código fonte
Arduino UNO
BlueSMiRF
Amarino
Android Sample Soft Keyboard
NESpad/SNESpad
Vídeo no Youtube

Enjoy :-)

Ping Pong com Matriz de Leds 8×8

Posted in Arduino, Electronics, Game on julho 6th, 2009 by Bruno Soares – 62 Comments

Há alguns dias consegui duas matrizes de leds 8×8 azuis e resolvi brincar com elas :)
O resultado foi um jogo de Ping Pong, sim O primeiro videogame lucrativo da história. É de fato um joguinho bem interessante e facíl para quem pratica eletrônica digital por hobby.

Em primeiro lugar achei melhor criar um protótipo do game em Flash (ActionScript 3.0) para não ficar “queimando os miolos” direto no C++. Pra quem quiser anexo o código fonte do game final e protótipo aqui.

This movie requires Flash Player 9

Espaço para iniciar.
Seta para cima e para baixo para mover as astes.

Para ligar e desligar cada led da matriz utilizei como referência o código encontrado no Arduino Playground (http://www.arduino.cc/playground/Main/DirectDriveLEDMatrix), só que minha matriz é diferente do utilizado no exemplo, então fiz algumas pequenas alterações no método doubleBuffer. Resumindo, no exemplo encontrado no Playground a matriz utilizada tem a linha como cátodo já minha matriz tem a coluna como cátodo.
Abaixo a especificação da matriz utilizada:

8x8-led-matriz-especification

Código-fonte:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/**
 * Ping Pong with 8x8 Led Dot Matrix on Arduino
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


#include "TimerOne.h"

#define PIN_LEFT 4
#define PIN_RIGHT 5

unsigned int left = 0;
unsigned int right = 0;
int angle = 0;
int radians;

byte rows[8] = {9, 14, 8, 12, 1, 7, 2, 5};
byte cols[8] = {13, 3, 4, 10, 6, 11, 15, 16};
byte pins[16] = {5, 4, 3, 2, 14, 15, 16, 17, 13, 12, 11, 10, 9, 8, 7, 6};
byte screen[8] = {0, 0, 0, 0, 0, 0, 0, 0};
volatile byte screenRow = 0;
volatile byte screenCol = 0;

int _angle;
int _px;
int _py;
int _w = 7;
int _h = 7;
int _wall[] = {3, 3};
int _count = 0;
int _speed = 3;
int _countPoints = 0;


void setup() {
  Timer1.initialize(100);
  for (int i = 2; i <= 17; i++)
    pinMode(i, OUTPUT);
  Timer1.attachInterrupt(doubleBuffer);
 
  Serial.begin(9600);
 
  face();
  reset();
}

void doubleBuffer() {
  digitalWrite(translatePin(rows[screenRow]), LOW);
  digitalWrite(translatePin(cols[screenCol]), HIGH);
 
  screenCol++;
  if (screenCol >= 8) {
    screenCol = 0;
    screenRow++;
    if (screenRow >= 8) {
      screenRow = 0;
    }
  }
 
  if((screen[screenRow] >> screenCol) & B1 == B1) {
    digitalWrite(translatePin(rows[screenRow]), HIGH);
    digitalWrite(translatePin(cols[screenCol]), LOW);
  } else {
    digitalWrite(translatePin(rows[screenRow]), LOW);
    digitalWrite(translatePin(cols[screenCol]), HIGH);
  }
}

byte translatePin(byte original) {
  return pins[original - 1];
}

void allOFF() {
  for (int i = 0; i < 8; i++)
    screen[i] = 0;
}

void on(byte row, byte column) {
    screen[column-1] |= (B1 << (row - 1));
}

void off(byte row, byte column) {
    screen[column-1] &= ~(B1 << (row - 1));
}

void calcWall()
{
  left = analogRead(PIN_LEFT);
  right = analogRead(PIN_RIGHT);
  left = constrain(map(left, 223, 800, 0, 6), 0, 6);
  right = constrain(map(right, 223, 800, 6, 0), 0, 6);
 
  clearWall();
 
  on(1, left + 1);
  on(1, left + 2);
  on(8, right + 1);
  on(8, right + 2);
 
  _wall[0] = left;
  _wall[1] = right;
  show();
}

void clearWall()
{
  for (int i = 0; i < 8; i++)
    screen[i] &= B01111110;
}

void clearGame()
{
  for (int i = 0; i < 8; i++)
    screen[i] &= B10000001;
}

void loop() {
  calcWall();
  enterFrameHandler();
  delay(50);
}

void enterFrameHandler()
{
  if (_count++ < _speed)
    return;
 
  _count = 0;
  checkCollision();
  calcAngleIncrement();
  show();
}

void retorted(int angle)
{
  Serial.println(angle);
  _angle = angle;
 
  if (++_countPoints % 5 == 0 && _speed > 1)
    _speed--;
}

void resetAnim()
{
  for (int i = 0; i < 8; i++)
  {
    screen[i] = B11111111;
    delay(25);
  }
  for (int i = 0; i < 8; i++)
  {
    screen[i] = B00000000;
    delay(25);
  }
}

void face()
{
  on(1, 1);
  on(1, 2);
  on(2, 1);
  on(2, 2);
  on(7, 1);
  on(7, 2);
  on(8, 1);
  on(8, 2);
  on(1, 1);
  on(1, 2);
  on(4, 4);
  on(4, 5);
  on(5, 4);
  on(5, 5);
  on(2, 7);
  on(7, 7);
  on(3, 8);
  on(4, 8);
  on(5, 8);
  on(6, 8);
  delay(5000);
}

void reset()
{
  resetAnim();
 
  _px = random(3, 5);
  _py = random(3, 5);
  _angle = random(0, 2) == 0 ? 0 : 180;
  _speed = 5;
  _countPoints = 0;
 
  show();
  delay(500);
}

void show()
{
  clearGame();
  on(_px + 1, _py + 1);
}

void checkCollision()
{
  if (_px == _w - 1)
  {
    if (_angle == 315 || _angle == 0 || _angle == 45)
    {
      if (_py == _wall[1] || _py == _wall[1] + 1)
      {
        if (_angle == 0 && _py == _wall[1])
          retorted(225);
        else if (_angle == 0 && _py == _wall[1] + 1)
          retorted(135);
        else if (_angle == 45 && _py == _wall[1])
          retorted(135);
        else if (_angle == 45 && _py == _wall[1] + 1)
          retorted(180);
        else if (_angle == 315 && _py == _wall[1])
          retorted(180);
        else if (_angle == 315 && _py == _wall[1] + 1)
          retorted(225);
      }
    }
  }
  else if (_px == 1)
  {
    if (_angle == 225 || _angle == 180 || _angle == 135)
    {
      if (_py == _wall[0] || _py == _wall[0] + 1)
      {
        if (_angle == 180 && _py == _wall[0])
          retorted(315);
        else if (_angle == 180 && _py == _wall[0] + 1)
          retorted(45);
        else if (_angle == 135 && _py == _wall[0])
          retorted(45);
        else if (_angle == 135 && _py == _wall[0] + 1)
          retorted(0);
        else if (_angle == 225 && _py == _wall[0])
          retorted(0);
        else if (_angle == 225 && _py == _wall[0] + 1)
          retorted(315);
      }
    }
  }
 
  if (_px == _w)
  {
    reset();
  }
  else if (_px == 0)
  {
    reset();
  }
  else if (_py == _h)
  {
    if (_angle == 45)
      _angle = 315;
    else if (_angle == 135)
      _angle = 225;
  }
  else if (_py == 0)
  {
    if (_angle == 225)
      _angle = 135;
    else if (_angle == 315)
      _angle = 45;
  }
}

void calcAngleIncrement()
{
  if (_angle == 0 || _angle == 360)
  {
    _px += 1;
  }
  else if (_angle == 45)
  {
    _px += 1;
    _py += 1;
  }
  else if (_angle == 135)
  {
    _px -= 1;
    _py += 1;
  }
  else if (_angle == 180)
  {
    _px -= 1;
  }
  else if (_angle == 225)
  {
    _px -= 1;
    _py -= 1;
  }
  else if (_angle == 315)
  {
    _px += 1;
    _py -= 1;
  }
}

Código fonte do experimento (Arduino e Flash).

Conteúdo relacionado:
Instructables
Mais fotos no Flickr
Ping Pong com Matriz de Leds 8×8 no YouTube
Arduino 8×8 LED Matrix Direct drive
O primeiro videogame lucrativo da história (Pong)
Led Dot Matrix
TimerOne