4x4x4 LED Würfel

Ein weiteres Projekt von mir war ein 4x4x4 LED Würfel, der mit einem Arduino Nano betrieben wird. Der Aufbau eines LED Würfels (oder auch LED Matrix) ist an sich sehr simple. Dabei wird jeweils die Zeile und die Spalte angesteuert um eine bestimmte LED zum leuchten zu bringen. Die einzigen Gedanken bei diesem Projekt musste ich mir lediglich über die I/Os am Arduino Nano, dem Strom und um das Bauen eines halbwegs geraden LED Würfels machen.

Planung/Entwurf

Da ich mindestens 20 I/Os brauche (16 für die Spalten; 4 für die Ebenen) habe ich den IO-Expander MCP23017 eingesetzt. Dieser übernimmt die gesamte Ansteuerung der Spalten, der Arduino übernimmt die Ebenen. Damit die Ebenen auch genug Strom bekommen, habe ich den PNP Transistor BC558 eingesetzt. Dies ist wichtig, da ein Pin vom Arduino maximal 40mA kann und insgesamt kann der Aruino auch nur 200mA (Quelle: stackexchange), das würde nur zu unterschiedlichen Helligkeiten bei den verschiedenen Animationen führen!

LED Biegung für den Würfel

Bau des Würfels

Nach dem Entwurf, Ätzen und Bestücken der Treiber-Platine ging es an den Bau des LED Würfels. Für den LED Würfel habe ich mich für diffuse blaue LEDs entschieden. Diese gibt es bereits in hohen Stückzahlen für unter 10€ auf Ebay. Von klaren LEDs würde ich eher abraten, da sich bei denen das Licht nicht gut streut. Außerdem sollte beim Kauf von LEDs immer an extra LEDs gedacht werden, falls welche beim Bau kaputt gehen oder der Halbleiter schon kaputt geliefert wurde.

Um die LEDs mehr oder weniger gerade zu löten (was mir leider nicht wirklich gut gelungen ist), habe ich einen Trick aus einem Instructables genommen und mir ein Muster mit Bohrungen für eine Ebene in eine alte Holzplatte gebohrt und die Biegungen aufgezeichnet. So konnten sich die LEDs nur minimal bewegen und man beim Biegen der Beine auch nichts falsch machen.

Beim zusammensetzten der Ebenen sollte man sich unbedingt einen Abstandshalter besorgen, damit die Ebenen auch alle gleich hoch sind! Dafür reicht häufig eine 9V Batterie o.ä. aus. Ebenfalls ist es ratsam sich jemanden dazu zu holen der einem beim Löten assistieren kann.

Ein Tipp: Die LEDs vor einbau auf Funktionsfähigkeit prüfen, so bleibt es einem erspart eine LED aus der Mitte auszubauen und zu ersetzten,

Programmierung

Zur Kommunikation mit dem MCP Baustein, habe ich die Bibliothek von Adafruit verwendet. Das Folgende Script hat mehrere vorinstallierte Animationen die im Loop durchlaufen.

Hinweis: Bei diesem Skript kann nicht jede LED ganz individuell angesteuert werden! Möchte man beispielsweise ein diagonales Kreuz anzeigen, wird dies ein ausgefülltes Kreuz, welches von oben zu betrachten ist. Dies passiert, da jede Ebene angesteuert wird und dabei bestimmte Spalten.
Dies kann man jedoch mit PWM Signalen umgangen werden. Das ist das gleiche Prinzip wird auch bspw. in Bus.LED-Anzeigen verwendet um die gleichen Probleme zu umgehen.

  1. /*
  2.  * Spalten: Layer:
  3.  * 15 14 13 12 0
  4.  * 11 10 9 8 1
  5.  * 7 6 5 4 2
  6.  * 3 2 1 0 3
  7.  *
  8.  *
  9.  *
  10.  *
  11.  * Spalten: Layer:
  12.  * 11 14 13 8 0
  13.  * 12 9 10 15 1
  14.  * 7 6 5 4 2
  15.  * 3 2 1 0 3
  16.  *
  17.  */
  18.  
  19. #include "Arduino.h"
  20. #include <Wire.h>
  21. #include "Adafruit_MCP23017.h"
  22.  
  23. Adafruit_MCP23017 mcp;
  24. int layer1Pin = 15;
  25. int layer2Pin = 16;
  26. int layer3Pin = 17;
  27. int layer4Pin = 14;
  28. int layers[4] = {layer1Pin, layer2Pin, layer3Pin, layer4Pin};
  29. int cols[4][4] = {{3, 2, 1, 0},{7, 6, 5, 4},{12, 9, 10, 15},{11, 14, 13, 8}};
  30.  
  31. void setup() {
  32. Serial.begin(9600);
  33. mcp.begin();
  34.  
  35. for (int i=0; i <= 15; i++){
  36. mcp.pinMode(i, OUTPUT);
  37. mcp.digitalWrite(i, HIGH);
  38. delay(10);
  39. }
  40.  
  41. pinMode(layer1Pin, OUTPUT);
  42. pinMode(layer2Pin, OUTPUT);
  43. pinMode(layer3Pin, OUTPUT);
  44. pinMode(layer4Pin, OUTPUT);
  45.  
  46. digitalWrite(layer1Pin, HIGH);
  47. digitalWrite(layer2Pin, HIGH);
  48. digitalWrite(layer3Pin, HIGH);
  49. digitalWrite(layer4Pin, HIGH);
  50. }
  51.  
  52. void loop() {
  53. circle();
  54. mixer(4);
  55. full_scan(2);
  56. stamp(2);
  57. ball(60);
  58. rain(25);
  59. randomflicker(250);
  60. }
  61.  
  62. void clear() {
  63. for (int i=0; i <= 15; i++){
  64. mcp.digitalWrite(i, HIGH);
  65. }
  66. }
  67.  
  68. void mixer(int rounds) {
  69. digitalWrite(layer1Pin, LOW);
  70. digitalWrite(layer2Pin, LOW);
  71. digitalWrite(layer3Pin, LOW);
  72. digitalWrite(layer4Pin, LOW);
  73.  
  74. for(int i=0; i<=rounds; i++){
  75. mcp.digitalWrite(cols[3][1], LOW);
  76. mcp.digitalWrite(cols[3][2], LOW);
  77. mcp.digitalWrite(cols[2][1], LOW);
  78. mcp.digitalWrite(cols[2][2], LOW);
  79. mcp.digitalWrite(cols[1][1], LOW);
  80. mcp.digitalWrite(cols[1][2], LOW);
  81. mcp.digitalWrite(cols[0][1], LOW);
  82. mcp.digitalWrite(cols[0][2], LOW);
  83. delay(165); clear();
  84.  
  85. mcp.digitalWrite(cols[3][0], LOW);
  86. mcp.digitalWrite(cols[2][1], LOW);
  87. mcp.digitalWrite(cols[1][2], LOW);
  88. mcp.digitalWrite(cols[0][3], LOW);
  89. delay(165); clear();
  90.  
  91. mcp.digitalWrite(cols[2][0], LOW);
  92. mcp.digitalWrite(cols[2][1], LOW);
  93. mcp.digitalWrite(cols[2][2], LOW);
  94. mcp.digitalWrite(cols[2][3], LOW);
  95. mcp.digitalWrite(cols[1][0], LOW);
  96. mcp.digitalWrite(cols[1][2], LOW);
  97. mcp.digitalWrite(cols[0][1], LOW);
  98. mcp.digitalWrite(cols[1][3], LOW);
  99. delay(165); clear();
  100.  
  101. mcp.digitalWrite(cols[0][0], LOW);
  102. mcp.digitalWrite(cols[1][1], LOW);
  103. mcp.digitalWrite(cols[2][2], LOW);
  104. mcp.digitalWrite(cols[3][3], LOW);
  105. delay(165); clear();
  106. }
  107. }
  108.  
  109. void full_scan(int rounds) {
  110. for(int i=0; i<=rounds; i++){
  111. digitalWrite(layer1Pin, LOW);
  112. digitalWrite(layer2Pin, LOW);
  113. digitalWrite(layer3Pin, LOW);
  114. digitalWrite(layer4Pin, LOW);
  115.  
  116. mcp.digitalWrite(cols[3][0], LOW);
  117. mcp.digitalWrite(cols[2][0], LOW);
  118. mcp.digitalWrite(cols[1][0], LOW);
  119. mcp.digitalWrite(cols[0][0], LOW);
  120. delay(100); clear();
  121.  
  122. mcp.digitalWrite(cols[3][1], LOW);
  123. mcp.digitalWrite(cols[2][1], LOW);
  124. mcp.digitalWrite(cols[1][1], LOW);
  125. mcp.digitalWrite(cols[0][1], LOW);
  126. delay(100); clear();
  127.  
  128. mcp.digitalWrite(cols[3][2], LOW);
  129. mcp.digitalWrite(cols[2][2], LOW);
  130. mcp.digitalWrite(cols[1][2], LOW);
  131. mcp.digitalWrite(cols[0][2], LOW);
  132. delay(100); clear();
  133.  
  134. mcp.digitalWrite(cols[3][3], LOW);
  135. mcp.digitalWrite(cols[2][3], LOW);
  136. mcp.digitalWrite(cols[1][3], LOW);
  137. mcp.digitalWrite(cols[0][3], LOW);
  138. delay(100); clear();
  139.  
  140.  
  141.  
  142. mcp.digitalWrite(cols[3][3], LOW);
  143. mcp.digitalWrite(cols[2][3], LOW);
  144. mcp.digitalWrite(cols[1][3], LOW);
  145. mcp.digitalWrite(cols[0][3], LOW);
  146. delay(100); clear();
  147.  
  148. mcp.digitalWrite(cols[3][2], LOW);
  149. mcp.digitalWrite(cols[2][2], LOW);
  150. mcp.digitalWrite(cols[1][2], LOW);
  151. mcp.digitalWrite(cols[0][2], LOW);
  152. delay(100); clear();
  153.  
  154. mcp.digitalWrite(cols[3][1], LOW);
  155. mcp.digitalWrite(cols[2][1], LOW);
  156. mcp.digitalWrite(cols[1][1], LOW);
  157. mcp.digitalWrite(cols[0][1], LOW);
  158. delay(100); clear();
  159.  
  160. mcp.digitalWrite(cols[3][0], LOW);
  161. mcp.digitalWrite(cols[2][0], LOW);
  162. mcp.digitalWrite(cols[1][0], LOW);
  163. mcp.digitalWrite(cols[0][0], LOW);
  164. delay(100); clear();
  165.  
  166.  
  167.  
  168. for (int i=0; i <= 15; i++){
  169. mcp.digitalWrite(i, LOW);
  170. }
  171.  
  172. digitalWrite(layer1Pin, HIGH);
  173. digitalWrite(layer2Pin, HIGH);
  174. digitalWrite(layer3Pin, HIGH);
  175. digitalWrite(layer4Pin, LOW);
  176. delay(100);
  177.  
  178. digitalWrite(layer1Pin, HIGH);
  179. digitalWrite(layer2Pin, HIGH);
  180. digitalWrite(layer3Pin, LOW);
  181. digitalWrite(layer4Pin, HIGH);
  182. delay(100);
  183.  
  184. digitalWrite(layer1Pin, HIGH);
  185. digitalWrite(layer2Pin, LOW);
  186. digitalWrite(layer3Pin, HIGH);
  187. digitalWrite(layer4Pin, HIGH);
  188. delay(100);
  189.  
  190. digitalWrite(layer1Pin, LOW);
  191. digitalWrite(layer2Pin, HIGH);
  192. digitalWrite(layer3Pin, HIGH);
  193. digitalWrite(layer4Pin, HIGH);
  194. delay(100);
  195.  
  196. digitalWrite(layer1Pin, LOW);
  197. digitalWrite(layer2Pin, HIGH);
  198. digitalWrite(layer3Pin, HIGH);
  199. digitalWrite(layer4Pin, HIGH);
  200. delay(100);
  201.  
  202. digitalWrite(layer1Pin, HIGH);
  203. digitalWrite(layer2Pin, LOW);
  204. digitalWrite(layer3Pin, HIGH);
  205. digitalWrite(layer4Pin, HIGH);
  206. delay(100);
  207.  
  208. digitalWrite(layer1Pin, HIGH);
  209. digitalWrite(layer2Pin, HIGH);
  210. digitalWrite(layer3Pin, LOW);
  211. digitalWrite(layer4Pin, HIGH);
  212. delay(100);
  213.  
  214. digitalWrite(layer1Pin, HIGH);
  215. digitalWrite(layer2Pin, HIGH);
  216. digitalWrite(layer3Pin, HIGH);
  217. digitalWrite(layer4Pin, LOW);
  218. delay(100);
  219. clear();
  220. }
  221. }
  222.  
  223. void ball(int rounds) {
  224. // https://electronics.stackexchange.com/a/67140
  225. // size of the space
  226. int height = 4;
  227. int width = 4;
  228. int depth = 4;
  229.  
  230. // Starting position of the shape
  231. int xpos = 2;
  232. int ypos = 2;
  233. int zpos = 1;
  234.  
  235. // Speed of the shape along each axis
  236. int xspeed = 1;
  237. int yspeed = 2;
  238. int zspeed = 1;
  239.  
  240. int choice[2] = {-1, 1};
  241. int xdirection = 1; // Left or Right
  242. int ydirection = 1; // Top to Bottom
  243. int zdirection = 1; // front or back
  244.  
  245. for(int i=0; i<=rounds; i++){
  246. clear();
  247. digitalWrite(layer1Pin, HIGH);
  248. digitalWrite(layer2Pin, HIGH);
  249. digitalWrite(layer3Pin, HIGH);
  250. digitalWrite(layer4Pin, HIGH);
  251. // Update the position of the shape
  252. xpos = xpos + ( xspeed * xdirection );
  253. ypos = ypos + ( yspeed * ydirection );
  254. zpos = zpos + ( zspeed * zdirection );
  255.  
  256. if (xpos >= width || xpos < 0) {
  257. xpos = constrain(xpos, 0, width-1);
  258. xdirection *= -1;
  259. }
  260. if (ypos >= height || ypos < 0) {
  261. ypos = constrain(ypos, 0, height-1);
  262. ydirection *= -1;
  263. }
  264.  
  265. if (zpos >= depth || zpos < 0) {
  266. zpos = constrain(zpos, 0, depth-1);
  267. zdirection *= -1;
  268. }
  269.  
  270.  
  271. if(random(0, 6) == 1) {
  272. xdirection *= -1;
  273. } else if(random(0, 6) == 1) {
  274. ydirection *= -1;
  275. } else if(random(0, 6) == 1) {
  276. zdirection *= -1;
  277. }
  278.  
  279. digitalWrite(layers[ypos], LOW);
  280. mcp.digitalWrite(cols[xpos][3-zpos], LOW);
  281. delay(150);
  282. }
  283. }
  284.  
  285. void randomflicker(int rounds) {
  286. for(int i=0; i<=rounds; i++){
  287. clear();
  288. digitalWrite(layer1Pin, HIGH);
  289. digitalWrite(layer2Pin, HIGH);
  290. digitalWrite(layer3Pin, HIGH);
  291. digitalWrite(layer4Pin, HIGH);
  292.  
  293. int randomLayer = random(0, 4);
  294. int randomLed = random(0, 16);
  295.  
  296. digitalWrite(layers[randomLayer], LOW);
  297. mcp.digitalWrite(randomLed, LOW);
  298. delay(10);
  299. }
  300. }
  301.  
  302. void stamp(int rounds) {
  303. for(int i=0; i<=rounds; i++){
  304. digitalWrite(layer1Pin, LOW);
  305. digitalWrite(layer2Pin, HIGH);
  306. digitalWrite(layer3Pin, HIGH);
  307. digitalWrite(layer4Pin, HIGH);
  308. clear();
  309.  
  310. for (int i=0; i <= 15; i++){
  311. mcp.digitalWrite(i, LOW);
  312. }
  313.  
  314. delay(1500);
  315.  
  316. digitalWrite(layer1Pin, HIGH);
  317. digitalWrite(layer2Pin, LOW);
  318. digitalWrite(layer3Pin, HIGH);
  319. digitalWrite(layer4Pin, HIGH);
  320. delay(50);
  321.  
  322. digitalWrite(layer1Pin, HIGH);
  323. digitalWrite(layer2Pin, HIGH);
  324. digitalWrite(layer3Pin, LOW);
  325. digitalWrite(layer4Pin, HIGH);
  326. delay(50);
  327.  
  328. digitalWrite(layer1Pin, HIGH);
  329. digitalWrite(layer2Pin, HIGH);
  330. digitalWrite(layer3Pin, HIGH);
  331. digitalWrite(layer4Pin, LOW);
  332. delay(1000);
  333.  
  334. digitalWrite(layer1Pin, HIGH);
  335. digitalWrite(layer2Pin, HIGH);
  336. digitalWrite(layer3Pin, LOW);
  337. digitalWrite(layer4Pin, HIGH);
  338. delay(400);
  339.  
  340. digitalWrite(layer1Pin, HIGH);
  341. digitalWrite(layer2Pin, LOW);
  342. digitalWrite(layer3Pin, HIGH);
  343. digitalWrite(layer4Pin, HIGH);
  344. delay(400);
  345.  
  346. }
  347. }
  348.  
  349. void rain(int rounds) {
  350. for(int i=0; i<=rounds; i++){
  351. digitalWrite(layer1Pin, HIGH);
  352. digitalWrite(layer2Pin, HIGH);
  353. digitalWrite(layer3Pin, HIGH);
  354. digitalWrite(layer4Pin, HIGH);
  355. clear();
  356. mcp.digitalWrite(random(0, 15), LOW);
  357.  
  358. digitalWrite(layer1Pin, LOW);
  359. digitalWrite(layer2Pin, HIGH);
  360. digitalWrite(layer3Pin, HIGH);
  361. digitalWrite(layer4Pin, HIGH);
  362. delay(85);
  363.  
  364. digitalWrite(layer1Pin, HIGH);
  365. digitalWrite(layer2Pin, LOW);
  366. digitalWrite(layer3Pin, HIGH);
  367. digitalWrite(layer4Pin, HIGH);
  368. delay(85);
  369.  
  370. digitalWrite(layer1Pin, HIGH);
  371. digitalWrite(layer2Pin, HIGH);
  372. digitalWrite(layer3Pin, LOW);
  373. digitalWrite(layer4Pin, HIGH);
  374. delay(85);
  375.  
  376. digitalWrite(layer1Pin, HIGH);
  377. digitalWrite(layer2Pin, HIGH);
  378. digitalWrite(layer3Pin, HIGH);
  379. digitalWrite(layer4Pin, LOW);
  380. delay(85);
  381.  
  382. digitalWrite(layer1Pin, HIGH);
  383. digitalWrite(layer2Pin, HIGH);
  384. digitalWrite(layer3Pin, HIGH);
  385. digitalWrite(layer4Pin, HIGH);
  386. delay(150);
  387. }
  388. }
  389.  
  390. void circle() {
  391. clear();
  392. digitalWrite(layer1Pin, LOW);
  393. digitalWrite(layer2Pin, LOW);
  394. digitalWrite(layer3Pin, LOW);
  395. digitalWrite(layer4Pin, LOW);
  396.  
  397. mcp.digitalWrite(cols[0][3], LOW); delay(150);
  398. mcp.digitalWrite(cols[0][2], LOW); delay(150);
  399. mcp.digitalWrite(cols[0][1], LOW); delay(150);
  400. mcp.digitalWrite(cols[0][0], LOW); delay(150);
  401. mcp.digitalWrite(cols[1][0], LOW); delay(150);
  402. mcp.digitalWrite(cols[2][0], LOW); delay(150);
  403. mcp.digitalWrite(cols[3][0], LOW); delay(150);
  404. mcp.digitalWrite(cols[3][1], LOW); delay(150);
  405. mcp.digitalWrite(cols[3][2], LOW); delay(150);
  406. mcp.digitalWrite(cols[3][3], LOW); delay(150);
  407. mcp.digitalWrite(cols[2][3], LOW); delay(150);
  408. mcp.digitalWrite(cols[1][3], LOW); delay(150);
  409. mcp.digitalWrite(cols[1][2], LOW); delay(150);
  410. mcp.digitalWrite(cols[1][1], LOW); delay(150);
  411. mcp.digitalWrite(cols[2][1], LOW); delay(150);
  412. mcp.digitalWrite(cols[2][2], LOW); delay(150);
  413.  
  414. delay(1500);
  415.  
  416. mcp.digitalWrite(cols[2][2], HIGH); delay(150);
  417. mcp.digitalWrite(cols[2][1], HIGH); delay(150);
  418. mcp.digitalWrite(cols[1][1], HIGH); delay(150);
  419. mcp.digitalWrite(cols[1][2], HIGH); delay(150);
  420. mcp.digitalWrite(cols[1][3], HIGH); delay(150);
  421. mcp.digitalWrite(cols[2][3], HIGH); delay(150);
  422. mcp.digitalWrite(cols[3][3], HIGH); delay(150);
  423. mcp.digitalWrite(cols[3][2], HIGH); delay(150);
  424. mcp.digitalWrite(cols[3][1], HIGH); delay(150);
  425. mcp.digitalWrite(cols[3][0], HIGH); delay(150);
  426. mcp.digitalWrite(cols[2][0], HIGH); delay(150);
  427. mcp.digitalWrite(cols[1][0], HIGH); delay(150);
  428. mcp.digitalWrite(cols[0][0], HIGH); delay(150);
  429. mcp.digitalWrite(cols[0][1], HIGH); delay(150);
  430. mcp.digitalWrite(cols[0][2], HIGH); delay(150);
  431. mcp.digitalWrite(cols[0][3], HIGH); delay(150);
  432.  
  433. delay(1500);
  434. }

Die erste Ansteuerung

Erste Animation auf dem LED Würfel
4x4x4 LED Cube IC Steckschaum

LED Würfel „Dämmung“

Das Gehäuse

Für meinen LED Würfel habe ich von Ebay eine klare Acryl-Vitrine 96mm x 96mm x 148mm besorgt, in der der Würfel sowie der Treiber mit dem Arduino platz hat. Die Elektronik habe ich mit schwarzem IC Steckschaum versteckt. Dieser hat die schönen Eigenschaften, dass dieser Antistatisch ist und die dünnen Beine der LEDs lassen sich ziemlich leicht durchdrücken. Den Steckschaum habe ich in 5 einzelne Stücke geschnitten und mit Heißkleber zusammengeklebt.

Für die Stromversorgung bin ich nicht an die angebaute Externe Stromversorgung gegangen, sondern direkt an den Arduino. Dies bietet mir die Möglichkeit auch im nachhinein noch die Animationen zu ändern, ohne viel rum zu basteln. Das USB-Typ B Kabel habe ich hinten mit einer Zugentlastung angebracht.
Für den Betrieb des LED Würfels reicht ein Smartphone Netzteil (5V/ mind. 1A) voll und ganz aus.

 

 

Schreiben Sie einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.