Cubo LED + cobra

Prefácio


Neste artigo, nós (desde que o artigo foi escrito e feito por um projeto de 2 pessoas) contaremos como inspiramos a vida no jogo antigo, esquecido por todos.

imagem

Preparação


Vamos começar com o cubo. Não criamos uma “bicicleta” e decidimos procurar soluções prontas. O artigo foi baseado em um artigo de um autor norueguês, embora tenham sido feitas algumas alterações que, a meu ver, foram benéficas.

Crie seu próprio cubo de LED


Tendo discutido alguns pontos, decidiu-se fazer um cubo 8x8x8. Conseguimos comprar 1000 LEDs a granel a um bom preço, exatamente o que você precisa, apesar de não serem realmente adequados. Visualmente, os LEDs azuis com uma lente fosca ficariam melhores, o brilho não é tão brilhante e mais uniforme. Há outro problema com os LEDs transparentes: a camada inferior destaca os superiores. No entanto, apesar de tudo isso, os LEDs devem estar suficientemente claros para tornar a imagem clara. Para maior transparência do cubo, é melhor usar LEDs pequenos, por exemplo, 3 mm. Outro ponto ao escolher os LEDs é o comprimento das pernas. Criaremos a estrutura do cubo a partir das pernas dos LEDs, para que não sejam menores que o tamanho da gaiola.

Outro ponto importante ao construir um cubo é o seu tamanho. Fabricamos a armação usando as pernas do LED, embora haja maneiras de usar hastes ou fios de metal. O comprimento dos cátodos dos nossos LEDs foi de aproximadamente 25 mm, por isso escolhemos um tamanho de célula de 20 mm e decidimos usar o restante para solda, para que o cubo fosse mais forte. Essa é apenas uma pequena diferença do design do autor do artigo acima.

O próximo passo é criar um layout para as camadas do cubo de solda, isso ajudará a facilitar a soldagem e o cubo será mais uniforme. O layout é muito simples, peguei um pedaço de madeira compensada, desenhei no tamanho do meu cubo e fiz furos na interseção das linhas no tamanho dos LEDs.

imagem

Soldando várias camadas, percebi que esse projeto não é suficiente para pernas, o que serviu como parafusos longos.

Antes de começar a soldar o cubo, recomendo que você prepare tudo. Por experiência pessoal, posso dizer que é muito mais conveniente fazer isso juntos, porque realmente não há mãos suficientes, uma pessoa aplica cátodos e a segunda alimenta soldas e soldas. Você precisa fazer isso rapidamente, os LEDs são pequenos e têm medo de superaquecer. Também em manuais diferentes, eles dizem que verifique cada LED antes de soldar. Eu não entendo o ponto, passo muito tempo em uma operação essencialmente sem sentido. Compramos novos LEDs e todos acabaram funcionando. Mas quando você soldou a camada, já vale a pena verificar tudo bem, porque soldar o LED do centro é uma tarefa desagradável, que foi verificada por experiência pessoal.

Então, vamos prosseguir diretamente para a solda. Não posso dizer que esse é o caminho mais certo, mas fizemos dessa maneira. Para começar, temos todos os LEDs em nosso layout, é aconselhável instalá-los sem problemas, então não haverá como consertar algo.

imagem

imagem

Então dobramos os cátodos para que eles se sobreponham. Quando tudo estiver pronto, você poderá começar a soldar, mas lembre-se de um possível superaquecimento e, se a solda falhar, não se apresse, é melhor deixar o LED esfriar.

Soldando todos os LEDs, obtemos 8 tiras de 8 LEDs. Para transformar tudo em uma camada, usamos arame de alumínio comum, que foi endireitado anteriormente. Decidimos usar apenas duas dessas “hastes”, para não complicar o design, pois ele se mostrou bastante forte.

Não é necessário soldar a última camada porque é necessário inseri-la novamente. Agora que temos todas as 8 camadas, precisamos combiná-las de alguma forma em um cubo. Para tornar o cubo mais ou menos uniforme, tive que dobrar os ânodos, como mostra a figura, agora gira em torno do LED e pode ser soldada com precisão.

imagem

imagem

Soldamos o cubo da camada superior até a parte inferior, enquanto verificamos constantemente a operação dos LEDs, é melhor não ter preguiça, porque será mais difícil corrigir o erro. É melhor definir a altura entre as camadas com a ajuda de alguns modelos, o autor do artigo acima usou coroas comuns. Não tínhamos nada adequado, mas éramos dois, então decidimos configurar tudo manualmente. Acabou bem, mas não perfeito.

Depois de executar todas essas ações, você obterá o cubo de LEDs coletado e alguns dedos queimados, mas isso já depende da sua precisão.

imagem

imagem

Projeto do circuito


Para implementar nossas idéias, precisávamos de algum tipo de microcontrolador. Decidimos ficar no microcontrolador Arduino Leonardo. Não queríamos nos preocupar com programadores e software necessário, apenas não precisamos de um processador poderoso para nossas tarefas. Com o dispositivo pronto, percebemos que era possível usar o Nano, mas isso não é tão crítico. Para controlar o cubo, decidimos usar um telefone conectado ao Arduino via Bluetooth; no nosso caso, o HC-05 é usado, mas você pode usar qualquer outro.

imagem

Mas o que é realmente importante é o número de pinos do microcontrolador, porque temos 64 pinos de ânodo e 8 pinos de cátodo, mas falaremos sobre conectá-los mais tarde. Decidimos expandir as portas de E / S com a ajuda de registros de turno; no nosso caso, esses são os registros TI 74HC595 no pacote DIP para soldar os soquetes na placa e inserir os microcircuitos neles. Você pode ler mais sobre esses registradores na folha de dados. Diremos apenas que usamos tudo, exceto o sinal de redefinição do registro, aplicamos uma unidade a ele, porque é inversa e transformamos a saída para permitir a entrada no terra, também é inversa e sempre desejamos receber dados de registros. Você também pode usar registros seqüenciais, mas precisará colocar um decodificador para selecionar em qual registro gravar informações.

imagem

Para fechar o circuito ao terra, escolhendo o nível que queremos acender, precisamos de chaves de transistor, ou apenas transistores. Utilizamos transistores bipolares de baixa potência do tipo NPN comuns, conectados em paralelo em 2. Não tenho certeza se isso faz sentido, mas parece que a corrente flui melhor. A base através de um resistor de 100 Ohms é conectada ao microcontrolador, que abrirá nossos transistores. As camadas do cubo são conectadas ao coletor e os emissores são conectados ao terra.

imagem

imagem

Juntando o cubo


Eles não encontraram nada melhor para fornecer o cubo do que a fonte de alimentação do tablet, cujos parâmetros são 5 V e 2 A. Talvez isso não seja suficiente, mas o cubo brilha bastante. Para não queimar os LEDs, todos os terminais do ânodo são conectados aos registradores através de um resistor limitador de corrente. De acordo com meus cálculos, eles deveriam ter aproximadamente 40 Ohms, mas eu não tinha, então usei 100 Ohms.

Colocamos tudo isso em duas pequenas placas de circuito impresso. Eles não envenenaram intencionalmente nada devido à falta de prática, eles simplesmente conectaram tudo aos condutores comuns. Os registros com os ânodos do cubo foram conectados usando os cabos que retiramos do computador antigo. É mais fácil navegar pelos fios.

imagem

Eles decidiram montar o protótipo no layout usado para a solda.

imagem

Depois de depurar tudo e corrigir os erros cometidos, criamos o corpo do laminado, que acabou sendo bom porque não precisava ser pintado. Aqui está o resultado final:

imagem

Que haja luz!


Existe um cubo, resta fazer brilhar. Em seguida, vários modos (modos) do cubo serão descritos, para alternar qual bluetooth + Android foi usado. O aplicativo de telefone foi escrito usando o Cordova. O código do aplicativo não será descrito aqui, mas o link para o repositório é apresentado na conclusão.

Algoritmo de cubo


Devido ao fato de não termos acesso a todos os LEDs de uma só vez, não podemos acender todos de uma vez. Em vez disso, precisamos iluminá-los em camadas.

O algoritmo é o seguinte:

  1. A camada atual é 0.
  2. Registre a máscara para a camada atual
  3. Feche o transistor para a camada anterior. Se a camada atual é zero, a camada anterior é a sétima camada
  4. Clicamos em valores nos registros. O valor aparece nas saídas dos registradores
  5. Abra o transistor para a camada atual. Viva, uma camada brilha!
  6. Camada atual ++. ir para: 2

No total, esse algoritmo se repete rapidamente, o que nos dá a ilusão de que todos os LEDs estão acesos simultaneamente (mas sabemos que isso não é verdade). As máscaras são armazenadas em uma matriz de 64 x 8 bytes.

Mods de escrita

Esses modos não apareceram na ordem em que são apresentados aqui; portanto, não os puna numerando no código. Vamos começar com o mais simples: acenda todos os LEDs.

Acenda o cubo
void Mode_2_Init() {
	for (byte z = 0; z < CUBE_EDGE_SIZE; z++) {
		for (byte y = 0; y < CUBE_EDGE_SIZE; y++) {
			for (byte x = 0; x < CUBE_EDGE_SIZE; x++) {
				cube->data[z * CUBE_LEVEL_SIZE + y * CUBE_EDGE_SIZE + x] = 1;
			}
		}
	}
}


O mod "pegajoso"

Idéia: apenas duas camadas de zero e sétima queimadura, e elas são inversas uma à outra (o LED na posição X acende apenas em uma das camadas). A posição é selecionada aleatoriamente (por algum motivo, todo mundo está tentando encontrar um algoritmo para selecionar a posição), e o LED nessa posição "rasteja" para a camada superior se estiver acesa na camada inferior e, de acordo com a inferior, se estiver acesa na parte superior.

Código fixo
void Mode_0() {
	byte positionToMove = random(CUBE_LEVEL_SIZE);
	byte fromLevel = cube->data[positionToMove] == 1 ? 0 : 7;
	bool top = fromLevel == 0;
	cube->ShowDataXTimes(5);
	while (true) {
		byte toLevel = top ? fromLevel + 1 : fromLevel - 1;
		if (toLevel >= CUBE_EDGE_SIZE || toLevel < 0) break;
		cube->data[fromLevel * CUBE_LEVEL_SIZE + positionToMove] = 0;
		cube->data[toLevel * CUBE_LEVEL_SIZE + positionToMove] = 1;
		cube->ShowDataXTimes(2);
		fromLevel = toLevel;
	}
}

void Mode_0_Init() {
	cube->Clear();
	for (byte i = 0; i < CUBE_LEVEL_SIZE; i++) {
		byte value = random(0, 2);  // max - 1
		cube->data[i] = value;  //first level
		cube->data[i + (CUBE_EDGE_SIZE - 1) * CUBE_LEVEL_SIZE] = !value;  //last level
	}
}


Como é a vida:



“Outro mod pegajoso”

Este mod é semelhante ao anterior, exceto que a camada não está queimando, mas a face e as luzes dessa face, uma a uma, se movem para o oposto e depois para trás.

Outro código fixo
void Mode_3_Init() {
	cube->Clear();
	positions->clear();
	new_positions->clear();
	mode_3_direction = NORMAL;
	for (short y = 0; y < CUBE_LEVEL_SIZE * CUBE_EDGE_SIZE; y += CUBE_LEVEL_SIZE) {
		for (byte x = 0; x < CUBE_EDGE_SIZE; x++) {
			cube->data[x + y] = 1;
			positions->push_back(x + y);
		}		
	}
}

void Mode_3() {
	if (positions->size() == 0) {
		delete positions;
		positions = new_positions;
		new_positions = new SimpleList<short>();
		mode_3_direction = mode_3_direction == NORMAL ? INVERSE : NORMAL;
	}
	byte item = random(0, positions->size());
	short position = *((*positions)[item]);
	positions->erase(positions->begin() + item);
	byte i = 1;
	while(i++ < CUBE_EDGE_SIZE ) {
		cube->data[position] = 0;
		if(mode_3_direction == NORMAL) position += CUBE_EDGE_SIZE;
		else position -= CUBE_EDGE_SIZE;
		cube->data[position] = 1;
		cube->ShowDataXTimes(1);
	}
	new_positions->push_back(position);
}




Cubo dentro de um cubo

Idéia: acenda os LEDs dentro do cubo na forma de faces do cubo com tamanhos de 1 a 8 LEDs e vice-versa.

Cubo dentro de um cubo
void Mode_1() {
	cube->Clear();
	for (byte cube_size = 0; cube_size < CUBE_EDGE_SIZE; cube_size++) {
		for (byte level = 0; level <= cube_size; level++) {
			for (byte x = 0; x <= cube_size; x++) {
				for (byte y = 0; y <= cube_size; y ++) {
					cube->data[level * CUBE_LEVEL_SIZE + y * CUBE_EDGE_SIZE + x] =
						(y % cube_size == 0 || x % cube_size == 0)
						&& level % cube_size == 0 ||
						(y % cube_size == 0) && (x % cube_size == 0) ? 1 : 0;
				}
			}
		}
		cube->ShowDataXTimes(5);
	}
	for (byte cube_size = CUBE_EDGE_SIZE - 1; cube_size > 0; cube_size--) {
		for (byte level = 0; level <= cube_size; level++) {
			for (byte x = 0; x <= cube_size; x++) {
				for (byte y = 0; y <= cube_size; y++) {
					cube->data[level * CUBE_LEVEL_SIZE + (CUBE_EDGE_SIZE - 1 - y) * CUBE_EDGE_SIZE + (CUBE_EDGE_SIZE - 1 - x)] =
						(((y % (cube_size - 1) == 0 || x % (cube_size - 1) == 0) && (level % (cube_size - 1) == 0))
						|| ((y % (cube_size - 1) == 0) && (x % (cube_size - 1) == 0) && level % cube_size != 0))
						&& x < (cube_size) && y < (cube_size) ? 1 : 0;
				}
			}
		}
		cube->ShowDataXTimes(5);
	}
}


Como é:



E finalmente a cobra

Das características da implementação da cobra, é importante notar que não há restrições no campo e, portanto, deixando o cubo, por um lado, você aparece por outro. Você só pode perder se esbarrar em si mesmo (na verdade, você não pode vencer).
Também vale a pena contar separadamente sobre o gerenciamento:

No caso de uma implementação bidimensional deste jogo, não há problemas com o gerenciamento: quatro botões e tudo é óbvio. No caso de uma implementação tridimensional, várias opções de controle surgem:

1.6 botões. Com esta opção, o botão tem sua própria direção de movimento: para os botões para cima e para baixo, tudo é óbvio, e o restante dos botões pode ser "amarrado" aos pontos cardeais; quando você pressiona o botão "esquerdo", o vetor de movimento sempre muda para "oeste" etc. Com esta opção, surgem situações em que a cobra se move para "leste" e clicamos em "oeste". Porque a cobra não pode girar 180 graus; você deve lidar com esses casos separadamente.

2.4 botões (Cima, Baixo, Esquerda, Direita). As ações desses botões são semelhantes às de uma implementação bidimensional, exceto que todas as alterações são realizadas em relação à direção atual do vetor de movimento. Deixe-me explicar por exemplo: ao mover no plano horizontal, pressionando o botão "Para cima", vamos para o plano vertical. Ao mover em um plano vertical pressionando “Para cima”, prosseguimos para o movimento em um plano horizontal contra a direção do eixo X, para “Para baixo” - na direção do eixo X, etc.

Obviamente, ambas as opções têm o direito de existir (seria interessante conhecer outras opções de controle). Para o nosso projeto, escolhemos o segundo.

Código de direção
void Snake::ApplyUp() {
	switch (direction) {
	case X:
	case Y:
		direction = Z;
		directionType = NORMAL;
		break;
	case Z:
		direction = X;
		directionType = INVERSE;
	}
}

void Snake::ApplyDown() {
	switch (direction) {
	case X:
	case Y:
		direction = Z;
		directionType = INVERSE;
		break;
	case Z:
		direction = X;
		directionType = NORMAL;
	}
}

void Snake::ApplyLeft() {
	switch (direction) {
	case X:
		direction = Y;
		directionType = directionType == NORMAL ? INVERSE : NORMAL;
		break;
	case Y:
		direction = X;
		directionType = directionType;
		break;
	
	case Z:
		direction = Y;
		directionType = NORMAL;
	}
}

void Snake::ApplyRight() {
	switch (direction) {
	case X:
		direction = Y;
		directionType = directionType;
		break;
	case Y:
		direction = X;
		directionType = directionType == NORMAL ? INVERSE : NORMAL;
		break;
	case Z:
		direction = Y;
		directionType = INVERSE;
	}
}

void Snake::ChangeDirection(KEY key) {
	switch(key) {
	case UP:
		ApplyUp();
		break;
	case LEFT:
		ApplyLeft();
		break;
	case RIGHT:
		ApplyRight();
		break;
	case DOWN:
		ApplyDown();
		break;
	}
}


O resultado do programa:





Links para o código-fonte do firmware do cubo e do aplicativo para o telefone: firmware do

aplicativo


Resultado


Como resultado, obtivemos um console primitivo e um tipo de lâmpada, com a possibilidade de iluminação dinâmica. No futuro, planejamos desenvolver uma placa de circuito impresso e melhorar o firmware. Também é possível adicionar módulos adicionais.

Source: https://habr.com/ru/post/pt388369/


All Articles