Archive for abril, 2009

Controlando Led RGB com Arduino e Processing

Posted in Arduino, Electronics, Processing on abril 26th, 2009 by Bruno Soares – 61 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/

JPGEncoder (AS3) com AMFPHP

Posted in ActionScript 3.0, Flash, PHP, Remoting on abril 7th, 2009 by Bruno Soares – 3 Comments

Tenho notado pelo Google Analytics que pessoas chegam ao blog procurando por AMFPHP, encode de imagens criadas no flash, salvar imagem com Flash + AMFPHP e outros critérios de busca. E por isso me sinto na obrigação de escrever algo sobre isto.

Exemplo:

This movie requires Flash Player 9

Vou demonstrar exatamente o que o título do post propõe (Criar imagens no Flash com ActionScript 3, encodar essas imagens com a classe JPGEncoder presente na biblioteca as3corelib e salvar como um arquivo .jpg utilizando o AMFPHP). Já escrevi aqui como fazer isso em FluorineFx (ASP.NET Flash Remoting Gateway).

Suponho que quem esteja interessado em rodar o que está descrito neste tutorial tenha o Apache com PHP instalado, ou algum servidor com suporte. Caso você não tenha recomendo a instalação do XAMPP (é de fácil instalação e tem tudo que um programador precisa).

Configuração do AMFPHP:
A versão que utilizo neste tutorial é a 1.9 beta, mas versões posteriores devem funcionar perfeitamente.
• Baixe o AMFPHP do seguinte link: http://www.amfphp.org/
• Copie o conteúdo do ZIP para o diretório onde você está criando o projeto, recomendo a estrutura de diretórios como mostrada na imagem abaixo:
folders

• Para testar o funcionamento é só acessar o diretório browser do navegador (http://127.0.0.1/www/amf/browser/).

Fique entendido que a responsabilidade de gerar a imagem é do Flash, e o servidor deve apenas receber os binários para gravar em disco. Para gerarmos o código da imagem (binário), vamos utilizar a classe JPGEncoder.
Exemplo:

1
2
3
4
5
6
7
8
import flash.display.BitmapData;
import flash.utils.ByteArray;
import com.adobe.images.JPGEncoder;

var bmpData:BitmapData = new BitmapData(width, height);
bmpData.draw(MEU_MOVIECLIP);
var objJPGEncoder:JPGEncoder = new JPGEncoder(QUALIDADE);
var dadosEncode:ByteArray = objJPGEncoder.encode(bmpData);

Muito simples não? O método draw da classe BitmapData obtém a imagem atual do clip, criamos uma instância da JPGEncoder já passando a qualidade (0 à 100) e por fim “encodamos” o BitmapData utilizando o método encode da nossa instância da JPGEncoder, ele nos retorna um Array de Bytes (flash.utils.ByteArray).

Vamos a parte do actionscript que interessa:

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
private function encode():void
{
    lblMessage.text = "Codificando dados (JPGEncoder.encode)";
   
    var bmpData:BitmapData = new BitmapData(hit.width, hit.height);
    bmpData.draw(target);
    var objJPGEncoder:JPGEncoder = new JPGEncoder(sliderQuality.value);
    var dadosEncode:ByteArray = objJPGEncoder.encode(bmpData);
   
    sendToAmf(dadosEncode);
}

private function sendToAmf(data:ByteArray):void
{
    lblMessage.text = "Enviando dados para o AMF...";
   
    _objService = new NetConnection();
    _objResponder = new Responder(onResultEvent, onStatusEvent);
    _objService.connect(_amfGateway);
    _objService.call("br.com.bsoares.Image.saveDataToFile", _objResponder, data);
}

private function onResultEvent(result:Object):void
{
    lblMessage.text = "Abrindo imagem";
    navigateToURL(new URLRequest("http://blog.bsoares.com.br/articles/jpgencoder_amfphp/generated_images/image.jpg"), "_blank");
}

private function onStatusEvent(event:Event):void
{
    lblMessage.text = "Erro";
}

Agora a classe Image do PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class Image
{
    var $imagePath;
   
    public function __construct ()
    {
        $this->imagePath = "../../../../../generated_images/image.jpg";
    }

    function saveDataToFile($byteArray)
    {
        file_put_contents($this->imagePath, $byteArray->data);
        return $this->imagePath;
    }
}
?>

O PHP só precisa pegar o ByteArray e salvar em um arquivo.

Dica: Para verificar os request usem o Charles Web Debugging Proxy.

É isso ai, qualquer dúvida só postar um comentário.

Conteúdo relacionado:
Código fonte do exemplo: http://blog.bsoares.com.br/articles/jpgencoder_amfphp/jpgencoder-amfphp.zip
AMFPHP: http://www.amfphp.org/
AS3CoreLib: http://code.google.com/p/as3corelib/
Charles: http://www.charlesproxy.com/

Enjoy

Convertendo PNG para SWF com PHP 5

Posted in Flash, PHP on abril 4th, 2009 by Bruno Soares – 1 Comment

Bom para quem não sabe o Adobe Flash consegue uma compressão incrível com arquivos do tipo PNG, mantendo a sua qualidade e transparência. Vou deixar um exemplo de código PHP para fazer essa conversão sem a ajuda de softwares como o png2swf.exe pois a realidade é que muitos servidores (hosts) não permitem que o script PHP rode softwares.

Classe:

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
<?php
/**
 * SwfConvertion
 *
 * @author Bruno Soares
 * @link   http://www.bsoares.com.br
 */

class SwfConvertion
{
    public function __construct () { }
   
    /**
     * image2swf
     *
     * @param  $imagePath String
     * @return void
     */

    public static function image2swf($imagePath, $outputPath = '')
    {
        if ($outputPath == '')
        {
            $outputPath = SwfConvertion::resolveOutputPath($imagePath);
        }
       
        $bitmap = new SWFBitmap(file_get_contents($imagePath));
       
        $shape = new SWFShape();
        $shape->setRightFill($shape->addFill($bitmap));
        $shape->drawLine($bitmap->getWidth(), 0);
        $shape->drawLine(0, $bitmap->getHeight());
        $shape->drawLine(-$bitmap->getWidth(), 0);
        $shape->drawLine(0, -$bitmap->getHeight());
       
        $movie = new SWFMovie();
        $movie->setDimension($bitmap->getWidth(), $bitmap->getHeight());
        $movie->add($shape);
        $movie->save($outputPath);
    }
   
    /**
     *
     *
     * @param $imagePath Object
     * @return String
     */

    private static function resolveOutputPath($imagePath)
    {
        return preg_replace('/^(.+)\.([A-Za-z]{3,4})$/i', '${1}.swf', $imagePath);
    }
}
?>

Exemplo de uso:

1
<?php SwfConvertion::image2swf('imagem_branco.png', 'imagem_branco_2.swf'); ?>

1º Teste:
PNG: imagem_branco.png 167KB
SWF: imagem_branco_2.swf 98KB
41,31% de compressão (menos 69KB)

2º Teste:
PNG: imagem_vermelho.png 309KB
SWF: imagem_vermelho_2.swf 264KB
14,56% de compressão (menos 45KB)

Obs.: É importante que você escolha bem as conversões que deseja fazer, pois em alguns casos o SWF pode ficar mais pesado que o PNG ou seja, se você vai utilizar este script para otimizar suas imagens compare os pesos antes de escolher a definitiva.

Conteúdo relacionado:
PHP Shockwave Flash: http://br2.php.net/manual/pt_BR/book.swf.php
Funções para SWF: http://br2.php.net/manual/pt_BR/ref.swf.php

Enjoy

Flickr – Buscar fotos por Tag com ActionScript

Posted in ActionScript 3.0, Flash on abril 4th, 2009 by Bruno Soares – 2 Comments

Vamos a um exemplo de uso da API do Flickr, bem simples pois a API do Flickr é realmente simples (isso não quer dizer que ela não é poderoza).

Aqui está o resultado de pouco código:

This movie requires Flash Player 9

Em primeiro lugar você vai precisar de uma api_key e pode conseguir neste link “Solicitar uma nova chave API“. Com sua KEY em mão vamos chamar a api buscando uma tag:
http://api.flickr.com/services/rest/?api_key=[SUA-API-KEY]&method=flickr.photos.search&tags=[TAG]

O resultado esperado é este:

1
2
3
4
5
6
7
<rsp stat="ok">
<photos page="1" pages="10" perpage="100" total="1000">
    <photo id="3411384625" owner="[email protected]" secret="74167a8895" server="3374" farm="4" title="_MG_0611" ispublic="1" isfriend="0" isfamily="0" />
    <photo id="3411401933" owner="[email protected]" secret="46c095f827" server="3585" farm="4" title="_MG_0641" ispublic="1" isfriend="0" isfamily="0" />
    ...
</photos>
</rsp>

Vamos entender o link, rest é o formato que você deseja receber a resposta, atualmente o Flickr suporta 5 formatos (REST, XML-RPC, SOAP, JSON e PHP), usamos o rest porque o ActionScript trabalha muito bem com ele (XML). api_key é a chave que você solicitou acima. method, é o método de busca (você pode encontrar muitos no link da API, http://www.flickr.com/services/api/). E por fim tags que é um parâmetro pelo qual você busca.

Agora que temos o XML de resposta podemos construir links para as fotos, para a página de galeria do usuário encontrado, para o perfil do usuário entro outros. O link que eu acho mais importante é o da imagem e para consegui-lo basta concatenar alguns dados presentes no XML dessa forma:

http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}.jpg

http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{secret}_[mstb].jpg

http://farm{farm-id}.static.flickr.com/{server-id}/{id}_{o-secret}_o.(jpg|gif|png)

O que é mstb?
s) quadrado pequeno 75×75.
t) miniatura, 100 no lado mais longo.
m) pequeno, 240 no lado mais longo.
-) médio, 500 no lado mais longo.
b) grande, 1.024 no lado mais longo (existe apenas para imagens originais muito grandes).
o) imagem original, jpg, gif ou png, dependendo do formato de origem.
Na página URLs da origem da foto você encontra tudo bem explicado.

Veja no exemplo abaixo o link (do primeio nó ‘photo’) para a imagem:
http://farm4.static.flickr.com/3374/3411384625_74167a8895.jpg

Agora tudo isso com ActionScript (Load do XML e parse para gerar o link):

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
var apiKey:String = "[SUA-API-KEY]";
var tag:String = "arduino";

var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE, onComplete);
var url:String = "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=" + apiKey + "&tags=" + tag;
trace("URL: ", url);
xmlLoader.load(new URLRequest(url));

function onComplete(event:Event):void
{
    var xmlData:XML = new XML(event.target.data);
    if (xmlData.@stat != "ok")
    {
        trace("ERROR: " + xmlData.err.@msg);
        return;
    }
   
    for (var i:uint = 0; i < xmlData.photos.photo.length(); i++)
    {
        var photo:Object = {
            id:xmlData.photos.photo[i].@id,
            owner:xmlData.photos.photo[i].@owner,
            secret:xmlData.photos.photo[i].@secret,
            server:xmlData.photos.photo[i].@server,
            farm:xmlData.photos.photo[i].@farm,
            title:xmlData.photos.photo[i].@title
        };
        photo.url = "http://farm" + photo.farm + ".static.flickr.com/" + photo.server + "/" + photo.id + "_" + photo.secret + ".jpg";
        trace("Imagem: ", photo.url);
    }
}

Repare que você pode adicionar dois parâmetros no link para controlar a paginação das fotos (page e perpage).

Conteúdo relacionado:
Flickr API: http://www.flickr.com/services/api/
XML ActionScript 3.0: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/XML.html

AIR Install Badge com Google Analytics

Posted in ActionScript 3.0, Adobe AIR on abril 2nd, 2009 by Bruno Soares – Be the first to comment

Já a algum tempo procurei um Air Install Badge com o Google Analytics integrado e acabei encontrando o do Marc’s Musings, mas me pareceu meio “travado” uma vez que é necessário compilar o flash com as informações do seu aplicativo AIR. Sendo assim modifiquei o original para receber via FlashVars o nome da aplicação, versão, endereço do .air, e é claro o código AnalyticsTracker.

O Google Translation Plus (uma aplicação que estou desenvolvendo) já está utilizando o Install Badge modificado, verifique.

Como é mostrado o relatório no Google Analytics:
googleanalytics


Principal trecho de código do Install Badge alterado:

1
2
3
4
// Google Analytics Tracking
if (tracker != null) {
    tracker.trackPageview("/" + appName + "/" + action);
}

Atenção:
Existem muitas variáreis para serem alteradas no arquivo AirAppPage.html, como por exemplo: analyticstracker, airversion, appname, appurl, appid, appversion, etc…

Download do AIR Install Badge com Google Analytics.

Conteúdo relacionado:
AIR Install Badge: http://www.adobe.com/devnet/air/articles/badge_for_air.html
gaforflash: Google Analytics Tracking For Adobe Flash
swfobject: http://code.google.com/p/swfobject/