Cube LED + serpent

Préface


Dans cet article, nous (puisque l'article a été écrit et réalisé par un projet de 2 personnes) vous raconterons comment nous avons insufflé la vie à l'ancien jeu, oublié par tous.

image

La préparation


Commençons par le cube. Nous n'avons pas trouvé de «vélo» et avons décidé de chercher des solutions toutes faites. L'article était basé sur un article d'un auteur norvégien, bien que certaines modifications aient été apportées, qui, il me semble, ont été bénéfiques.

Créez votre propre cube LED


Après avoir discuté de certains points, il a été décidé de faire un cube 8x8x8. Nous avons réussi à acheter 1000 LED en vrac à un bon prix, juste ce dont vous avez besoin, bien que ce ne soient pas vraiment des LED appropriées. Visuellement, les LED bleues avec une lentille mate seraient plus belles, la lueur n'est pas si brillante et plus uniforme. Il y a un autre problème avec les LED transparentes; la couche inférieure met en évidence les couches supérieures. Cependant, malgré tout cela, les LED doivent être suffisamment lumineuses pour rendre l'image claire. Pour plus de transparence du cube, il est préférable de prendre de petites LED, par exemple 3 mm. Un autre point lors du choix des LED est la longueur des pieds. Nous fabriquerons le cadre en cube à partir des pieds des LED, de sorte qu'ils ne devraient pas être plus petits que la taille de la cage.

Un autre point important lors de la construction d'un cube est sa taille. Nous fabriquons le cadre à l'aide des pieds de la LED, bien qu'il existe des moyens d'utiliser des tiges ou des fils métalliques. La longueur des cathodes de nos LED s'est avérée être d'environ 25 mm, nous avons donc choisi une taille de cellule de 20 mm et avons décidé d'utiliser le reste pour le soudage, afin que le cube soit plus solide. C'est juste une légère différence par rapport à la conception de l'auteur de l'article ci-dessus.

L'étape suivante consiste à créer une disposition pour les couches de cube à souder, cela aidera à faciliter la soudure et le cube sera plus uniforme. La disposition est très simple, j'ai pris un morceau de contreplaqué, je l'ai dessiné à la taille de mon cube et j'ai percé des trous à l'intersection des lignes de la taille des LED.

image

En soudant plusieurs couches, j'ai réalisé que cette conception ne suffisait pas aux jambes, qui servaient de longs boulons.

Avant de commencer à souder le cube, je vous conseille de tout préparer. Par expérience personnelle, je peux dire qu'il est beaucoup plus pratique de le faire ensemble, car il n'y a vraiment pas assez de mains, une personne applique des cathodes et la seconde alimente la soudure et les soudures. Vous devez le faire rapidement, les LED sont petites et ont peur de surchauffer. Dans différents manuels, ils disent également de vérifier chaque LED avant de souder. Je ne vois pas l'intérêt, je passe beaucoup de temps sur une opération essentiellement dénuée de sens. Nous avons acheté de nouvelles LED et elles se sont toutes avérées fonctionnelles. Mais lorsque vous avez soudé la couche, cela vaut déjà la peine de bien vérifier tout, car souder la LED depuis le centre est une tâche désagréable, elle a été vérifiée par expérience personnelle.

Alors, passons directement à la soudure. Je ne peux pas dire que c'est le moyen le plus sûr, mais nous l'avons fait de cette façon. Pour commencer, nous avons toutes les LED dans notre disposition, il est conseillé de les installer en douceur, puis il n'y aura aucun moyen de réparer quelque chose.

image

image

Ensuite, nous plions les cathodes de sorte qu'elles se chevauchent. Lorsque tout est prêt, vous pouvez commencer à souder, mais vous devez vous rappeler d'une éventuelle surchauffe, et si la soudure a échoué, vous ne devriez pas vous précipiter pour souder, il est préférable de laisser la LED refroidir.

En soudant toutes les LED, nous obtenons 8 bandes de 8 LED. Afin de transformer le tout en une seule couche, nous avons utilisé du fil d'aluminium ordinaire, qui était auparavant redressé. Nous avons décidé de n'utiliser que 2 de ces «tiges», afin de ne pas compliquer le design, d'ailleurs il s'est avéré assez solide.

Il n'est pas nécessaire de souder la dernière couche, car vous devez alors la réinsérer. Maintenant que nous avons les 8 couches, nous devons en quelque sorte les combiner en un seul cube. Pour rendre le cube plus ou moins uniforme, j'ai dû plier les anodes comme indiqué sur la figure, maintenant il fait le tour de la LED et peut être soigneusement soudé.

image

image

Nous soudons le cube de la couche supérieure au fond, tout en vérifiant constamment le fonctionnement des LED, il vaut mieux ne pas être paresseux, car il sera alors plus difficile de corriger l'erreur. Il est préférable de régler la hauteur entre les couches à l'aide de certains modèles, l'auteur de l'article ci-dessus a utilisé des couronnes ordinaires. Nous n'avions rien de convenable, mais nous étions deux, nous avons donc décidé de tout configurer manuellement. Cela s'est avéré assez fluide, mais pas parfait.

Après avoir fait toutes ces actions, vous obtiendrez le cube LED que vous avez collecté et quelques doigts brûlés, mais cela dépend déjà de votre précision.

image

image

Conception de circuits


Pour mettre en œuvre nos idées, nous avions besoin d'une sorte de microcontrôleur. Nous avons décidé de rester sur le microcontrôleur Arduino Leonardo. Nous ne voulions pas nous soucier des programmeurs et des logiciels nécessaires, mais nous n'avons pas besoin d'un processeur puissant pour nos tâches. Ayant l'appareil fini, nous avons réalisé qu'il était possible d'utiliser Nano, mais ce n'est pas si critique. Pour contrôler le cube, nous avons décidé d'utiliser un téléphone connecté à l'Arduino via Bluetooth, dans notre cas le HC-05 est utilisé, mais vous pouvez en utiliser un autre.

image

Mais ce qui est vraiment important, c'est le nombre de broches de microcontrôleur, car nous avons 64 broches d'anode et 8 broches de cathode, mais nous parlerons de les connecter plus tard. Nous avons décidé d'étendre les ports IO à l'aide de registres à décalage, dans notre cas, ce sont les registres TI 74HC595 dans le package DIP pour souder les prises sur la carte et y insérer les microcircuits eux-mêmes. Vous pouvez en savoir plus sur ces registres dans la fiche technique, je dirai simplement que nous avons tout utilisé sauf le signal de réinitialisation du registre, nous y avons appliqué une unité, car il est inverse, et nous avons mis l'entrée d'activation de la sortie à la masse, elle est également inverse et nous avons toujours voulu recevoir des données des registres. Vous pouvez également utiliser des registres séquentiels, mais vous devrez alors mettre un décodeur pour sélectionner le registre dans lequel écrire les informations.

image

Afin de fermer le circuit à la masse, en choisissant le niveau que nous voulons enflammer, nous avons besoin de commutateurs à transistors, enfin, ou simplement de transistors. Nous avons utilisé des transistors bipolaires de faible puissance de type NPN ordinaires connectés en parallèle en 2. Je ne sais pas si cela a du sens, mais c'est un peu comme si le courant circule mieux. La base à travers une résistance de pull-up de 100 Ohms est connectée au microcontrôleur, qui ouvrira nos transistors. Les couches cubiques sont connectées au collecteur et les émetteurs sont connectés à la terre.

image

image

Assembler le cube


Ils ne pouvaient rien trouver de mieux pour alimenter le cube que l'alimentation électrique de la tablette, dont les paramètres sont 5 V et 2 A. Peut-être que ce n'est pas suffisant, mais le cube brille assez fort. Afin de ne pas brûler les LED, toutes les bornes d'anode sont connectées aux registres via une résistance de limitation de courant. Selon mes calculs, ils auraient dû être d'environ 40 Ohms, mais je n'en avais pas, alors j'ai utilisé 100 Ohms.

Nous avons placé tout cela sur 2 petites cartes de circuits imprimés. Ils n’ont pas intentionnellement empoisonné quoi que ce soit par manque de pratique, ils ont simplement tout connecté avec des conducteurs ordinaires. Les registres avec les anodes du cube ont été connectés à l'aide des câbles que nous avons sortis de l'ancien ordinateur. Il est plus facile de naviguer dans les fils.

image

Ils ont décidé d'assembler le prototype sur le schéma utilisé pour la soudure.

image

Après avoir tout débogué et corrigé les erreurs commises, nous avons fait le corps de notre stratifié; il s'est avéré être bon car il n'avait pas besoin d'être peint. Voici le résultat final:

image

Que la lumière soit!


Il y a un cube, il reste à le faire briller. Ensuite, divers modes (modes) du cube seront décrits, pour changer quel Bluetooth + Android a été utilisé. L'application téléphonique a été écrite en utilisant Cordova. Le code d'application ne sera pas décrit ici, mais le lien vers le référentiel est présenté dans la conclusion.

Algorithme du cube


Étant donné que nous n'avons pas accès à toutes les LED à la fois, nous ne pouvons pas toutes les allumer en même temps. Au lieu de cela, nous devons les éclairer en couches.

L'algorithme est le suivant:

  1. La couche actuelle est 0.
  2. Enregistrer le masque pour le calque actuel
  3. Fermez le transistor de la couche précédente. Si la couche actuelle est nulle, la couche précédente est la 7e couche
  4. Nous cliquons les valeurs dans les registres. Des valeurs apparaissent sur les sorties des registres
  5. Ouvrez le transistor de la couche actuelle. Hourra, une couche brille!
  6. Couche actuelle ++. goto: 2

Au total, cet algorithme se répète assez rapidement, ce qui nous donne l'illusion que toutes les LED sont allumées simultanément (mais nous savons que ce n'est pas le cas). Les masques sont stockés dans un tableau de 64 x 8 octets.

Écriture de mods

Ces modes n'apparaissaient pas dans l'ordre dans lequel ils sont présentés ici, donc ne les punissez pas en les numérotant dans le code. Commençons par le plus simple: allumer toutes les LED.

Allumez le cube
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;
			}
		}
	}
}


Le mod "sticky"

Idée: seulement deux couches de zéro et de septième brûlure, et elles sont inverses l'une par rapport à l'autre (la LED en position X n'est allumée que sur l'une des couches). La position est sélectionnée au hasard (pour une raison quelconque, tout le monde essaie de trouver un algorithme pour sélectionner la position), et la LED dans cette position "rampe" vers la couche supérieure si elle était allumée sur la couche inférieure, et en conséquence vers le bas si elle était allumée sur la partie supérieure.

Code collant
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
	}
}


À quoi ça ressemble dans la vie:



"Un autre mod collant"

Ce mod est similaire au précédent, sauf que le calque ne brûle pas, mais le visage et les lumières de ce visage, un par un, se déplacent vers l'opposé, puis reviennent.

Un autre code collant
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);
}




Cube à l'intérieur d'un cube

Idée: allumer les LED à l'intérieur du cube sous forme de faces du cube avec des tailles de 1 à 8 LED et vice versa.

Cube à l'intérieur d'un cube
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);
	}
}


À quoi ça ressemble:



Et enfin le serpent

Parmi les caractéristiques de la mise en œuvre du serpent, il convient de noter qu'il n'y a pas de restrictions sur le terrain et qu'en conséquence, en laissant le cube d'une part, vous apparaissez de l'autre. Vous ne pouvez perdre que si vous vous cognez (en vérité, vous ne pouvez pas gagner).
Il convient également de parler séparément de la gestion:

dans le cas d'une implémentation en deux dimensions de ce jeu, il n'y a pas de problèmes de gestion: quatre boutons et tout est évident. Dans le cas d'une implémentation tridimensionnelle, plusieurs options de contrôle se présentent:

1.6 boutons. Avec cette option, le bouton a sa propre direction de mouvement: pour les boutons haut et bas, tout est évident, et les boutons restants peuvent être «liés» aux points cardinaux, lorsque vous appuyez sur le bouton «gauche», le vecteur de mouvement change toujours «ouest», etc. Avec cette option, des situations surviennent lorsque le serpent se déplace «à l'est» et que nous cliquons sur «à l'ouest». Parce que le serpent ne peut pas tourner à 180 degrés; vous devez gérer ces cas séparément.

2.4 boutons (haut bas gauche droite). Les actions de ces boutons sont similaires à celles d'une implémentation bidimensionnelle, sauf que toutes les modifications sont prises par rapport à la direction actuelle du vecteur de mouvement. Je m'explique par exemple: lorsque l'on se déplace dans le plan horizontal, en appuyant sur le bouton «Haut», on passe au plan vertical. Lorsque vous vous déplacez dans un plan vertical en appuyant sur «Haut», nous passons à un déplacement dans un plan horizontal contre la direction de l'axe X, pour «Vers le bas» - dans la direction de l'axe X, etc.

Bien entendu, les deux options ont le droit d'exister (il serait intéressant de connaître d'autres options de contrôle). Pour notre projet, nous avons choisi le second.

Code de direction
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;
	}
}


Le résultat du programme:





Liens vers le code source du firmware du cube et de l'application pour le téléphone: firmware de l'

application


Résultat


En conséquence, nous avons obtenu une console primitive et une sorte de lampe, avec la possibilité d'un éclairage dynamique. À l'avenir, nous prévoyons de développer une carte de circuit imprimé et d'améliorer le firmware. Il est également possible d'ajouter des modules supplémentaires.

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


All Articles