domingo, 7 de junho de 2009

Operações binárias

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)
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:
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)
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:
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).

// 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:
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++):

// 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:
• Post original: http://blog.bsoares.com.br/flash/operacoes-binarias
Bitwise operation on Wikipedia
 

sexta-feira, 24 de abril de 2009

Boas práticas.


Estou ensaiando de postar aqui algo sobre boas prática a tempos, foi uma das primeiras ideias que tive, pois odeio quando pego um código de algum dev que nem identar o código identa. Enfim, até hoje, NADA. Eu com minha correria diária nunca consegui juntar tudo e postar aqui, hoje falando com o BSoares, ele mencionou um link de boas práticas, imensooo e muito útil, em 5 min vi que tenho mais a aprender sobre boas práticas do que imaginava, isso por que eu idento meu código hein! haha!

Divirtam-se:

http://opensource.adobe.com/wiki/display/flexsdk/Coding+Conventions

E aqui, para quem não sabe o que é identar ;-)

http://pt.wikipedia.org/wiki/Indenta%C3%A7%C3%A3o

Update:
Conversando com o Cássio, acabei conhecendo o blog dele e vendo que ele também postou algo sobre boas práticas. :)

http://blog.devlab.com.br/post/85041607/desenvolvimento-flash-voc-est-fazendo-tudo-errado

Aproveite e conheça o Blog dele, tem muita coisa boa! :D



Se você também souber de algum post ou materia online sobre boas práticas, deixa ai o link nos comentários para outros devs também terem acesso.

[]s!





Twitter Hardware (Arduino)

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

// Includes
#include

// 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):
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:
Post original: http://blog.bsoares.com.br/arduino/twitter-hardware
Mais fotos: http://www.flickr.com/photos/bsoares/sets/72157616923436133/
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

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:




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:
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:
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:
<?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:
Post original: http://blog.bsoares.com.br/remoting/jpgencoder-as3-with-amfphp
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

Flickr - Buscar fotos por Tag com ActionScript 3.0

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:




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:
<rsp stat="ok">
<photos page="1" pages="10" perpage="100" total="1000">
<photo id="3411384625" owner="23534352@N07" secret="74167a8895" server="3374" farm="4" title="_MG_0611" ispublic="1" isfriend="0" isfamily="0" />
<photo id="3411401933" owner="23534352@N07" 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 75x75.
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):
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:
Post original: http://blog.bsoares.com.br/flash/flickr-search-photos-by-tag-with-actionscript
Flickr API: http://www.flickr.com/services/api/
XML ActionScript 3.0: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/XML.html