Projetos
Construindo um cubo de LEDs 3×3
O LED é um componente que traz muitas possibilidades de uso, pois ele serve tanto pra dar um toque para algum projeto, como também podemos criar coisas utilizando apenas eles e aproveitando para soltar a imaginação brincando com suas cores, variando formatos e caprichando na programação com os estilos de piscar. Por isso hoje trouxemos pra vocês um cubo de LEDs! Vamos começar então pela parte do funcionamento.
Funcionamento do cubo de leds
Note que os LEDs dessa montagem estão dispostos como uma matriz 3×9. Além disso, todos os LEDs de uma linha estão conectados pelos seus catodos, enquanto os LEDs de uma coluna estão ligados pelos seus anodos. Dessa forma, se queremos selecionar um LED específico, basta selecionar uma linha e uma coluna.
Analogamente para se selecionar os LEDs do cubo basta selecionar seu nível e sua coluna. Dessa forma podemos usar o arduino para selecionar posições no cubo de led. Assim, quando queremos acender determinado LED, devemos acionar a saída correspondente a linha, ou ao “andar” no caso do cubo, e a coluna específica, enquanto garantimos que as demais linhas se mantenham em zero. O código abaixo mostra como fazemos para acender o LED do centro do cubo.
void setup(){
pinMode(A1, OUTPUT);
pinMode(6 , OUTPUT);
}
void loop(){
digitalWrite(A1, HIGH);
digitalWrite(6 , HIGH);
}
Nesse trecho de código, chamamos a atenção para o fato que as linhas e as colunas estão enumeradas de acordo com a ordem em que elas estão conectadas no arduino. Além disso, destacamos que ao agrupar os leds em níveis, conseguimos reduzir a quantidade de portas usadas pelo arduino de 27 para 12. O nome desta técnica é multiplexação e ela é muito usada em eletrônica para reduzir o número de barramentos usados na transmissão de dados. Agora que já entendemos o funcionamento do cubo de LEDs, vamos para uma das partes mais legais: a montagem!
Montagem
Antes de tudo, precisamos ter em mãos os seguintes materiais:
-
LEDs (27 unidades);
-
Protoboard;
-
Resistores de 330R (9 unidades);
-
Resistores de 10k (3 unidades;
-
Transistores 2N2222 (3 unidades);
-
Fios para as ligações;
Com esses materiais em mãos, a primeira parte desse projeto é montar o cubo, para isso vamos construir uma guia, nela os leds serão posicionados lado a lado, com espaços constantes entre eles, assim conseguiremos montar o cubo de forma rápida, garantindo que seus lados fiquem retos. Essa guia consiste em um quadrado de 50mm de lado com nove furos com espaços de 20 mm entre eles. Cada furo deverá ter o diâmetro do led, que para o nosso caso é de 5 mm.
Para construir essa guia você pode fazer do modo que desejar, mas nós usamos uma impressora 3D. Assim, primeiro desenhamos ela com o tinkercad e geramos o desenho no formato STL deste link, em seguida usamos o Cura para criar o arquivo .gcode. Por fim imprimimos esse arquivo usando a nossa impressora 3D. A peça ficou como mostrado abaixo.
Agora que temos essa peça em mãos, posicionamos um led em cada furo, e soldamos os seus catodos com o do led ao lado, formando uma pequena grade, como mostra a figura abaixo, essas grades serão usadas para confeccionar os andares do nosso cubo, portanto precisamos de três peças deste tipo.
Finalmente usando algum tipo de apoio (no nosso caso usamos um pedaço de papelão), soldamos as três grades uma sobre a outra, unindo apenas os anodos dos leds de cima aos de baixo, o que nos deixa com um cubo de 3 níveis e 9 colunas, como as imagens abaixo.
Circuito de controle
Para realizar as conexões do circuito de controle pegamos um pedaço de cabo flat de 12 vias e o separamos em 4 pedaços de 3 vias, assim conseguimos soldar estes pedaços a cada nível e a cada coluna do cubo de led, ficando um pedaço para cada linha de colunas do cubo, enquanto o último pedaço é soldado nos níveis, conforme vocês podem ver na imagem abaixo.
Para os cabos soldados nas colunas, a outra extremidade é ligada nas saídas numeradas de 2 até 10 de um Arduino, com cada via passando antes por um resistor de 330Ω. Já para os 3 níveis conectamos cada via ao coletor de transistores NPN 2N2222. Esses transistores possuem seus emissores aterrados, enquanto suas bases estão ligadas às saídas A0, A1 e A2 do Arduino por meio de resistores de 10k. Isso está representado na imagem abaixo.
![](https://blog.ryndackcomponentes.com.br/wp-content/uploads/2024/05/IMG_20240522_130653-scaled.jpg)
Agora que já montamos nosso cubo e já sabemos o básico sobre o seu funcionamento, vamos começar a criar e explorar alguns efeitos que podemos criar com ele!
Explorando com o cubo
1 – Função “seleciona_nivel()”
Essa função é bem simples e o objetivo dela é escrever como alto o nível selecionado, enquanto demais são mantidos em nível lógico baixo. Ela ficou implementada como segue:
void seleciona_nivel(int nivel){
for (int i = 0; i < 3; ++i){
digitalWrite(niveis[ i ], LOW);
}
if (nivel >= 1 && nivel <= 4){
if (nivel != 4) {
digitalWrite(niveis[nivel - 1], HIGH);
}
else{
for (int i = 0; i < 3; ++i){
digitalWrite(niveis[ i ], HIGH);
}
}
}
}
Aqui começamos escrevendo todos os níveis como baixo, em seguida é verificado se o valor de nível escolhido está no intervalo correto, se não está, então nada é realizado, apenas se mantêm os níveis como baixo, mas se ele está no intervalo correto, então o nível selecionado é escrito como alto. Observe que se o nível selecionado for 4 então todos os níveis são descritos como alto.
Veja que o nível foi acionado por meio de um vetor chamado “niveis[ i ]”, esse é um vetor de 3 posições com cada nível do cubo descrito nele. Além desse vetor, temos outro para as colunas (com nove posições) e uma matriz para os estados do cubo.
2 – Função “envia_estados()”
A segunda função implementada é a função enviar estados. Ela funciona com o auxílio da variável global “int estados[3][9];”, e é responsável por enviar os valores dessa matriz para a saída, então o que ela faz é simplesmente realizar uma varredura da matriz e enviar os valores dela para as saídas correspondentes do Arduino. Ela ficou como mostra abaixo.
//Envia a matriz que representa os niveis dos led para o cubo.
//Pode realizar uma varredura nivel a nivel (varredura 1) ou
//habilitar todos os niveis simultaneamente (varredura 2).
void envia_estados(int estados[ 3 ][ 9 ], int varredura){
if(varredura==1){
for(int i=0; i<=2; i++){
for(int j=0; j<9; j++){
digitalWrite(colunas[ j ], estados[ i ][ j ]);
seleciona_nivel(i+1);
}
}
}
else if(varredura==2){
for(int j=0; j<9; j++){
digitalWrite(colunas[ j ], estados[ 0 ][ j ]);
}
seleciona_nivel(4);
}
}
O código começa verificando o modo de varredura desejado, ou seja, se é desejado realizar uma varredura nível a nível, ou se o objetivo é controlar as colunas apenas. Se o modo escolhido for o 1, então temos dois loops, o primeiro acionando as colunas e o segundo selecionando o nível, enviando assim todos os valores da matriz para a saída. Entretanto, se a opção foi a 2 então temos apenas um loop lendo as colunas, e o único nível enviado para a saída é o nível 0 da matriz.
3 – Função “acende_limpa_cubo()”
Essa função é bem simples, ela é formada apenas pelos loops para a escrita em matriz, como mostrado abaixo:
//Função torna alto ou baixo o nivel lógico de todos os leds do cubo.
void acende_limpa_cubo(int on_off){
for(int i=0; i<4; i++){
for(int j=0; j<9; j++){
estados[i][j]=on_off;
}
}
}
Nessa função temos que o valor de “on_off” é escrito em todas as posições da matriz “estados[ 3 ][ 9 ]”, assim, se passarmos 1 para esse função e posteriormente enviar essa matriz para a função “envia_estados()”, o cubo acenderá completamente, mas ele apagará completamente se o valor de on_off for 0.
4 – Função “led_aleatorio()”
Como o nome já diz, o objetivo dessa função é acender um led aleatório, ou seja, colocar em alto uma posição aleatória na matriz estados. Para isso usamos a função “random()” do Arduino para selecionar a posição aleatória, como no código abaixo.
//Acende ou apaga um led aleatório do cubo.
void led_aleatorio(int pausa, int on_off){
estados[random(0,3)][random(0,9)]=on_off;
for(int tempo=0; tempo<pausa; tempo++){
envia_estados(estados, 1);
}
}
Essa função recebe dois valores, a “pausa” determina por quanto tempo o led ficará aceso, porém, diferente de outros lugares do código, aqui não usamos a função “delay()” e sim um “for”. Fazemos isso porque o delay iria pausar a execução do código, mantendo apenas o último comando da função “envia_estados()’, por outro lado o “for” executa essa função completamente, várias vezes, dando a impressão de que o programa foi pausado.
O outro parâmetro recebido pela função é o “on_off”, que assim como o parâmetro de mesmo nome atua na função “acende_limpa_cubo()”, determina se os leds serão ligados ou desligados.
5 – Função “Plano_vertical()”
A próxima função que trazemos aqui é a função “plano_vertical()”, nessa função acendemos leds que estão posicionados lado a lado, gerando planos verticais. O código ficou como abaixo:
//Acende conjuntos de 3 colunas do cubo, formando planos.
void plano_vertical(int plano){
if(plano>=1 && plano<=3){
for(int i=0; i<3; i++){
for(int j=(3*(plano-1)); j<(3*plano); j++){
estados[i][j]=1;
}
}
}
else if(plano>=4 && plano <=6){
for(int i=0; i<3; i++){
for(int j=(plano-4); j<(plano+3); j+=3){
estados[i][j]=1;
}
}
}
}
Primeiramente os planos que essa função gera estão enumerados de 1 a 6, sendo que os de 1 a 3 são paralelos ao plano frontal e os de 4 a 6 são perpendiculares a estes. Assim, essa função recebe o parâmetro “plano” para selecionar o plano que será acionado, e por meio dos loops adequados ele envia nível alto para a matriz de estados.
6 – Função “gira_led()”
Agora vamos falar da última função criada para controlar o cubo, a função “gira_led()”.A ideia por trás dessa função está em usar uma matriz 4×9, sendo que em cada linha armazenamos os níveis lógicos de todas as colunas de led, dessa forma ao alternamos entre as linhas dessa matriz, os leds em nível alto irão aparentar ser deslocados.
Assim podemos gerar um efeito de rotação usado um loop que envia os valores presentes na linha para saída, e em seguida passa para a próxima linha após uma breve pausa.
Essa função ficou como abaixo:
//Função cria o efeito de leds girando em torno da coluna
//central. A quantidade de voltas pode ser inserida e o sentido
//de rotação pode ser horário ou anti-horário, sendo para horário
//e 2 para anti-horário.
void gira_led(int pausa, int voltas, int sentido) {
// Padrões de ligar/desligar dos LEDs para cada passo do movimento
const int padrao_horario[4][9] = {
{LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW},
{LOW, LOW, LOW, HIGH, HIGH, HIGH, LOW, LOW, LOW},
{HIGH, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, HIGH},
{LOW, HIGH, LOW, LOW, HIGH, LOW, LOW, HIGH, LOW}
};
const int padrao_antihorario[4][9] = {
{LOW, LOW, HIGH, LOW, HIGH, LOW, HIGH, LOW, LOW},
{LOW, HIGH, LOW, LOW, HIGH, LOW, LOW, HIGH, LOW},
{HIGH, LOW, LOW, LOW, HIGH, LOW, LOW, LOW, HIGH},
{LOW, LOW, LOW, HIGH, HIGH, HIGH, LOW, LOW, LOW}
};
int (*padrao)[9]; // Ponteiro para o padrão de acordo com o sentido
if (sentido == 1) {
padrao = padrao_horario;
}
else {
padrao = padrao_antihorario;
}
for (int i = 0; i < voltas; i++) {
for (int j = 0; j < 4; j++) {
// Define o estado dos LEDs de acordo com o padrão atual
for (int k = 0; k < 9; k++) {
digitalWrite(colunas[k], padrao[j][k]);
}
delay(pausa);
}
}
}
Para essa função enviamos os valores de “pausa”, “voltas” bem como o “sentido”.
Começando pela “pausa”, esse parâmetro define o tempo entre um estado e outro, assim, se quisermos um efeito de rápida rotação passamos um valor baixo para a “pausa”. O parâmetro “voltas” define a quantidade de vezes que o efeito se repete, ou seja, se for enviado 10 para “voltas”, esse efeito será repetido 10 vezes. Finalmente o “sentido” de rotação é definido, sendo que 1 é para sentido horário e 2 para anti-horário.
Loop Principal
Finalmente vamos falar do nosso loop principal, nele, o que fazemos é usar as funções já apresentadas para criar diversos efeitos com o cubo. Por exemplo, o trecho abaixo apresenta uma forma de acender o cubo, acendendo leds de forma aleatória:
for(int n=0; n<100; n++){ //Cubo acendendo 1 led por vez, de forma aleatória
led_aleatorio(500,1); //até todos os leds serem acesos.
}
acende_limpa_cubo(1);
for(int n=0; n<1000; n++){
envia_estados(estados, 1);
}
Aqui executamos a função “led_aleatório()” 100 vezes, assim o cubo vai acendendo lentamente, um led por vez. Entretanto para garantir que qualquer led que não tenha sido aceso ainda acenda, ao final da execução dessa função, usamos a função “acende_limpa_cubo(1)”. Para apagar o cubo lentamente podemos usar o mesmo código, apenas mudando o valor de “on_off” enviado para as funções “led_aleatório(500, on_off)” e “acende_limpa_cubo(on_off)”, ou seja mudar o valor de “on_off” para 0.
Outro efeito que podemos é fazer planos “caminharem” pelo cubo, para isso a função “planos_venticais()” é usada como mostrado abaixo.
acende_limpa_cubo(1);
for(int nivel=0; nivel<4; nivel++){ //"planos" subindo e descendo.
seleciona_nivel(nivel);
delay(250);
}
seleciona_nivel(0);
delay(250);
for(int nivel=3; nivel>=0; nivel--){
seleciona_nivel(nivel);
delay(250);
if(nivel==0){
acende_limpa_cubo(0);
envia_estados(estados, 2);
}
}
for(int n=0; n<3; n++){ //"planos" verticais se movendo para lateralmente.
plano_vertical(n+4);
envia_estados(estados,2);
delay(250);
acende_limpa_cubo(0);
envia_estados(estados, 2);
}
delay(250);
for(int n=0; n<3; n++){
plano_vertical(6-n);
envia_estados(estados,2);
delay(250);
acende_limpa_cubo(0);
envia_estados(estados, 2);
}
for(int n=0; n<3; n++){ //"planos" verticais se movendo para tras e para frente.
plano_vertical(n+1);
envia_estados(estados,2);
delay(250);
acende_limpa_cubo(0);
envia_estados(estados, 2);
}
delay(250);
for(int n=0; n<3; n++){
plano_vertical(3-n);
envia_estados(estados,2);
delay(250);
acende_limpa_cubo(0);
envia_estados(estados, 2);
}
A princípio temos a função “acende_limpa_cubo(1)” mantendo todos os valores da matriz de estados em alto, em seguida, os dois laços de repetição alternam os níveis acionados, causando o efeito de “subir e descer”. Os laços seguintes usam a função “planos_verticais()” para produzir um efeito similar nas demais direções.
O ultimo efeito do qual vamos falar usa a função “gira_led()”, ao contrário das funções anteriores essa não necessita de um loop() para se realizar um efeito, sendo que ela foi aplicada como abaixo:
seleciona_nivel(3); //Efeito de leds girando uma vez em cada nivel,
gira_led(75, 15, 2); //alternando o sentido.
seleciona_nivel(2);
gira_led(75, 15, 1);
seleciona_nivel(1);
gira_led(75, 15, 2);
Nesse trecho de código gera o efeito de rotação uma vez em cada nível em sentidos alternados, ou seja, no sentido anti-horário nos níveis 1 e 3 e no sentido horário no nível 2, gerando 15 rotações por nível.
Enfim, o código deste projeto bem como o de outros projetos do nosso blog podem ser encontrados no nosso github.
Não esqueça de contar para a gente o que você achou desse projeto nos comentários e qual o próximo que vocês querem ver por aqui!!!