Jogando Super Nintendo no Android com controle original

Posted in Android, Arduino, C++, Electronics, Game on abril 21st, 2011 by Bruno Soares – 67 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 – 58 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

PianoDuino (Arduino + Processing + SoundCipher)

Posted in Arduino, C++, Electronics, Processing on maio 30th, 2009 by Bruno Soares – 21 Comments

PianoDuino é um experimento simples que integra Arduino, Processing e uma biblioteca para manipular sons, a SoundCipher. A ideia serviu para experimentar o Multiplexador / Demultiplexador 4051.

PianoDuino (Arduino + Processing + SoundCipher) from Bruno Soares on Vimeo.

Arduino:

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
/**
 * PianoDuino
 *
 * @author   Bruno Soares
 * @link     http://www.bsoares.com.br
 * @language Arduino / C++
 */


#define PIN_SELECTOR0 2
#define PIN_SELECTOR1 3
#define PIN_SELECTOR2 4

int value;
int count;

// Selectors
byte s0;
byte s1;
byte s2;

void setup()
{
  Serial.begin(9600);
 
  pinMode(PIN_SELECTOR0, OUTPUT);
  pinMode(PIN_SELECTOR1, OUTPUT);
  pinMode(PIN_SELECTOR2, OUTPUT);
}

void loop()
{
  for (count = 0; count < 8; count++)
  {
    // Extract active bits
    s0 =  count       & 0x1;
    s1 = (count >> 1) & 0x1;
    s2 = (count >> 2) & 0x1;
   
    // Select input
    digitalWrite(2, s0);
    digitalWrite(3, s1);
    digitalWrite(4, s2);
   
    // Read input selected
    value = analogRead(0);
    if (value > 5)
    {
      Serial.print(count);
      while(analogRead(0) > 5);
    }
  }
}

Processing:

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
/**
 * PianoDuino
 *
 * @author   Bruno Soares
 * @link     http://www.bsoares.com.br
 * @language Processing
 */


import arb.soundcipher.*;
import processing.serial.*;

SoundCipher[] sc = new SoundCipher[8];
Serial port;
int portValue = 0;

void setup()
{
  for (int i = 0; i < sc.length; i++)
  {
    sc[i] = new SoundCipher(this);
  }
 
  println(Serial.list());
  port = new Serial(this, Serial.list()[1], 14400);
}

void draw()
{
  while (port.available() > 0) {
    portValue = int(port.readString().substring(0, 1));
    print(portValue);
    sc[portValue].playNote(80 - (portValue * 7), 100, 2.5);
  }
}

Baixe o código fonte completo aqui.

 


 

Conteúdo relacionado:
Fotos no Flickr
Arduino
Processing
SoundCipher
Analog multiplexer / demultiplexer 4051

Operações binárias

Posted in ASP.NET, ActionScript 3.0, Arduino, C++, Electronics, Flash, PHP on maio 25th, 2009 by Bruno Soares – 7 Comments

Ultimamente tenho me deparado com muitos trechos de códigos que utilizam operações binárias, como chaveamento de multiplexador, extração de RGB a partir de um inteiro ou hexadecimal, bitshift para controlar LED Matrix, etc… E finalmente dei aquela estuda, agora vai ai um post sobre o que resultou o estudo.

Obs.: Os trechos de códigos deste post foram escritos em ActionScript, mas pode ser aplicado a C, C++, Java, Processing, PHP, entre outras linguagens.


Introdução
• Bit Shift
   • Operador >> (bitwise right shift)
   • Operador << (bitwise left shift)
• Operações Bitwise
   • Operador & (bitwise AND)
   • Operador | (bitwise OR)
   • Operador ^ (bitwise XOR)
   • Operador ~ (bitwise NOT)
• Exemplos
   • Extraindo o RGB de uma cor
   • Chaveando multiplexador 4051
 


Introdução
Um operador binário, como o nome sugere, é um operador que trabalha com a representação binária do número, e como normalmente não sabemos a representação binária dos números de cabeça, vamos utilizar a tabela abaixo:

 -----------------------
|      BIN |  DEC | HEX |
|-----------------------|
|        1 |    1 |   1 |
|       10 |    2 |   2 |
|       11 |    3 |   3 |
|      100 |    4 |   4 |
|      101 |    5 |   5 |
|      110 |    6 |   6 |
|      111 |    7 |   7 |
|     1000 |    8 |   8 |
|     1001 |    9 |   9 |
|     1010 |   10 |   A |
|     1011 |   11 |   B |
|     1100 |   12 |   C |
|     1101 |   13 |   D |
|     1110 |   14 |   E |
|     1111 |   15 |   F |
|    10000 |   16 |  10 |
|    10001 |   17 |  11 |
|    10010 |   18 |  12 |
|    10011 |   19 |  13 |
|    10100 |   20 |  14 |
|-----------------------|
| 11111111 |  255 |  FF |
 ----------------------- 

A tabela lista os números de 1 à 20 e 255 em três bases diferentes:
Binário (BIN)
• Decimal (DEC)
Hexadecimal (HEX)

Analisando a tabela podemos concluir que 3d = 11b, 19d = 10011b (as letras d e b significam decimal e binário respectivamente). Lembrando que pode ser utilizada uma calculadora que opere em binário (como a do windows) ou uma alternativa de conversão de bases on-line como está: “Conversão de número binário”.

Então vamos deslocar, escorregar, escovar alguns bits para entender melhor.


Operador >>
Deslocamento de bits para a direita (bitwise right shift)

1
2
3
4
trace(8 >> 1); // 4
trace(8 >> 2); // 2
trace(8 >> 3); // 1
trace(8 >> 4); // 0

Olhando os números na base decimal faz pouco sentido, ou talvez nenhum sentido, então passamos os números corretos para a base binária e tudo fica mais claro:
8d = 1000b (8 decimal é igual a 1000 em binário), então:
1000b >> 1 (deslocando uma casa para direita) temos o número:
100b que em decimal é 4(dê uma olhada na tabela).

Agora ficou fácil não? Vamos deslocar o número 13:

1
trace(13 >> 1); // 6

13 em binário é 1101, deslocando uma casa para a direita (ou removendo 1 bit), fica 110, e 110 é igual a 6 em decimal.


Operador <<
Deslocamento de bits para a esquerda (bitwise left shift)

1
2
3
trace(2 << 1); // 4
trace(2 << 2); // 8
trace(2 << 3); // 16

Agora é só seguir o mesmo raciocino já utilizando anteriormente.
Se 2 em base binária é igual a 10 e deslocarmos um bit para esquerda, vamos ganhar mais um zero, ficando com 100 que é igual a 4 em decimal.


Operador &
AND binário (bitwise AND)
O operador & compara bit a bit os números a sua direita e esquerda, por exemplo o resultado de 10 & 11 é 10:

  1010
& 1011
------
  1010

A comparação bit-a-bit somente retorna True (1) quando os bits comparados são iguais a 1, caso contrário retorna False (0). Formando assim um novo número.
Mais alguns exemplos para fortalecer:

|14 &  9|13 & 11|20 &  9|14 & 10|89 &  112|45  &  77|255  &  13|112 &  255|
|       |       |       |       |         |         |          |          |
|  1110 |  1101 |  10100|  1110 |  1011001|   101101|  11111111|   1110000|
|& 1001 |& 1011 |&  1001|& 1010 |& 1110000|& 1001101|&     1101|& 11111111|
|  ---- |  ---- |  -----|  ---- |  -------|  -------|  --------|  --------|
|  1000 |  1001 |      0|  1010 |  1010000|     1101|      1101|   1110000|
|    8d |    9d |     0d|   10d |      80d|      13d|       13d|      112d|


Operador |
OR binário (bitwise OR)
O operador | tem a mesma função do operador OR comum (||) só que atua bit-a-bit, assim como os outros operadores binários. Vejamos um exemplo:

  1010
| 1011
------
  1011

Se um dos bits comparados forem iguais a 1 a expressão retornará 1, caso os dois bits comparados forem iguais a 0, a expressão retorna 0. Agora vamos refazer o exemplo anterior trocando apenas o operador & (and binário) por | (or binário):

|14 |  9|13 | 11|20 |  9|14 | 10|89 |  112|45  |  77|255  |  13|112 |  255|
|       |       |       |       |         |         |          |          |
|  1110 |  1101 |  10100|  1110 |  1011001|   101101|  11111111|   1110000|
|| 1001 || 1011 ||  1001|| 1010 || 1110000|| 1001101||     1101|| 11111111|
|  ---- |  ---- |  -----|  ---- |  -------|  -------|  --------|  --------|
|  1111 |  1111 |  11101|  1110 |  1111001|  1101101|  11111111|  11111111|
|   15d |   15d |    29d|   14d |     121d|     109d|      255d|      255d|


Operador ^
OU exclusivo (bitwise XOR)
A letra X na frente do OR significa Exclusive (Exclusive OR). Isso quer dizer que este operador faz a comparação binária de dois números e resulta os bits que são diferentes. Por exemplos, quais são os bits diferentes entre os números 10 e 11?

  1010
^ 1011
------
     1

Vamos novamente trocar o operador do exemplo anterior para analisar os resultados:

|14 ^  9|13 ^ 11|20 ^  9|14 ^ 10|89 ^  112|45  ^  77|255  ^  13|112 ^  255|
|       |       |       |       |         |         |          |          |
|  1110 |  1101 |  10100|  1110 |  1011001|   101101|  11111111|   1110000|
|^ 1001 |^ 1011 |^  1001|^ 1010 |^ 1110000|^ 1001101|^     1101|^ 11111111|
|  ---- |  ---- |  -----|  ---- |  -------|  -------|  --------|  --------|
|   111 |   110 |  11101|   100 |   101001|  1100000|  11110010|  10001111|
|    7d |    6d |    29d|    4d |      41d|      96d|      242d|      143d|


Operador ~
Negação (bitwise NOT)
O operador NOT inverte o sinal e complementa em um.
Negando o número 168 (~168) teremos -169.

Alguns exemplos:

1
2
3
4
5
6
trace(~7);   // -8
trace(~-7);  // 6
trace(~14);  // -15
trace(~13);  // -14
trace(~255); // -256
trace(~112); // -113

 


Extraindo o RGB de uma cor
Sabendo que uma cor no formato RGB utiliza dois dígitos hexadecimais para definir quanto existe de Vermelho, Verde e Azul (respectivamente), formando cores como: Vermelho (FF0000), Cinza (C0C0C0), Laranja (FF9900), etc. Temos ai a possibilidade de gerar 16.581.375 de cores com este código, é só fazer a conta para conferir: 255 * 255 * 255 ou FF * FF * FF.
Vamos desmembrar um tom de azul (#347BB7) para saber quanto esta cor tem de Vermelho, Verde e Azul (o valor dos canais RGB).

1
2
3
4
5
6
7
8
9
10
// DEC: 3439543
// BIN: 1101000111101110110111
var color:uint = 0x347BB7;

var r:uint = (color >> 16) & 0xFF;
var g:uint = (color >>  8) & 0xFF;
var b:uint =  color        & 0xFF;

trace("Red:", r, "Green:", g, "Blue:", b);
// Red: 52 Green: 123 Blue: 183

Linha 5) Deslocando 16 bits para a direita temos:

  1101000111101110110111 >> 16
=                 110100 (DEC: 52)

Para o caso do vermelho não precisamos continuar a expressão (& 0xFF),
pois deslocando 16 bits para a direita já temos o resultado do vermelho,
mas se a cor estivesse no formato ARGB (Alpha Red Green Blue), seria necessário.

Linha 6) Deslocando 8 bits para conseguir o verde:

  1101000111101110110111 >> 8
=         11010001111011 (DEC: 13435)

Só com o valor do deslocamentos não vamos conseguir a cor verde, então utilizamos o
operador & (AND) com o valor 255 (0xFF) para extrair a parte binária que nos interessa:

  11010001111011 (DEC: 13435)
&       11111111 (DEC: 255, HEX: 0xFF)
  --------------
        01111011 (DEC: 123)

Linha 7) Para extrair o azul não precisamos deslocar bits e sim pegar os últimos 8 bits:

  1101000111101110110111
&               11111111 (DEC: 255, HEX: 0xFF)
  ----------------------
                10110111 (DEC: 183)

Agora voltando para o hexadecimal:

1
2
3
4
5
6
7
var r:uint = 52;
var g:uint = 123;
var b:uint = 183;
var color:uint = (r << 16) | (g << 8) | b;

trace(color.toString(16));
// 347bb7

Linha 4) Deslocando 16 bits para a esquerda do número 52 (Vermelho):

  110100 << 16
= 1101000000000000000000

Deslocando 8 bits para a esquerda do número 123 (Verde):

  1111011 << 8
= 111101100000000

Efetuando o OR (|) com o resultado das duas operações ((r << 16) | (g << 8)):

  1101000000000000000000
|        111101100000000
  ----------------------
  1101000111101100000000

Efetuando a última operação, o OR com o Azul (183)

  1101000111101100000000
| 0000000000000010110111
  ----------------------
  1101000111101110110111

O resultado agora ficou claro. O número 1101000111101110110111 (binário) é igual a 3439543 (decimal) e 347BB7 (hexadecimal).

 


Chaveando multiplexador 4051
A tarefa de chavear um Multiplexador / Demultiplexador (MUX / DEMUX) 4051 é muito parecida com a extração dos canais RGB de uma cor. Você só precisa Ligar ou Desligar três pinos de seleção (select pins) para que o circuito interprete o valor gerado e transmita a voltagem da entrada desejada.
Existe um gif animado do RogerCom muito didático que demonstra o funcionamento do CI 4051, gif animado CI 4051 aqui.
Por exemplo, para ler a entrada 3, precisamos desligar o pino de seleção 0, ligar o 1 e o 2, formando assim o número 011 (binário) que é igual a 3 em decimal. Veja no código (Escrito em Arduino / C++):

1
2
3
4
5
6
7
8
9
10
11
12
// Entrada desejada
int count = 3;

// Extração dos bits ativos
byte s0 =  count       & 0x1;
byte s1 = (count >> 1) & 0x1;
byte s2 = (count >> 2) & 0x1;

// Ligando ou desligando os pinos de seleção
digitalWrite(2, s0);
digitalWrite(3, s1);
digitalWrite(4, s2);

Conteúdo relacionado:
Bitwise operation on Wikipedia
 

Vector em AS3 para Flash Player 10

Posted in ActionScript 3.0, Flash on maio 25th, 2009 by Bruno Soares – 2 Comments

Para aqueles que já estão usando Flash Player 10, trocar o Array por Vector é uma boa opção, pois você ganha em performance e organização de código (Vector é um array tipado ou seja, mais organizado).
O que mudou mais é a forma de construir o objeto, a sintaxe funciona como o Generics do C# ou Java. Ressaltando que não existe generics em AS3, somente o Vector que implementa uma sintaxe parecida com a sintaxe de um generics.

Muitos blogueiros já comentaram sobre o Vector logo quando saiu a versão beta do Flash Player 10. Por este motivo não vou falar muito sobre, vou apenas exemplificar e dar links para os outros blog.

Criando um Vector:

1
var vetor:Vector.<T> = new Vector.<T>();

(Substitua o T pelo tipo desejado)

Veja como é parecido com um Array:

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
package
{
    import __AS3__.vec.Vector;
   
    import flash.display.Sprite;
    import flash.utils.getTimer;
   
    [SWF(framerate="30", width="500", height="500")]
    public class Main extends Sprite
    {
        private var _vector:Vector.<String> = new Vector.<String>();
        private var _timer:Number;
       
        public function Main()
        {
            _vector.push("a");
            _vector.push("b");
            trace(_vector.length);
            _vector.push("c");
            trace(_vector.length);
            _vector[_vector.length] = "d";
            trace(_vector.length);
           
            for each (var s:String in _vector)
            {
                trace("FOR: " + s);
            }
           
            trace("Timer Vector:");
            testVector();
           
            trace("Timer Array:");
            testArray();
        }
       
        private function testArray():void
        {
            _timer = getTimer();
            var array:Array = new Array();
            for(var i:uint = 0; i < 1000000; i++)
            {
                array[i] = i;
            }
            trace(getTimer() - _timer);
        }
       
        private function testVector():void
        {
            _timer = getTimer();
            var vector:Vector.<uint> = new Vector.<uint>();
            for(var i:uint = 0; i < 1000000; i++)
            {
                vector[i] = i;
            }
            trace(getTimer() - _timer);
        }
    }
}

(O código acima é a Main Class de um projeto Flex / ActionScript)

Teste de performance entre arrays, vector e outros:
http://businessintelligence.me/projects/performance_tester/performanceTester.html

Veja na imagem abaixo como é “gritante” a diferença de performance entre um Array comum e um Vector:
as3-vector-array-performance-tester

Conteúdo relacionado:
Documentação do Vector
Teste de performance
Post sobre Vector por: Mike Chambers
Post sobre Vector por: DaveOnCode
Post sobre Vector por: The Back Button

Funções do C++ úteis para Arduino

Posted in Arduino, C++, Electronics on maio 16th, 2009 by Bruno Soares – Be the first to comment

Para aqueles que sempre gostaram de eletrônica e que agora se envolveram com eletrônica digital, vai uma diga bem interessante.
Nós sabemos que para programar para microcontroladores é necessário entender um pouco de C, C++ ou até mesmo Assembly, e isso pode ser uma barreira para aqueles que tem interesse mas não dominam tais linguagens, então vai ai um site que pode ajudar muito a encontrar as funções necessárias para se manipular string, arrays, converter tipos, alocar memória, efetuar operações matemáticas entre outros:
http://www.cplusplus.com/

Na verdade o que o programa compilador do código Arduino faz é simplesmente converter o código “Arduino” para C++, compilar e gravar no microcontrolador. Por isso que podemos utilizar muitas funções do C++ para o Arduino. Para conferir entre no diretório /applet (sua aplicação/applet) e abra o arquivo com o mesmo nome do seu “.pde” só que com a extensão “.cpp”.

Vou listar aqui algumas funções bem úteis:

Conversão de tipos:
atof
– Converte string para double
atoi – Converte string  para integer
itoa – Converte integer para string
atol – Converte string  para long integer
strtod – Converte string  para double
strtol – Converte string  para long integer
strtoul – Converte string  para unsigned long integer
Obs.: Você pode encontrar mais funões de conversão de tipos no site da Arduino:
char(), byte(), int(), long() e float()

Manipulação dinâmica da memória:
calloc – Aloca espaço na memória para um array
free – Desaloca espaço na memória

Busca e ordenação:
bsearch – Busca binária em arrays
qsort – Ordena elementos de um array

Operações matemáticas (Math):
atan2 – Calcula o arco tangente com dois parâmetros
log – Calcula o logaritmo natural
log10 – Calcula o logaritmo comum
floor – Arredonda um valor para baixo

Manipulação de strings (char*):
strcat – Concatena strings
strncat – Concatena caracteres com base nas suas posições
strchr – Localiza a primeira ocorrência do caracter em uma string
strstr – Localiza a primeira ocorrência de uma string dentro de outra

Vale a pena conferir e dar uma estudada.

Conteúdo relacionado:
Referência
Arduino

Classe C++ para controlar Display de 7 seguimentos

Posted in Arduino, C++, Electronics on maio 5th, 2009 by Bruno Soares – 4 Comments

No meio de um experimento com Display de 7 seguimentos e Arduino, achei melhor criar uma classe para tratar dos números exibidos no Display.
Deixo aqui a Classe e um exemplo de como usá-la.

Configuração de pinos (Digital) utilizada no exemplo:
7 Segments LED Display - Pin Configuration
Obs.: Está configuração pode ser modificada no momento em que é criada a instância da classe “Simple7Segments” (Simple7Segments display(2, 3, 4, 5, 6, 7, 8, 9);).

Simple7Segments.h

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
/**
 * Simple seven segments LED display
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


#ifndef Simple7Segments_h
#define Simple7Segments_h

#include <inttypes.h>

class Simple7Segments
{
  private:
    uint8_t *_pins;
    uint8_t _count;
 
  public:
    Simple7Segments(uint8_t, uint8_t, uint8_t, uint8_t,
      uint8_t, uint8_t, uint8_t, uint8_t);
    void showNumber(char);
    void showAll();
    void clearAll();
};

#endif

Simple7Segments.cpp

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
/**
 * Simple seven segments LED display
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


#include "Simple7Segments.h"
#include "WProgram.h"

#include <inttypes.h>

#define QUANTITY_PINS 8

Simple7Segments::Simple7Segments(uint8_t a, uint8_t b, uint8_t c, uint8_t d,
  uint8_t e, uint8_t f, uint8_t g, uint8_t pd)
{
  // Alocate memory for Array _pins
  _pins = (uint8_t*)calloc(QUANTITY_PINS, 4);
 
  // Transfer parameters to array pins
  _pins[0] = a;
  _pins[1] = b;
  _pins[2] = c;
  _pins[3] = d;
  _pins[4] = e;
  _pins[5] = f;
  _pins[6] = g;
  _pins[7] = pd;
 
  // Set pin mode to all
  for (_count = 0; _count < QUANTITY_PINS; _count++)
  {
    pinMode(_pins[_count], OUTPUT);
  }
}

void Simple7Segments::showNumber(char letter)
{
  clearAll();
  switch (letter)
  {
    case '0':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[2], HIGH);
      digitalWrite(_pins[3], HIGH);
      digitalWrite(_pins[4], HIGH);
      digitalWrite(_pins[5], HIGH);
    break;
    case '1':
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[2], HIGH);
    break;
    case '2':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[6], HIGH);
      digitalWrite(_pins[4], HIGH);
      digitalWrite(_pins[3], HIGH);
    break;
    case '3':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[6], HIGH);
      digitalWrite(_pins[2], HIGH);
      digitalWrite(_pins[3], HIGH);
    break;
    case '4':
      digitalWrite(_pins[5], HIGH);
      digitalWrite(_pins[6], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[2], HIGH);
    break;
    case '5':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[5], HIGH);
      digitalWrite(_pins[6], HIGH);
      digitalWrite(_pins[2], HIGH);
      digitalWrite(_pins[3], HIGH);
    break;
    case '6':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[2], HIGH);
      digitalWrite(_pins[3], HIGH);
      digitalWrite(_pins[4], HIGH);
      digitalWrite(_pins[5], HIGH);
      digitalWrite(_pins[6], HIGH);
    break;
    case '7':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[2], HIGH);
    break;
    case '8':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[2], HIGH);
      digitalWrite(_pins[3], HIGH);
      digitalWrite(_pins[4], HIGH);
      digitalWrite(_pins[5], HIGH);
      digitalWrite(_pins[6], HIGH);
    break;
    case '9':
      digitalWrite(_pins[0], HIGH);
      digitalWrite(_pins[1], HIGH);
      digitalWrite(_pins[2], HIGH);
      digitalWrite(_pins[5], HIGH);
      digitalWrite(_pins[6], HIGH);
    break;
  }
}

void Simple7Segments::clearAll()
{
  for (_count = 0; _count < QUANTITY_PINS; _count++)
  {
    digitalWrite(_pins[_count], LOW);
  }
}

void Simple7Segments::showAll()
{
  for (_count = 0; _count < QUANTITY_PINS; _count++)
  {
    digitalWrite(_pins[_count], HIGH);
  }
}

SevenSegmentsExample.pde

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
/**
 * Simple seven segments LED display
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


#include "Simple7Segments.h"

#define LED_FINISH 12
#define DELAY 500

unsigned int count = 0;

// Define variable display.
// The numbers are the pins of the display
Simple7Segments display(2, 3, 4, 5, 6, 7, 8, 9);

void setup()
{
  pinMode(LED_FINISH, OUTPUT);
}

void loop()
{
  char* buffer;
  itoa(count, buffer, 10);
  display.showNumber(buffer[0]);
  delay(DELAY);
  if (++count == 10)
  {
    count = 0;
    finish();
  }
}

void finish()
{
  digitalWrite(LED_FINISH, HIGH);
  delay(50);
  digitalWrite(LED_FINISH, LOW);
  delay(50);
  digitalWrite(LED_FINISH, HIGH);
  delay(50);
  digitalWrite(LED_FINISH, LOW);
}

Linha 17) Cria uma instância da classe Simple7Segments passando no construtor os pinos correspondentes as posições no display.
Linha 27) Converte o valor da variável contadora (count) para char* (“String”).
Linha 28) Chama o método showNumber da instância display passando o número a exibir.
Linha 37) Método chamado quando uma contagem de 0 à 9 chega ao fim (pisca o LED).

Faça o download do código fonte completo aqui.

Conteúdo relacionado:
Arduino: http://www.arduino.cc/
Mais fotos: http://www.flickr.com/photos/bsoares/sets/72157617609513978/
Display de 7 seguimento na Wikipedia: http://en.wikipedia.org/wiki/Seven-segment_display

Controlando a Arduino com PHP via porta serial

Posted in Arduino, Electronics, PHP on maio 1st, 2009 by Bruno Soares – 63 Comments

É isso mesmo, o PHP pode escrever ou ler dados da porta serial, e com isso podemos controlar a Arduino.

Você pode ligar o seu ar-condicionado, cafeteira, luz, etc… via Internet, e de uma forma bem simples.
Serialproxy também é uma ótima forma de se conectar a Arduino via Internet, utilizei no projeto Twitter Hardware, mas este post é sobre PHP, então vamos lá.

Arduino + PHP Diagram

Vou utilizar o exemplo descrito no post “Controlando Led RGB com Arduino e Processing” (trocando o Processing pelo PHP).

A função fopen do PHP da suporte a escrita na porta serial:

1
2
3
$port = fopen('COM2', 'w');
fwrite($port, '1');
fclose($port);

Linha 1) Abre a conexão com a COM2 (porta serial onde a minha Arduino está conectada).
Linha 2) Escreve na porta
Linha 3) Fecha a conexão

Código PHP do exemplo:

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
<?php
/**
 * Arduino + PHP
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


$color = $_REQUEST['color'];

if (isset($color) && !empty($color)) {
    $color = hexdec($color);
    $message = '^' . $color . '$';

    // USB Serial Port (COM2)
    $portAddress = 'COM2';
   
    // Open connection on port
    $port = fopen($portAddress, 'w');
   
    // Necessary when the Arduino reset after the connection
    sleep(2);
   
    // Send chars
    fwrite($port, $message);
   
    // Close connection
    fclose($port);
}
?>

Linha 12) Converte a cor de hexadecimal para decimal.
Linha 13) Coloca os caracteres que indicam o inicio e fim da mensagem.
Linha 16) Define a variável com o endereço da porta (no meu caso COM2).
Linha 19) Abre a “conexão” com a porta serial.
Linha 22) Pausa o código por 2 segundo, pois a Arduino costuma reiniciar quando é feita uma conexão a ela.
Linha 25) Escreve a mensagem com a cor na porta serial.
Linha 28) Fecha a “conexão” com a porta serial.

Update 09/07/2009:
Lendo dados:
Tenho recebido diversos e-mails e até alguns comentários de pessoas que precisam ler dados de um sensor, potenciometro, ou qualquer coisa conectada a Arduino, e isso via PHP. Então vamos lá, preparei um código que faz isso de forma fácil.
Acredito que um problema que o pessoal tem tido com esta tarefa é: você deve se dar tempo para a mensagem chegar a Arduino (não que isso seja muito tempo), e também para que a Arduino consiga responder:

Código Arduino:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * Arduino + PHP
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


#define ANALOG_PIN 4

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

void loop()
{
  if (Serial.available() > 0) {
    if (Serial.read() == '1')
      Serial.print(analogRead(ANALOG_PIN), DEC);
  }
}

Código PHP:

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
<?php
/**
 * Arduino + PHP
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


// Conecta na porta
$port = fopen('COM2', 'w+');

// Em alguns casos a Arduino pode reiniciar, por isso
// é bom esperar para enviar informação depois de conectar
sleep(2);

// Envia "1" para o programa na Arduino saber que deve responder
fwrite($port, '1');

// Espera para que o dado enviado pelo PHP chegue até a Arduino
sleep(1);

// Agora que a Arduino "Provavelmente já respondeu", pega
// o valor da resposta
echo fgets($port);

// Fecha a conexão com a porta
fclose($port);
?>

O que esse código faz?
A Arduino fica em loop esperando receber pela porta serial o numero 1, assim que recebido ela lê a voltagem do pino analógico 4 e escreve na porta serial.
O PHP abre a porta serial, escreve “1″, e espera para ler novamente, quando lê novamente encontra o valor do pino analógico 4, assim como a Arduino escreveu.
Bom, espero que este exemplo acabe com as dúvidas do pessoal que precise ler dados da Arduino :)

Observações:
Caso você precise fazer um projeto onde muitos usuário vão acessar o script que escreve na porta serial, você deve implementar uma fila, de forma a não ter um usuário escrevendo na porta ao mesmo tempo que outro, isto geraria um erro.
O código fonte escrito para a Arduino é o mesmo do post Controlando Led RGB com Arduino e Processing

Faça o download do código fonte aqui.

Conteúdo relacionado:
Arduino: http://www.arduino.cc/
Referência: http://www.arduinoprojects.com/?q=node/10
jQuery ColorPicker: http://www.eyecon.ro/colorpicker/

Controlando Led RGB com Arduino e Processing

Posted in Arduino, Electronics, Processing on abril 26th, 2009 by Bruno Soares – 60 Comments

Vamos a um exemplo de uso da Arduino onde controlo um Led RGB de quatro terminais através da porta serial.
Para enviar as cores através da serial usaremos processing.

Para combinar as cores de um Led RGB é necessário variar a voltagem em cada terminal, e para isso a Arduino conta com 6 pinos PWM (Pulse Width Modulation, Modulação por largura de pulso), somente 3 são necessários, pois o quarto terminal deve ser conectado a GND (terra).

No exemplo conectamos os pinos da seguinte forma:

  • Terminal Vermelho no pino 9 (utilizando resistência de 150 Ohm)
  • Terminal Azul no pino 10 (utilizando resistência de 90 Ohm)
  • Terminal Verde no pino 11 (utilizando resistência de 90 Ohm)
  • Terminal terra no pino GND

Obs.: Como não tinha em mãos os resistores liguei os terminais diretamente aos pinos da arduino, mas é importante ressaltar que isso pode danificar o seu led e com certeza reduzir o seu tempo de vida útil.

Espesificação do Led usado:

  • Diâmetro: 5mm.
  • Bulbo: transparente.
  • Intensidade Luminosa: 8000 mcd.
  • Voltagem: vermelho = 1.9 ~ 2.4v; verde e azul = 3.2 ~ 3.6v.
  • Corrente: 20mA.
  • Ângulo: 20 ~ 25º.
  • 5mm rgb led color especification

Vamos ao código utilizado na arduino:

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
/**
 * Controller LED RGB
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


#define START_COLOR_CHAR '^'
#define END_COLOR_CHAR '$'
#define COLOR_SIZE 8
#define PIN_RED 9
#define PIN_GREEN 11
#define PIN_BLUE 10

char serialMessage[COLOR_SIZE];
unsigned int readChar;
unsigned int count;
unsigned long color;
unsigned int r;
unsigned int g;
unsigned int b;
boolean readingSerial;

void setup() {
  Serial.begin(9600);
  readingSerial = false;
}

void loop() {
  if (Serial.available() > 0 && !readingSerial) {
    if (Serial.read() == START_COLOR_CHAR) {
      serialReadColor();
    }
  }
}

void serialReadColor() {
  readingSerial = true;
  count = 0;
 
  iniReading:
  if (Serial.available() > 0) {
    readChar = Serial.read();
    if (readChar == END_COLOR_CHAR || count == COLOR_SIZE) {
      goto endReading;
    } else {
      serialMessage[count++] = readChar;
      goto iniReading;
    }
  }
  goto iniReading;
 
  endReading:
  readingSerial = false;
  serialMessage[count] = '\0';
 
  setColor(serialMessage);
}

void setColor(char* value)
{
  // Convert Char* to Long
  color = atol(value);
 
  // Extract RGB
  r = color >> 16 & 0xFF;
  g = color >>  8 & 0xFF;
  b = color >>  0 & 0xFF;
 
  // Send values to analog pins
  analogWrite(PIN_RED, r);
  analogWrite(PIN_GREEN, g);
  analogWrite(PIN_BLUE, b);
}

Linha 25) Iniciamos a conexão com a porta serial na velocidade 9600.
Linha 30) Verifica se existe mensagem na porta serial.
Linha 32) Chama o método serialReadColor para iniciar a leitura da serial.
Linha 57) Envia o texto lido entre os caracteres ^ e $ para o método setColor.
Linha 63) Converte o valor obtido para Long.
Linha 66 à 68) Extrai a quantidade de Red, Green e Blue da cor.
Linha 71 à 73) Envia a voltagem para os pinos correspondêntes a cada cor.

Conclusão: A Arduino está programada para receber a cor em formato numérico entre dois caracteres que identificam o inicio e o fim da cor (^ para o inicio e $ para o fim). Sendo assim se enviarmos ^16711680$ o LED ficaria vermelho, pois o número 16711680 é o correspondente á 0xFF0000 (hexadecimal).

Agora o código do Processing:

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
/**
 * Controller LED RGB
 *
 * @author  Bruno Soares
 * @website www.bsoares.com.br
 */


import processing.serial.*;

Serial port;

void setup() {
  size(100, 150);
  noStroke();
 
  // Background
  colorMode(HSB, 100);
  for (int i = 0; i < 100; i++) {
    for (int j = 0; j < 100; j++) {
      stroke(i, j, 100);
      point(i, j);
    }
  }
 
  // Select port
  println(Serial.list());
  port = new Serial(this, Serial.list()[1], 9600);
}

void draw() {
  // Only to enable the method mouseDragged
}

void mouseClicked() {
  processColor();
}

void mouseDragged() {
  processColor();
}

void processColor() {
  color c = get(mouseX, mouseY);
  noStroke();
  fill(c);
  rect(0, 100, 100, 50);
  sendColorToSerial(c);
}

void sendColorToSerial(color colour) {
  // Get HEX
  String hexColor = hex(colour, 6);
 
  // Convert HEC to Number
  long numColor = unhex(hexColor);
 
  // Send color number to serial port
  port.write("^" + numColor + "$");
}

A responsabilidade do Processing é se comunicar com a Arduino via porta serial e enviar as cores, pois com o processing podemos criar um aplicativo onde fica facíl selecionar a cor desejada.
Na linha 50 (método sendColorToSerial), o processing processa o objeto Color, obtendo o seu Hexa, transformando em um número do tipo long, concatenando esse número com os caracteres ^ e $, e finalmente enviando este dado formatado para a porta serial para a Arduino fazer o que está a sua responsabilidade.

É isso ai, fica ai mais uma dica ;-)

Faça o download do código fonte aqui.

Conteúdo relacionado:

Twitter Hardware

Posted in Adobe AIR, Arduino, Electronics, Projects on abril 18th, 2009 by Bruno Soares – 4 Comments

Twitter Hardware é um projeto que utiliza a placa Arduino e um Display LCD para exibir as mensagens do Twitter. Para que a Arduino tenha as mensagens escrevi um software em Adobe AIR / Flex, que se conecta ao Serial Proxy enviando o conteúdo para a Arduino.

Arduino:

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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/**
 * Twitter Hardware
 *
 * @author   Bruno Soares
 * @link     http://www.bsoares.com.br
 * @language Arduino / C++
 */


// Includes
#include <LiquidCrystal.h>

// Defines
#define DEBUG_PIN                 13

#define COMMAND_RIGHT             0
#define COMMAND_LEFT              1
#define QUANTITY_COMMANDS         2

#define ANALOG_PIN_COMMAND_RIGHT  0
#define ANALOG_PIN_COMMAND_LEFT   1
#define ANALOG_PIN_SPEED          2

#define MESSAGE_START             94  //  94 = ^
#define MESSAGE_END               126 // 126 = ~

#define SERIAL_BOUND              9600

#define LCD_LINES                 2
#define LCD_COLUMNS               16

#define ANIMATE_CMD_WIDTH         4

// Global variables
unsigned long time;

// LiquidCrystal display with:
// rs on pin 12
// rw on pin 11
// enable on pin 2
// dbs 3, 4, 5, 6, 7, 8, 9, 10
LiquidCrystal lcd(12, 11, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Analog configuration
unsigned int analogCmd[QUANTITY_COMMANDS];
unsigned int analogSpeed = 0;

// Control commands
boolean changingSpeed = true;
boolean readingSerial = false;
boolean pressedCmd[QUANTITY_COMMANDS];
unsigned int lastSpeed = 0;

// Massages
char currentMessage[400] = "No messages.";
unsigned int currentMessageLenght;

// ------------------------------------------------------------------------ \\
// Program
void setup() {
  Serial.begin(SERIAL_BOUND);
  pinMode(DEBUG_PIN, OUTPUT);
 
  smartDelay(10);
  lastSpeed = analogSpeed;
 
  presentation();
  changingSpeed = false;
}

void loop() {
 
  // Show current message
  clearLcd();
  writeLongTextInLcd(currentMessage);
  smartDelay(applySpeed(7000));
 
}

void presentation() {
  lcd.clear();
  writeLongTextInLcd("Twitter Hardwareby Bruno Soares");
  smartDelay(3000);
}

void refreshAnalogVars() {
  analogCmd[COMMAND_RIGHT] = analogRead(ANALOG_PIN_COMMAND_RIGHT);
  analogCmd[COMMAND_LEFT]  = analogRead(ANALOG_PIN_COMMAND_LEFT);
  analogSpeed              = analogRead(ANALOG_PIN_SPEED);
}

// ------------------------------------------------------------------------ \\
// [INI] Commands
void detectCommand() {
  unsigned int i = 0;
  while (i < QUANTITY_COMMANDS) {
    if (analogCmd[i] > 500 && !pressedCmd[i]) {
      executeCommand(i);
    }
    i++;
  }
}

void executeCommand(int command) {
  pressedCmd[command] = true;
  digitalWrite(DEBUG_PIN, 1);
 
  if (command == 0) {
    animateNext();
  } else {
    animatePrevious();
  }
 
  currentMessage[0] = 'L';
  currentMessage[1] = 'o';
  currentMessage[2] = 'a';
  currentMessage[3] = 'd';
  currentMessage[4] = 'i';
  currentMessage[5] = 'n';
  currentMessage[6] = 'g';
  currentMessage[7] = '\0';
  currentMessageLenght = 7;
 
  Serial.print(command);
 
  while (analogCmd[command] > 500) {
    smartDelay(4);
  }
 
  pressedCmd[command] = false;
  digitalWrite(DEBUG_PIN, 0);
}

void animateNext()  {
  for (unsigned int i = 0; i < LCD_COLUMNS + ANIMATE_CMD_WIDTH; i++) {
    if (i < LCD_COLUMNS) {
      lcd.setCursor(i, 0);
      lcd.print(">");
      lcd.setCursor(i, 1);
      lcd.print(">");
    }
    delay(5);
    if (i - ANIMATE_CMD_WIDTH >= 0) {
      lcd.setCursor(i - ANIMATE_CMD_WIDTH, 0);
      lcd.print(" ");
      lcd.setCursor(i - ANIMATE_CMD_WIDTH, 1);
      lcd.print(" ");
    }
    delay(30);
  }
}

void animatePrevious() {
  for (int i = LCD_COLUMNS; i >= -ANIMATE_CMD_WIDTH; i--) {
    if (i >= 0 && i < LCD_COLUMNS) {
      lcd.setCursor(i, 0);
      lcd.print("<");
      lcd.setCursor(i, 1);
      lcd.print("<");
    }
    delay(5);
    if (i + ANIMATE_CMD_WIDTH < LCD_COLUMNS && i + ANIMATE_CMD_WIDTH >= 0) {
      lcd.setCursor(i + ANIMATE_CMD_WIDTH, 0);
      lcd.print(" ");
      lcd.setCursor(i + ANIMATE_CMD_WIDTH, 1);
      lcd.print(" ");
    }
    delay(30);
  }
}
// [END] Commands

// ------------------------------------------------------------------------ \\
// [INI] Messages
void detectSerialMessage() {
  if (Serial.available() > 0 && !readingSerial) {
    if (Serial.read() == MESSAGE_START) {
      serialReadMessage();
    }
  }
}

void serialReadMessage() {
  digitalWrite(DEBUG_PIN, 1);
  readingSerial = true;
  currentMessageLenght = 0;
 
  iniReading:
  if (Serial.available() > 0) {
    unsigned int _char = Serial.read();
    if (_char == MESSAGE_END) {
      goto endReading;
    } else {
      currentMessage[currentMessageLenght++] = _char;
      delay(2);
      goto iniReading;
    }
  }
  goto iniReading;
 
  endReading:
  currentMessage[currentMessageLenght] = '\0';
  digitalWrite(DEBUG_PIN, 0);
  readingSerial = false;
}
// [END] Messages

// ------------------------------------------------------------------------ \\
// [INI] LCD manipulation
void writeLongTextInLcd(char text[]) {
  writeInit:
  clearLcd();
  unsigned int cml = currentMessageLenght;
  int loops = -1;
  unsigned int chars = 1;
  while (text[loops++ + 1] != 0) {
    if (chars == 17) {
      lcd.setCursor(0, 1);
      smartDelay(applySpeed(600));
      if (cml != currentMessageLenght) goto writeInit;
    } else if (chars == 33) {
      smartDelay(applySpeed(3000));
      clearLcd();
      if (cml != currentMessageLenght) goto writeInit;
      chars = 1;
    }
    lcd.print(text[loops]);
    smartDelay(applySpeed(60));
    if (cml != currentMessageLenght) goto writeInit;
    chars++;
  }
  Serial.print(2);
}

void writeInLcd(char text[], int quantity) {
  for (unsigned int i = 0; i < quantity; i++) {
    if (text[i] == 0) {
      do {
        lcd.print(" ");
      } while (i++ < quantity);
      break;
    } else {
      lcd.print(text[i]);
    }
  }
  delay(2);
  //smartDelay(5);
}

void clearLcd() {
  lcd.setCursor(0, 0);
  smartDelay(5);
  lcd.print("                ");
  smartDelay(applySpeed(400));
  lcd.setCursor(0, 1);
  smartDelay(5);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  smartDelay(applySpeed(0));
}
// [END] LCD manipulation

// ------------------------------------------------------------------------ \\
// [INI] Speed
void detectChangeSpeed() {
  if (changingSpeed) return;
  if (!(lastSpeed < analogSpeed + 6 && lastSpeed > analogSpeed - 6)) {
    showGraderSpeed();
  }
}

void showGraderSpeed() {
  changingSpeed = true;
  digitalWrite(DEBUG_PIN, 1);
  lcd.clear();
  writeInLcd("Speed:", LCD_COLUMNS);
 
  // Create display velocity
  showSpeed:
  for (unsigned int i = 0; i < 10; i++) {
    int charsSpeed = map(analogSpeed, 0, 1023, 0, LCD_COLUMNS);
    int percentSpeed = map(analogSpeed, 0, 1023, 0, 100);
    char* percentString;
    itoa(100 - percentSpeed, percentString, 10);
    percentString = strcat(percentString, "%");
    lcd.setCursor(7, 0);
    writeInLcd(percentString, 4);
    charsSpeed = LCD_COLUMNS - charsSpeed;
    lcd.setCursor(0, 1);
    for (unsigned int x = 1; x < LCD_COLUMNS + 1; x++) {
      if (x <= charsSpeed) {
        lcd.print(">");
      } else {
        lcd.print(" ");
      }
    }
    lastSpeed = analogSpeed;
    smartDelay(40);
  }
 
  // Verify
  for (unsigned int i = 0; i < 10; i++) {
    if (!(lastSpeed < analogSpeed + 6 && lastSpeed > analogSpeed - 6)) {
      goto showSpeed;
    }
    smartDelay(40);
  }
 
  // Finalize
  lcd.clear();
  changingSpeed = false;
  digitalWrite(DEBUG_PIN, 0);
}

long applySpeed (unsigned int value) {
  return map(analogSpeed, 0, 1023, 0, value);
}

// [END] Speed

// ------------------------------------------------------------------------ \\
// [INI] Milliseconds controller
void smartDelay(int milliseconds) {
  if (milliseconds < 2) {
    noDelayFunctions();
    delay(milliseconds);
    return;
  }
  do {
    delay(1);
    milliseconds -= noDelayFunctions();
    milliseconds -= 1;
  } while (milliseconds > 0);
}

int noDelayFunctions() {
  time = millis();
 
  // [INI] No delay functions here
  refreshAnalogVars();
  detectChangeSpeed();
  detectCommand();
  detectSerialMessage();
  // [END] No delay functions here
 
  return millis() - time;
}
// [END] Milliseconds controller

ActionScript (usando Serialproxy):

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
package br.com.bsoares.air.airtwitterhardware
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.net.Socket;
   
    /**
     * Message Control
     * Control messages between the Arduino and in AIR
     * Application using Serial Proxy.
     *
     * @author Bruno Soares
     * @link   http://www.bsoares.com.br
     */

    public class MessageControl extends EventDispatcher
    {
        // Properties
        private var _host:String;
        private var _port:uint;
        private var _socket:Socket;
        private var _messages:Array;
        private var _currentMessage:uint;
        private static var _instance:MessageControl;
        private static var _allowInstantiation:Boolean = false;
       
        // Constructor
        public function MessageControl()
        {
            if (!_allowInstantiation)
                throw new Error("Use instance property (this is a Singleton Class).");
            init();
        }
       
        // Logic
        private function init():void
        {
            _host = "127.0.0.1";
            // COM2
            _port = 5332;
            _messages = [ ];
            _currentMessage = 0;
            socketConnect();
        }
       
        private function socketConnect():void
        {
            _socket = new Socket();
            _socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
            _socket.addEventListener(Event.CLOSE, onSocketClose);
            _socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
            _socket.connect(_host, _port);
        }
       
        private function processData(data:String):void
        {
            trace("Arduino command:", data);
            switch (true)
            {
                // Send next message
                case data == "2" || data == "0":
                {
                    sendNextMessage();
                    break;
                }
                // Send previous message
                case data == "1":
                {
                    sendPreviousMessage();
                    break;
                }
            }
        }
       
        private function sendMessage(message:String):void
        {
            if (message == null || message == "")
                return;
           
            trace("Message:", message);
           
            // MESSAGE_START
            _socket.writeUTFBytes("^");
            _socket.flush();
           
            // MESSAGE
            for (var i:uint = 0; i < message.length; i++)
            {
                _socket.writeUTFBytes(message.charAt(i));
            }
            _socket.flush();
           
            // MESSAGE_END
            _socket.writeUTFBytes("~");
            _socket.flush();
           
            dispatchEvent(new Event(Event.CHANGE));
        }
       
        private function sendNextMessage():void
        {
            _currentMessage = _currentMessage == _messages.length - 1 ? 0 : _currentMessage + 1;
            sendMessage(_messages[_currentMessage]);
        }
       
        private function sendPreviousMessage():void
        {
            _currentMessage = _currentMessage == 0 ? _messages.length - 1 : _currentMessage - 1;
            sendMessage(_messages[_currentMessage]);
        }
       
        // Events
        private function onSocketData(event:ProgressEvent):void
        {
            while (_socket.bytesAvailable > 0)
                processData(_socket.readUTFBytes(1));
        }
       
        private function onSocketClose(event:Event):void
        {
            socketConnect();
        }
       
        private function onSocketIoError(event:IOErrorEvent):void
        {
            trace(event.text);
        }
       
        // Getters and Setters
        public static function get instance():MessageControl
        {
            if (_instance == null) {
                _allowInstantiation = true;
                _instance = new MessageControl();
                _allowInstantiation = false;
            }
            return _instance;
        }
       
        public function set messages(value:Array):void
        {
            _currentMessage = 0;
            _messages = value;
            if (_messages.length > 0)
                sendMessage(_messages[0]);
        }
       
        public function get messages():Array
        {
            return _messages;
        }
       
        public function get currentMessage():uint
        {
            return _currentMessage;
        }
    }
}

Esquemático:
twitterhardware-schematic

Baixe o código fonte aqui.

Conteúdo relacionado:
Mais fotos: http://www.flickr.com/photos/bsoares/sets/72157616923436133/
Fritzing: http://fritzing.org/
Arduino: http://arduino.cc/
Adobe AIR: http://www.adobe.com/products/air/
Adobe Flex: http://www.adobe.com/products/flex/
LiquidCrystal Library: http://arduino.cc/en/Reference/LiquidCrystal?from=Tutorial.LCDLibrary
Twitter: http://twitter.com/