Cubo de LEDs com NetDuino

Neste artigo iremos entender como funciona o Netduino, como estão dispostas suas saídas digitais e como utilizá-las num projeto de um cubo de LEDs de 3x3x3. Um cubo de LEDs é uma peça decorativa que, através do acendimento sequencial de seus vinte e sete leds, reproduz animações criadas em código - no nosso caso, em linguagem C# (.NET Microframework). Iremos conhecer algumas das classes que atuam sobre a interface do Netduino, além de entender algumas particularidades e limitações do .NET Microframework em relação ao .NET Framework.

O .NET Microframework é uma plataforma que lhe da a possibilidade de escrever lógica para micro controladores em uma linguagem de mais alto nível, como C#. A proposta é simples: colocar num microcontrolador um Framework que pudesse controlar funções básicas de entrada e saída de sinal digital de um circuito. As pessoas poderiam construir circuitos baseados neste processador e que poderiam executar funções de controle de hardware via software e tudo programado em C# utilizando um IDE como o Visual Studio! Muitos microcontroladores com .NET Microframework foram lançados desde então, mas poucos com um apelo comercial tangível. Ou os circuitos eram muito caros ou muito complicados (ou ambos).

Foi então que uma empresa de New York chamada Secret Labs criou algo surpreendente. Um microcontrolador com quatorze saídas digitais e seis entradas analógicas (Figura 1), com dois pinos para saída de alimentação, com 3.3 e 5 Volts, respectivamente. A estrutura deste circuito possui a mesma configuração de outro microcontrolador muito popular (que utiliza uma linguagem própria para a lógica) chamado Arduino. Como o novo microcontrolador possuía o .NET Micro framework embebido, juntando os dois nomes chegamos a Netduino.

Netduino (fonte: www.netduino.com)

Figura 1: Netduino (fonte: www.netduino.com)

Netduino

O Netduino é uma plataforma eletrônica Open Source que utiliza o .NET Microframework, que por sua vez, também é Open Source. Todas as fontes do SDK e a arquitetura do circuito estão disponíveis no site (veja o endereço na seção de Links).

O Netduino é surpreendentemente pequeno. Esta entrada prateada “enorme” do lado esquerdo da placa é uma microUSB. Nesta pequena placa de circuito temos:

  • Processador ATMEL 32-bits de 48MHz (ARM7);
  • 128 KB de memória de armazenamento de código;
  • 60 KB de RAM.

Bom, com o que estamos acostumados a ver em configurações de máquinas, 60KB parece um nada de RAM. Mas você vai ver que dá e sobra. Veja na Figura 2 a interface do Netduino:

 Interface do Netduino

Figura 2: Interface do Netduino

Debug em um circuito

Com o Netduino, graças à interface do .NET Microframework com o Visual Studio, você pode colocar breakpoints no código e “debugar” passo-a-passo cada etapa da lógica. Você também pode criar watches, observar variáveis em tempo real, enfim, utilizar todos os recursos de debug do Visual Studio. Sensacional!

Expansões mil

Existem diversas empresas que já fabricam acoplamentos (ou Shields, como são mais conhecidos) que se encaixam perfeitamente nos pinos do Netduino e acrescentam funcionalidades incríveis, como controladores de servo-motores, localizadores GPS, adaptadores para baterias recarregáveis, matrizes de leds, controles tipo joystick etc. Como o Netduino é construído em compatibilidade com seu primo Arduino, todas as expansões para esta plataforma também servem para utilização no Netduino.

Um pouco sobre eletrônica digital

A ciência Eletrônica é divida em duas vertentes: analógica e digital. A eletrônica analógica trabalha com diversos tipos de grandezas elétricas, variando em tensão e corrente elétrica. Já a eletrônica digital, foco do .NET Microframework, trabalha com apenas duas grandezas: 0 e 1. Ou seja, quando temos em uma “saída digital” uma tensão de cinco volts, temos o sinal digital um ou VERDADEIRO. Ao contrário, se não tivermos tensão na saída digital (zero volts), temos o sinal digital zero ou FALSO. Através da manipulação destes valores, temos uma infinidade de aplicações práticas. Podemos inverter um sinal digital através de um circuito inversor; podemos utilizar “portas lógicas” (AND, NAND, OR, NOR, XOR) para manipular o sinal em conjunto com outros sinais digitais; podemos utilizar osciladores de onda quadrada (sequencias de zero e cinco volts) para criarmos som ou efeitos em leds, iluminação etc. Enfim, as possibilidades são inúmeras.

Nota: É comum encontrarmos na literatura digital os termos HIGH (ou H) e LOW (ou L) para os valores binários VERDADEIROS (true) e FALSO (false), respectivamente. Porém, a notação não influi no resultado, que deve ser o mesmo para quaisquer casos.

O que são saídas digitais?

Saídas digitais (ou “digital i/o” como descrito na Figura 2) são saídas de energia (ou sinal digital) programáveis. O conceito é o mesmo que explicamos anteriormente: uma saída digital pode fornecer energia em duas tensões: zero e cinco Volts. Estes valores são os valores padrões para a eletrônica digital. Ou seja: zero volts significa o bit zero (false); cinco volts significa o bit um (true).

Quando desejamos acender um led (Figura 3), por exemplo, devemos ligar seu terminal positivo (chamado de anodo) a uma das saídas digitais do Netduino e seu terminal negativo deve ser ligado a um resistor (componente que diminui a corrente elétrica para não queimar o led) e este deve ser ligado ao terminal Ground (ou gnd), que é o polo negativo (terra) do circuito.

Estrutura de um LED e sua representação gráfica

Figura 3: Estrutura de um LED e sua representação gráfica

Feito isso, devemos “ativar” a saída digital em que o led está ligado, através de um método “Write(true)”. Assim, a saída enviará cinco volts para o led, e este acenderá.

Através do controle da passagem de energia pelas saídas digitais podemos realizar diversas tarefas comuns a circuitos eletrônicos, que vão desde o acendimento sequencial de luzes (que é o objetivo da nossa experiência) até controlar travas, acionar relés, e uma infinidade de aplicações que exigem o controle lógico de correntes elétricas.

O que é um cubo de LEDs?

Um cubo de leds é um conjunto de leds soldados uns aos outros, dispostos em uma forma quadrada e sobrepostos em camadas, o que dá a aparência de um cubo.

Os leds são ligados (soldados) de forma que todos seus terminais negativos (catodos) estejam unidos em uma camada e todos seus terminais positivos (anodos) estejam unidos em uma coluna. Veremos os detalhes da montagem mais adiante, mas é importante que o leitor entenda o processo da lógica que iremos desenvolver em C#, integrando as saídas digitais com os leds.

Quando construirmos o cubo dispondo os leds desta forma, criamos um modo peculiar de endereçar um led, já que teremos que criar uma maneira de acessar os leds individualmente. E como se acende um led em um cubo de leds? Simples: você coloca energia (polo positivo) na coluna do led e abre a terra (polo negativo) da respectiva camada. Isso faz com que o único caminho possível para que a corrente elétrica circule seja através do led, o que faz com que ele acenda.

Entendendo o código do Netduino

Para fazer acontecer a mágica do cubo, precisamos escrever a lógica do circuito. O SDK do Netduino disponibiliza diversas classes para o controle das saídas digitais. Talvez a classe mais importante que iremos trabalhar é a OutputPort. Esta classe possui um construtor com dois parâmetros: um enumerador chamado Cpu.Pin, que contém referências para todas as saídas do Netduino e um booleano, que contém o valor do estado inicial da saída. Esta classe também contém um método chamado Write para “escrever” um dado booleano na saída. Assim, quando criamos uma instância da classe OutputPort e executamos seu método Write passando o parâmetro true, o circuito libera energia para a respectiva saída digital (mais conhecida como pino). Ao contrário, se executamos o mesmo método passando o parâmetro false, o circuito bloqueia a energia no respectivo pino, que passa a conter zero volts.

Desta forma, podemos liberar tensões para os pinos positivos ligados aos polos positivos (colunas) do cubo e liberar passagem de corrente elétrica para o terra com o uso dos pinos do Netduino ligados à base de alguns transistores.

Nota: Um transistor é um componente que atua como uma chave de liga/desliga. Possui três pinos: coletor, emissor e base. A energia flui do coletor para o emissor, desde que haja uma carga positiva na base. Ou seja, se o Netduino aplicar a energia de uma porta digital (pino em estado true) na base de um transistor, o mesmo vai deixar que a energia do coletor flua para o emissor, como se fosse um interruptor de uma lâmpada ligada; quando a energia do pino deixa de chegar à base do transistor (pino em estado false), é como se desligássemos o interruptor, e a energia deixa de passar do coletor para o emissor.

Animação no cubo

Como se anima um cubo? Através de uma sequencia de “imagens” ou “frames”, que são acesas e apagadas em uma taxa maior que 15 quadros por segundo, que é a taxa que o olho humano acredita estar olhando para um objeto em movimento. Até aí, tudo bem. O problema é que nosso cubo tem uma particularidade: apenas uma camada de cada vez pode ser acesa. Se tentarmos acionar os leds de mais de uma camada por vez, todas as camadas ativas no momento em que enviarmos o sinal para a coluna em questão irão acender. Então, como fazer para que uma imagem tridimensional seja “escrita” no cubo? Usando uma técnica chamada multiplexação (do inglês, “multiplexing”), que consiste no seguinte: o olho humano enxerga o mundo também através de diversas “imagens”, tiradas pela retina a uma taxa de 24 quadros por segundo. Se observarmos uma animação que esteja abaixo desta taxa, o cérebro entende o movimento, mas no caso de uma figura estática ela tem aquele efeito de “piscagem” ou, como costumamos dizer no jargão, “flicking”. Ao contrário, se cada imagem piscar acima de 48 quadros por segundo, o olho já pode interpretar que se trata de uma imagem estática. Então aí está o truque: cada camada da figura é acesa e apagada numa velocidade tão grande que dá a impressão ao olho humano de ser uma figura tridimensional. Portanto, cada quadro da animação é uma sequência de “acendimentos” e “apagamentos” sucessivos e, juntando diversos quadros por segundo, temos uma animação no nosso cubo. Concluindo, temos duas animações simultâneas em um cubo: uma da multiplexação, cujo intuito é criar a ilusão de tridimensionalidade e a da animação em si, cujo objetivo é dar a impressão de movimento em um plano 3D.

Construindo o hardware

Bem, agora que você já viu um pouco do conceito das saídas digitais do Netduino e a animação em cubos de leds usando a multiplexação, vamos começar a montar nosso cubo. Vamos soldar os leds de forma que o resultado seja um cubo de 3x3x3, ou seja, três leds de largura por três leds de altura por três leds de profundidade. Depois de pronto este cubo será soldado a uma matriz de contatos, onde todos os outros componentes serão igualmente soldados e ligados entre si, de forma que o circuito funcione de forma correta, conforme o diagrama da Figura 4. Se você não sabe ler diagramas esquemáticos de eletrônica, não se desespere: iremos fazer tudo passo a passo.

Esquema eletrônico do circuito

Figura 4: Esquema eletrônico do circuito

Depois de montado o cubo e todos os componentes estiverem no lugar, vamos colocar os fios que irão ligar o aparato ao Netduino, permitindo que a programação que iremos fazer possa atuar nos componentes eletrônicos. Então, vem a parte do código onde iremos escrever dois métodos e uma classe que irão realizar a execução de animações no cubo, fazer o deploy no Netduino e ver a mágica acontecer!

A lista de materiais para a construção do hardware é a seguinte:

  • Uma matriz de contatos (placa com pontos para solda de uso genérico);
  • 27 LEDs vermelhos comuns;
  • Nove resistores de 220 ohms, 5% (vermelho, vermelho, marrom, dourado);
  • Três resistores de 22K ohms, 5% (vermelho, vermelho, laranja, dourado);
  • Três transistores 2N3904 (NPN de uso geral);
  • Um pedaço de madeira para furação da guia para soldar os leds (como veremos adiante);
  • Fios para ligação, estanho para solda, ferro de solda etc.

Usei um pedaço de madeira para fazer uma matriz de 3x3 furos. Esta matriz me serviu de guia para a soldagem dos leds em cada camada. Veja na Figura 5 como minha matriz ficou. Claro que você não precisa fazer exatamente igual (já que eu fiz uma matriz de 5x5), mas pode usar esta como referência.

Matriz guia para a soldagem dos leds

Figura 5: Matriz guia para a soldagem dos leds

Soldando os LEDs

Antes de começar a disposição dos leds para solda, devemos dobrar o catodo (polo negativo) de todos os leds para que fiquem um apontando para o outro na mesma camada. O catodo de um led, por definição da indústria, é sempre o menor terminal. Veja na Figura 6 como deve ser feita esta dobra.

Dobra do catodo

Figura 6: Dobra do catodo

Depois que tiver dobrado todos os leds, vá colocando os mesmos em linha. Você já pode ir soldando os catodos, conforme mostra a Figura 7.

Arranjo dos LEDs na matriz de furos

Figura 7: Arranjo dos LEDs na matriz de furos

Continue montando todos os LEDs para fazer uma camada, conforme a Figura 8. Lembre-se de soldar todos os catodos de todos os leds, ficando assim um led soldado a seu vizinho.

Sequência de montagem dos leds na camada

Figura 8: Sequência de montagem dos leds na camada

Faça a mesma coisa para as outras duas camadas. Cada camada deve ficar como mostrado na Figura 9.

Camada pronta

Figura 9: Camada pronta

Depois que as três camadas estiverem prontas, coloque-as uma sobre a outra e comece a soldar os anodos (polos positivos) dos leds. Isto irá criar a ligação das colunas do nosso cubo. Lembre-se: cada led será aceso ativando-se a entrada de energia pela coluna e a saída pela camada. É uma boa ideia dar uma torcidinha na ponta dos anodos para que fiquem mais próximos do anodo da camada de baixo, facilitando a soldagem. Tome como exemplo o resultado da soldagem mostrado na Figura 10. Mas lembre-se que os anodos devem ser soldados formando uma coluna, ou seja, cada led da camada de cima deve ter seu anodo soldado ao led na mesma posição na camada de baixo.

Exemplo de soldagem de anodos (colunas)

Figura 10: Exemplo de soldagem de anodos (colunas)

Depois de tudo soldado vamos inserir o cubo na matriz de contatos. Solde os terminais de baixo do cubo nos terminais da matriz, como mostrado na Figura 11.

Soldando o cubo na matriz de contatos

Figura 11: Soldando o cubo na matriz de contatos

Depois de finalizada esta solda, há um pequeno truque a ser feito. Você deve fazer uma solda entre um terminal da placa e cada uma das camadas do cubo, juntando os catodos soldados (que formam cada camada) a um terminal da matriz de contatos. Isto servirá para fazer o contato com os terminais negativos das camadas com a saída para a terra (polo negativo do circuito). Desta forma, ficaremos com três contatos além dos nove contatos soldados anteriormente, totalizando doze pontos de solda. Veja esta ligação na Figura 12.

Pontos das colunas e pontos das camadas

Figura 14: Pontos das colunas e pontos das camadas

Feito isso, é hora de soldar os nove resistores de 220 ohms. Cada resistor vai fornecer resistência à passagem da corrente elétrica no polo positivo de cada coluna, impedindo que o excesso de energia possa queimar um ou mais leds, já que a tensão de operação de cada led é de 1,5 volts e a tensão da saída digital é, novamente, de cinco volts.

Você deve soldar um resistor a cada contato de cada coluna soldada na matriz de contatos. Como os leds mais centrais estão distantes dos contatos dos resistores, use fios soldados para ligar um dos polos do resistor ao contato da sua respectiva coluna. A Figura 13 mostra um exemplo desta ligação. Lembre-se que os resistores não têm polos positivos ou negativos por definição, então você pode ligar a coluna de leds em qualquer terminal do resistor. Outra coisa importante é que eu usei o padrão de ligação iniciando do contato mais inferior e mais à direita; este então passou a ser a coluna um. É importante seguir este padrão, pois o código que usaremos para criar as animações está baseado nesta configuração.

Solda dos resistores às colunas

Figura 13: Solda dos resistores às colunas

Na Figura 14 temos a vista superior dos resistores dispostos em linha na matriz de contatos.

Soldagem dos resistores- vista superior

Figura 14: Soldagem dos resistores- vista superior

Agora, com todos os resistores soldados à matriz de contatos, é hora de ligar os transistores ao circuito. Tenha um pouco mais de cuidado com os transistores, porque eles possuem terminais identificados e precisam ser ligados na posição correta. Veja como você deve liga-los no detalhe da Figura 15, onde identificamos os terminais dos transistores.

Detalhe da instalação dos transistores

Figura 15: Detalhe da instalação dos transistores

Depois de soldados os terminais dos três transistores à matriz de contatos, vamos fazer as seguintes ligações:

  1. Soldar com fios os coletores de cada transistor no terminal de cada camada que soldamos na placa;
  2. Soldar com fios a base em um terminal mais separado do conjunto. Este terminal será soldado aos últimos resistores, que serão à entrada de sinal (energia) para abrir as camadas que deverão conter os transistores que serão acesos na animação.
  3. Soldar com um fio os três emissores dos três resistores em um último terminal, que será ligado ao polo negativo (terra) do circuito.

Veja como fica a ligação depois de pronta na Figura 16.

Soldas dos coletores e dos emissores

Figura 16: Soldas dos coletores e dos emissores

Com esta solda, falta apenas colocar os três resistores de 22k ohms e os fios para ligar o circuito ao Netduino. Vamos colocar os três resistores junto à base de cada transistor. Um resistor para cada base. Estes resistores servem para limitar a corrente elétrica que passa dos pinos do Netduino para a base dos resistores, para que os transistores não se sobrecarreguem e se danifiquem.

Depois, solde os fios que vão ligar ao Netduino, os resistores das camadas que você acabou de soldar, o terra (polo negativo) do circuito (terminal ligado aos três emissores dos transistores) e os nove resistores das colunas (os primeiros que você soldou). O resultado final é mostrado na Figura 17.

Circuito totalmente montado

Figura 17: Circuito totalmente montado

Ligando os cabos

Agora que o circuito está pronto, vamos ligar os fios aos pinos do Netduino. Primeiro, ligue o fio que vem dos emissores dos transistores (aqueles que estão soldados juntos nos três transistores). Este fio é a passagem da corrente para o terra do Netduino. Depois, ligue os fios que vêm dos resistores de 22k ohms (fios das camadas) obedecendo a seguinte ordem:

  • Camada “1” no pino 11.
  • Camada “2” no pino 12.
  • Camada “3” no pino 13.

Estes pinos vão ser acessados via código para que possamos liberar a passagem da corrente para o terra do circuito em cada uma das camadas.

O conceito que estamos usando com esses transistores é chamado de “circuito inversor”. Os pinos 11, 12 e 13 do Netduino vão enviar sinais digitais (zero e cinco volts) pelo polo positivo, mas a nossa intenção com esses pinos é liberar o polo negativo para ativar as respectivas camadas. O transistor que usamos é do tipo NPN, ou seja, Negativo-Positivo-Negativo. Isso significa que quando esse tipo de transistor recebe um sinal positivo na sua base, libera a corrente negativa entre o coletor e o emissor. Então, quando ativarmos os pinos 11, 12 ou 13, a saída da energia do circuito de cada camada para o polo negativo (terra) do Netduino será liberada e a energia que irá entrar através de outro pino irá acender um ou mais leds.

Agora, ligue os fios que estão conectados às colunas do cubo (os nove resistores). Os fios foram ligados segundo a posição das colunas, que numeramos de um a nove; então, ligue os fios nesta ordem nos pinos de 0 a oito do Netduino.

Estes fios são aqueles que levam o sinal digital para alimentar as nove colunas de leds. A junção do sinal digital na entrada e a liberação da camada na saída, vão formar as figuras que usaremos para criar as animações.

Finalmente, o código

Depois de tudo pronto vamos preparar nosso ambiente de programação. Para utilizar o Netduino como plataforma, você deve baixar os seguintes itens:

  • .NET Microframework SDK v4.1;
  • Netduino SDK v4.1.0 (32 ou 64 bits).

Todos esses pré-requisitos estão disponíveis para download no próprio site oficial do Netduino.

Antes de criarmos nosso projeto para o Netduino você precisa conectá-lo a uma porta USB no seu computador. Assim que o sistema operacional reconhecê-lo e instalar todos os drivers do SDK, nosso projeto já pode ser criado.

Abra o Visual Studio e escolha, na aba “Installed Templates”, o tipo de projeto “Netduino Application”, conforme a Figura 18.

Novo projeto para o Netduino

Figura 18: Novo projeto para o Netduino

Nomeie o projeto da forma que preferir. Vou usar o nome “LedCube3” por razões óbvias. Antes de começar a codificar devemos ajustar as propriedades do projeto para que possamos executar o código diretamente no Netduino. Abra as propriedades do projeto e na guia .NET Microframework escolha a opção USB no campo Transport e a opção Netduino_device no campo Device, conforme mostrado na Figura 19.

Configurações de deploy do projeto

Figura 19: Configurações de deploy do projeto

Para começar, vamos criar os objetos da classe OutputPort que irão controlar cada um dos pinos do Netduino que utilizaremos em nosso projeto. Crie variáveis estáticas na classe Program do seu projeto, uma para cada uma das nove colunas e, uma para cada uma das três camadas do cubo. Além disso, crie um array de OutputPort para conter as camadas, conforme mostrado na Listagem 1.

Listagem 1: Pinos de saída do projeto

private static OutputPort led1 = new
OutputPort(Pins.GPIO_PIN_D0, false);
private static OutputPort led2 = new OutputPort(Pins.GPIO_PIN_D1, false);
private static OutputPort led3 = new OutputPort(Pins.GPIO_PIN_D2, false);
private static OutputPort led4 = new OutputPort(Pins.GPIO_PIN_D3, false);
private static OutputPort led5 = new OutputPort(Pins.GPIO_PIN_D4, false);
private static OutputPort led6 = new OutputPort(Pins.GPIO_PIN_D5, false);
private static OutputPort led7 = new OutputPort(Pins.GPIO_PIN_D6, false);
private static OutputPort led8 = new OutputPort(Pins.GPIO_PIN_D7, false);
private static OutputPort led9 = new OutputPort(Pins.GPIO_PIN_D8, false);

private static OutputPort camada1 = new OutputPort(Pins.GPIO_PIN_D11, false);
private static OutputPort camada2 = new OutputPort(Pins.GPIO_PIN_D12, false);
private static OutputPort camada3 = new OutputPort(Pins.GPIO_PIN_D13, false);

private static OutputPort[] camadas = new OutputPort[] { camada1, camada2, camada3 };

Como você pode notar, a classe OutputPort recebe como parâmetro uma enumeração para identificar o pino do Netduino que aquele objeto irá controlar, além de outro parâmetro que diz qual é o estado inicial da saída – true para que a saída inicie ligada, false para que inicie desligada.

Ainda na classe Program vamos adicionar algumas constantes para controlar os intervalos das animações. Será possível entender após você que criar suas próprias animações, que existem certas sequências de quadros que devem ter intervalos menores e outros que devem ter intervalos maiores. Tudo vai depender do estilo da animação e do efeito que se quer criar. Também é interessante criar constantes para a quantidade de repetições, já que também dependendo do efeito que se quer criar, existem animações que podem repetir muitas vezes sem se tornarem cansativas; já outras que devem ser repetidas apenas poucas vezes para não estragar o efeito visual. O código a seguir mostra as constantes que adicionei para este projeto.

private const int tempo = 20;
private const int loopsMin = 5;
private const int loopsMax = 20;

A classe Animation

Como já dissemos antes, uma animação é uma sequência de diversas imagens. Como nosso cubo possui uma resolução muito baixa (“3” por “3” por “3” pixels), iremos criar uma matriz de bits (bool[]) que irá conter três linhas e nove colunas (assim como nosso cubo). Cada imagem (ou frame, como é mais usualmente chamado) será armazenada em um ArrayList que fará o papel de lista de frames para facilitar a execução da animação. O leitor pode estar se perguntado por que preferimos o uso do ArrayList ao invés de um List < bool[]>. A resposta é simples: não preferimos. Isto é uma imposição do .NET Microframework que não implementa o namespace System.Collections.Generic, que contém a implementação de todas as estruturas de listas genéricas. Não é de se estranhar que muitas das funcionalidades a que estamos acostumados no .NET Framework convencional não estejam implementadas no .NET Microframework, já que ele possui apenas cerca de 400 KB de tamanho. Lembre-se: não estamos desenvolvendo para PCs, e sim para micro controladores.

A classe Animation possui duas propriedades: Length (retorna a quantidade de bits em cada frame da animação) e FrameCount (que retorna a quantidade de frames da animação).

O construtor recebe um parâmetro do tipo Int chamado rank, que é o tamanho de cada lado do cubo. Por exemplo, nosso cubo possui três lados com três leds em cada lado (3x3x3). Então o rank das animações para este cubo é três. Isso existe, pois a classe Animation pode ser utilizada para quaisquer tamanhos de cubos de led.

Para encerrarmos nossa classe temos três métodos: GetFrame, que obtém o frame atual da animação ou obtém o frame cujo índice for passado como parâmetro; AddFrame, que adiciona um frame ao final da animação; e InsertFrame, que adiciona um frame no índice que for passado por parâmetro.

Veja a classe completa na Listagem 2.

Listagem 2: Classe Animation

using System;
using Microsoft.SPOT;
using System.Collections;

namespace LedCube3
{
public class Animation
{
private ArrayList _frames;
private int _actualFrame;
private int _rank;
private double _length;

public double Length
{
get { return _length; }
}

public int FrameCount
{
get { return _frames.Count; }
}

public Animation(int rank)
{
_frames = new ArrayList();
_actualFrame = 0;
_rank = rank;
_length = System.Math.Pow(rank, 3);
}

public bool[] GetFrame()
{
if (_actualFrame >= _frames.Count)
_actualFrame = 0;

return (bool[])_frames[_actualFrame++];
}

public bool[] GetFrame(int index)
{
return (bool[])_frames[index];
}

public void AddFrame(bool[] frame)
{
if (frame.Length != _length)
throw new IndexOutOfRangeException("This frame contains more bits than passed by the Animation constructor.");

_frames.Add(frame);
}

public void InsertFrame(int index, bool[] frame)
{
if (frame.Length != _length)
throw new IndexOutOfRangeException("This frame contains more bits than passed by the Animation constructor.");

_frames.Insert(index, frame);
}
}
}

O método Clear

Voltando à nossa classe Program, vamos criar um método simples que irá apagar todas as colunas do cubo, para que possamos facilitar a escrita do próximo método. Iremos apenas usar a instrução Write para passar os pinos para o estado false, apagando os leds. Veja na Listagem 3.

Listagem 3: Apagando os leds

private static void Clear()
{
led1.Write(false);
led2.Write(false);
led3.Write(false);
led4.Write(false);
led5.Write(false);
led6.Write(false);
led7.Write(false);
led8.Write(false);
led9.Write(false);
}

O método Animate

Vamos criar um método estático chamado Animate. Este método irá receber como parâmetros um objeto do tipo Animation, que será reproduzido no cubo.

A lógica utilizada aqui é simples: para cada camada do cubo iremos “pintar” os leds de acordo com a respectiva linha do frame do objeto Animation. Iremos repetir estas instruções de acender e apagar leds por camada em um loop cuja duração será definida pela constante tempo, criando assim o efeito da multiplexação. E isso irá se repetir por todos os frames da animação, criando o efeito desejado. Veja o resultado na Listagem 4.

Listagem 4: Método animate

private static void Animate(Animation anim)
{
bool[] frame = anim.GetFrame();
int offset = 0;

for (int time = 0; time < tempo; time++)
{
for (int level = 0; level < 3; level++)
{
for (int i = 0; i < 3; i++)
{
camadas[i].Write(level == i);
}
offset = level * 9;
led1.Write(frame[offset]);
led2.Write(frame[offset + 1]);
led3.Write(frame[offset + 2]);
led4.Write(frame[offset + 3]);
led5.Write(frame[offset + 4]);
led6.Write(frame[offset + 5]);
led7.Write(frame[offset + 6]);
led8.Write(frame[offset + 7]);
led9.Write(frame[offset + 8]);
Limpar();
}
}
}

Gerando as animações

Para criar as figuras animadas, crie métodos para facilitar a modularização do código. No nosso exemplo criei quatro métodos para gerar os objetos do tipo Animation: DiagonalEsquerda (forma uma diagonal que gira para a esquerda), PiramideSobe (forma uma pirâmide cujos degraus sobem e descem), Cubo (forma o contorno de um cubo que pisca de dentro para fora) e Cobra (forma uma figura como uma cobra que arrasta sua cauda pelas laterais do cubo).

Perceba que cada método cria seus frames de acordo com os bits true e false dentro do array de booleanos, formando as informações lógicas para as camadas e colunas do cubo. Veja na Listagem 5 como estes métodos são preenchidos.

Listagem 5: Métodos DiagonalEsquerda, PiramideSobe, Cubo e Cobra

private static Animation
DiagonalEsquerda()
{
Animation anim = new Animation(3);
bool[] frame1 = new bool[27] {
true, false, false, false, false, false, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, false, false, false, false, false, true
};
bool[] frame2 = new bool[27] {
false, true, false, false, false, false, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, false, false, false, false, true, false
};
bool[] frame3 = new bool[27] {
false, false, true, false, false, false, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, false, false, false, true, false, false
};
bool[] frame4 = new bool[27] {
false, false, false, true, false, false, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, false, false, true, false, false, false
};
bool[] frame5 = new bool[27] {

false, false, false, false, true, false, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, false, true, false, false, false, false
};
bool[] frame6 = new bool[27] {
false, false, false, false, false, true, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, true, false, false, false, false, false
};
bool[] frame7 = new bool[27] {
false, false, false, false, false, false, true, false, false,
false, false, false, false, true, false, false, false, false,
false, false, true, false, false, false, false, false, false
};
bool[] frame8 = new bool[27] {
false, false, false, false, false, false, false, true, false,
false, false, false, false, true, false, false, false, false,
false, true, false, false, false, false, false, false, false
};
bool[] frame9 = new bool[27] {
false, false, false, false, false, false, false, false, true,
false, false, false, false, true, false, false, false, false,
true, false, false, false, false, false, false, false, false
};
anim.AddFrame(frame1);
anim.AddFrame(frame2);
anim.AddFrame(frame3);
anim.AddFrame(frame6);
anim.AddFrame(frame9);
anim.AddFrame(frame8);
anim.AddFrame(frame7);
anim.AddFrame(frame4);

return anim;
}

private static Animation PiramideSobe()
{
Animation anim = new Animation(3);
bool[] frame1 = new bool[27] {
false, false, false, false, false, false, true, false, false,
false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false

bool[] frame2 = new bool[27] {

false, false, false, false, false, false, false, false, false,
false, false, false, true, false, false, false, true, false,
false, false, false, false, false, false, false, false, false

bool[] frame3 = new bool[27] {

false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false,
true, false, false, false, true, false, false, false, true

bool[] frame4 = new bool[27] {

false, false, false, false, false, false, false, false, false,
false, true, false, false, false, true, false, false, false,
false, false, false, false, false, false, false, false, false

bool[] frame5 = new bool[27] {

false, false, true, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false
};
bool[] frame6 = new bool[27] {
false, true, false, false, false, true, false, false, false,
false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false
};
bool[] frame7 = new bool[27] {
true, false, false, false, true, false, false, false, true,
false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false
};
bool[] frame8 = new bool[27] {
false, false, false, true, false, false, false, true, false,
false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false

bool[] frame9 = new bool[27] {
false, false, false, false, false, false, true, false, false,
false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false
};
anim.AddFrame(frame1);
anim.AddFrame(frame2);
anim.AddFrame(frame3);
anim.AddFrame(frame4);
anim.AddFrame(frame5);
anim.AddFrame(frame6);
anim.AddFrame(frame7);
anim.AddFrame(frame8);
anim.AddFrame(frame9);

return anim;
}

private static Animation Cubo()
{
Animation anim = new Animation(3);
bool[] frame1 = new bool[27] {
true, true, true, true, false, true, true, true, true,
true, false, true, false, false, false, true, false, true,
true, true, true, true, false, true, true, true, true
};
bool[] frame2 = new bool[27] {
false, false, false, false, false, false, false, false, false,
false, false, false, false, true, false, false, false, false,
false, false, false, false, false, false, false, false, false
};

anim.AddFrame(frame1);
anim.AddFrame(frame1);
anim.AddFrame(frame1);
anim.AddFrame(frame1);
anim.AddFrame(frame2);
anim.AddFrame(frame2);
anim.AddFrame(frame2);
anim.AddFrame(frame2);

return anim;
}

private static Animation Cobra()
{
Animation anim = new Animation(3);
bool[] frame1 = new bool[27] {
false, false, true, false, false, false, false, false, false,
false, true, false, false, false, false, false, false, false,
true, false, false, false, false, false, false, false, false
};
bool[] frame2 = new bool[27] {
false, true, false, false, false, false, false, false, false,
true, false, false, false, false, false, false, false, false,
false, false, false, true, false, false, false, false, false
};
bool[] frame3 = new bool[27] {
true, false, false, false, false, false, false, false, false,
false, false, false, true, false, false, false, false, false,
false, false, false, false, false, false, true, false, false
};
bool[] frame4 = new bool[27] {
false, false, false, true, false, false, false, false, false,
false, false, false, false, false, false, true, false, false,
false, false, false, false, false, false, false, true, false
};
bool[] frame5 = new bool[27] {
false, false, false, false, false, false, true, false, false,
false, false, false, false, false, false, false, true, false,
false, false, false, false, false, false, false, false, true
};
bool[] frame6 = new bool[27] {
false, false, false, false, false, false, false, true, false,
false, false, false, false, false, false, false, false, true,
false, false, false, false, false, true, false, false, false
};
bool[] frame7 = new bool[27] {
false, false, false, false, false, false, false, false, true,
false, false, false, false, false, true, false, false, false,
false, false, true, false, false, false, false, false, false
};
bool[] frame8 = new bool[27] {
false, false, false, false, false, true, false, false, false,
false, false, true, false, false, false, false, false, false,
false, true, false, false, false, false, false, false, false
};

anim.AddFrame(frame1);
anim.AddFrame(frame2);
anim.AddFrame(frame3);
anim.AddFrame(frame4);
anim.AddFrame(frame5);
anim.AddFrame(frame6);
anim.AddFrame(frame7);
anim.AddFrame(frame8);

return anim;
}

Acende tudo!

Vamos finalizar nosso projeto completando o método Main, criando os objetos do tipo Animation e passando-os por parâmetro para o método Animate, que será repetido pela quantidade de vezes definida na constante loopMin ou loopMax, dependendo, novamente, do efeito que se deseja obter.

Veja o método Main completo na Listagem 6.

Listagem 6: Método Main

public static void Main()
{

Animation anim1 = DiagonalEsquerda();
Animation anim2 = PiramideSobe();
Animation anim3 = Cubo();
Animation anim4 = Cobra();
OutputPort[] levels = new OutputPort[3] { camada1, camada2, camada3 };

while (true)
{
for (int i = 0; i < anim1.FrameCount * loopsMin; i++)
{
Animate(anim1);
}
for (int i = 0; i < anim2.FrameCount * loopsMin; i++)
{
Animate(anim2);
}
for (int i = 0; i < anim3.FrameCount * loopsMin; i++)
{
Animate(anim3);
}
for (int i = 0; i < anim4.FrameCount * loopsMin; i++)
{
Animate(anim4);
}
}
}

Desta forma, basta rodar o código que o Visual Studio irá entregar o programa na memória do Netduino e irá iniciar sua execução. Você verá que o cubo de leds irá reproduzir as animações cujos frames definimos através de bits true (aceso) e false (apagado) no array de booleanos dentro da classe Animation.

Perceba que temos um loop while (true). Isto se deve ao fato de que, quando colocamos um programa em um microcontrolador, queremos que este programa execute ininterruptamente. Podem haver casos em que você não deseje que o programa fique executando sem parar; para estes casos, você pode definir o fim da aplicação após a execução completa do código ou da forma que achar melhor. Se quiser reiniciar a execução do mesmo, basta pressionar o botão do Netduino.

Conclusão

Como pudemos notar, o Netduino oferece muitas possibilidades mesmo para aqueles que possuem um conhecimento básico de eletrônica. Trazendo o hardware até o C#, o Netduino pode dar margem à imaginação e facilitar a criação de dispositivos muito úteis, interessantes e divertidos para o desenvolvedor.

Devido à compatibilidade da disposição e da configuração de saídas com o Arduino, existem dezenas de projetos na web que podem ser adaptados para o Netduino sem dificuldade alguma, já que os pinos desses dois microcontroladores são idênticos, mudando apenas a linguagem de desenvolvimento. E cá pra nós, nosso bom e velho C# é outra coisa, não é mesmo? Bem vindo ao mundo do .NET Microframework com Netduino! Software, meet Hardware!

Links

Saiba Mais