Im FabLab tüfteln und arbeiten wir gemeinsam an technischen Projekten. Hier findest du eine kleine Auswahl aktueller Projekte im FabLab. Natürlich bist du herzlich eingeladen, vorbeizukommen, selbst mitzubauen oder eigene Projekt-Ideen mitzubringen.
Zum Senso: Gehäuse im 3D-Druck erzeugt und aus lasergeschnittenen Arcylteilen gefertigt. Hardware: Arduino Standard, Mp3-Player (DFPlayer mini), LCD-Display 16×2, 4 Tastenfelder jeweils mit individuell ansteuerbarer LED und 4 zusätzliche Bedientasten. Funktionen: Verschiedene Spiele mit Licht- und Soundeffekten und MP3-Player zum Musikabspielen mit verschiedenen Lichteffekten. Besonderheiten: Objektorientierte Software, die speziell mit den beschränkten Fähigkeiten eines normalen Arduino umgehen muss in Bezug auf Speicherkapazität und Rechenleistung. Ergebnis: Absolut direkte Bedienung ohne jegliche Hänger. Reagiert jederzeit auf alle Tastendrücke. Ebenfalls Aufbau einer Menüstruktur, die mit den geringen Ressourcen umgehen kann und eine gute Basis ist für weitere Projekte. Mit dieser Software wurde speziell gezeigt, wie eine Echtzeitfähigkeit mit einem Mikrocontroller erreicht werden kann.
Arduino Code
include LiquidCrystal_I2C lcd(0x27, 16, 2); // Den Tasten wird eine programminterne Nummer zugeordnet: define oben 0 define rechts 1 define unten 2 define links 3 define ESC 4 define Pfeil_links 5 define Pfeil_rechts 6 define Enter 7 // Nur zur Info für LCD-Darstellung: ä:“\xE1″, ö :“\xEF“, ü :“\xF5″, ß:“\xE2″, °:“\xDF“, μ:“\xE4″, Ω:“\xF4″, komplett helles Feld:“\xFF“ // Prototyping: class Tastenklasse; class KeySoundSet; class LEDklasse; class Menuepunkt; class Liedwahl; void leiser(); void lauter(); void soundStop(); void Spiele_Lied(uint8_t Songnumber); void schreibe(const String& a, const String& b); void schreibe_loops(uint8_t Zahl); class Tastenklasse { private: int Pinnummer; // die hardwaremäßige Pinnummer der Taste am Arduino bool Key_pressed; // hier wird der letzte Tastenzustand gespeichert 1=war gedrückt public: bool Tastenevent; // Das Ereignis des Drückens der Taste, nicht der zustand des Gedrücktseins ! void Setup(int Pinnummer_) { Pinnummer = Pinnummer_; // Die private Variable Pinnummer wird mit dem Wert der übergebenen Variablen belegt pinMode(Pinnummer, INPUT); // Weil eine Taste angeschlossen ist muss der Eingang natürlich auf INPUT gesetzt werden Key_pressed = false; } void Eventabfrage() { // setzt Key_pressed auf true, wenn die Taste gedrückt ist und Tastenevent auf true, wenn sie bei der letzten Abfrage nicht gedrückt war Tastenevent = false; if (digitalRead(Pinnummer) == 1) { if (! Key_pressed) Tastenevent = true; // wenn die Taste seither nicht gedrückt war: Key_pressed = true; } else Key_pressed = false; } bool pressed() { return (Key_pressed); } }; class LEDklasse { private: int Pinnummer; bool ist_ein; int Helligkeit; public: void Setup (int Pinnummer_, int Helligkeit_) { Pinnummer = Pinnummer_; Helligkeit = Helligkeit_; // Helligkeit ist die aktuell eingestellte Helligkeit ist_ein = false; } void ein() { ist_ein = true; } void aus() { ist_ein = false; } void set_Helligkeit(int Helligkeit_) { Helligkeit = Helligkeit_; } void heller(int Schritt) { Helligkeit += Schritt; if (Helligkeit > 255) Helligkeit = 255; } void dunkler(int Schritt) { Helligkeit -= Schritt; if (Helligkeit < 0) Helligkeit = 0; } void strahle() { Helligkeit = (Helligkeit << 1) + 1; if (Helligkeit > 255) Helligkeit = 255; } void verblasse() { Helligkeit = Helligkeit >> 1; } void set(bool soft_) { if (ist_ein or soft_) analogWrite(Pinnummer, Helligkeit); else analogWrite(Pinnummer, 0); } }; LEDklasse LED[4]; class KeySoundSet { private: uint8_t _ESC, _Arraynummer; uint8_t Soundset = 190; //Tastensounds: 190 = Liednummer 190..194, 195 = Liednummer 195..199, 200 = Liednummer 200..204 4 Töne und ein „Zong“ uint8_t _Number_of_Sets; public: void set(uint8_t ESC_, uint8_t Arraynummer_, uint8_t Number_of_Sets_) { ESC = ESC; Arraynummer = Arraynummer; Number_of_Sets = Number_of_Sets; } uint8_t Soundnumber() { return (Soundset); } uint8_t loop(Tastenklasse Tastatur[]) { if (Tastatur[Pfeil_links].Tastenevent) { if (Soundset > 190) Soundset -= 5; } if (Tastatur[Pfeil_rechts].Tastenevent) { if (Soundset < 5 * (_Number_of_Sets - 1) + 190) Soundset += 5; } schreibe("", "Set: " + String((Soundset - 190) / 5 + 1) + " try it!"); for (uint8_t i = 0; i < 4; i++) { if (Tastatur[i].pressed()) LED[i].ein(); else LED[i].aus(); if (Tastatur[i].Tastenevent) Spiele_Lied(Soundset + i); } if (Tastatur[Enter].Tastenevent) Spiele_Lied(Soundset + 4); // Zong auf Enter if (Tastatur[ESC].Tastenevent) return (_ESC); else return (_Arraynummer); } }; KeySoundSet Tastensound; class Menuepunkt { private: uint8_t _links, _rechts, _ESC, _Enter, Eigene_Menuenummer; public: void set(uint8_t links_, uint8_t rechts_, uint8_t ESC_, uint8_t Enter_, uint8_t Eigene_Menuenummer_) { _links = links_; _rechts = rechts_; _ESC = ESC_; _Enter = Enter_; Eigene_Menuenummer = Eigene_Menuenummer_; } uint8_t loop(Tastenklasse Tastatur[]) { if (Tastatur[Pfeil_links].Tastenevent) return (_links); if (Tastatur[Pfeil_rechts].Tastenevent)return (_rechts); if (Tastatur[ESC].Tastenevent) return (_ESC); if (Tastatur[Enter].Tastenevent) return (_Enter); else return (Eigene_Menuenummer); } }; class Senso_Classic { private: uint8_t Key_event[4], Key_pressed[4], _ESC, Eigene_Menuenummer, Spielzustand, Zaehler, Vorgabetaste[50], akt_elem; // Spielzustand 0=Zufallsfolge erzeugen, 1 = spielt vor, 2 = nachspielen; unsigned long timestamp; public: void set(uint8_t ESC_, uint8_t Eigene_Menuenummer_) { ESC = ESC; Eigene_Menuenummer = Eigene_Menuenummer_; Spielzustand = 0; Zaehler = 0; akt_elem = 0; } uint8_t loop(Tastenklasse Tastatur[]) { Key_event[0] = Tastatur[oben].Tastenevent; Key_event[1] = Tastatur[rechts].Tastenevent; Key_event[2] = Tastatur[unten].Tastenevent; Key_event[3] = Tastatur[links].Tastenevent; Key_pressed[0] = Tastatur[oben].pressed(); Key_pressed[1] = Tastatur[rechts].pressed(); Key_pressed[2] = Tastatur[unten].pressed(); Key_pressed[3] = Tastatur[links].pressed(); if (Spielzustand == 0 and Zaehler == 0) { // Spiel hat noch nicht begonnen for (auto &i : LED) i.set_Helligkeit(255); randomSeed(millis()); // den Randombefehl mit der aktuellen zufälligen Systemzeit setzen (macht diesen wirklich zufällig) for (auto &i : Vorgabetaste) i = random(4); // vorbelegen einer Zufallsfolge Zaehler = 1; // Zaehler gibt vor, wieviele Tasten man sich merken muss Spielzustand = 1; // 1=Abspielen der Zufallsfolge / 2=Nachspielen timestamp = millis(); schreibe_loops(Zaehler); } else { if (Spielzustand == 1 and millis() - 300 > timestamp) for (auto &i : LED) i.aus(); if (Spielzustand == 1 and millis() - 500 > timestamp) { // Abspielen timestamp = millis(); if (akt_elem < Zaehler) { LED[Vorgabetaste[akt_elem]].ein(); Spiele_Lied(Tastensound.Soundnumber() + Vorgabetaste[akt_elem]); akt_elem++; } else { Spielzustand = 2; // Umschalten auf Nachspielen akt_elem = 0; } } else if (Spielzustand == 2) { // Nachspielen for (uint8_t i = 0; i < 4; i++) { // alle LEDs ausschalten if (not Key_pressed[i]) LED[i].aus(); // nur, wenn die Taste losgelassen wurde. } if (Key_event[Vorgabetaste[akt_elem]]) { // es wurde die richtige Taste gedrückt LED[Vorgabetaste[akt_elem]].ein(); Spiele_Lied(Tastensound.Soundnumber() + Vorgabetaste[akt_elem]); akt_elem++; } else { for (uint8_t i = 0; i < 4; i++) { if (Key_event[i] and i != Vorgabetaste[akt_elem]) // Falsch getippt { Spiele_Lied(Tastensound.Soundnumber() +4); delay (2500); Zaehler = 0; akt_elem = 0; Spielzustand = 0; } } } if (akt_elem == Zaehler) { // alle wurden richtig getippt, zurück zum Abspielen Spielzustand = 1; akt_elem = 0; Zaehler++; schreibe_loops(Zaehler); timestamp = millis() + 1000; } } } if (Tastatur[ESC].Tastenevent) { Spielzustand = 0; Zaehler = 0; akt_elem = 0; return (_ESC); } else return (Eigene_Menuenummer); } }; class Reaktionsspiel { private: uint8_t _ESC, Eigene_Menuenummer, Spielzustand, Countdown, Taste; // Spielzustand 0 = Neu gestartet, 1 = Startmodus, 2 = Taste wird vorgegeben, 3 = warte auf Tastendruck; unsigned long Restzeit, Punkte, Top_Punkte, timestamp; public: void set(uint8_t ESC_, uint8_t Eigene_Menuenummer_) { ESC = ESC; Eigene_Menuenummer = Eigene_Menuenummer_; Spielzustand = 0; Top_Punkte = 0; } uint8_t loop(Tastenklasse Tastatur[]) { switch (Spielzustand) { case 0 : for (auto &i : LED) { i.set_Helligkeit(255); i.aus(); } Countdown = 5; Restzeit = 20000; Punkte = 0; timestamp = millis(); Spielzustand = 1; break; case 1: schreibe("Start in " + String(Countdown) + " sek. ", "Punkte:" + String(Punkte) + " Top:" + String(Top_Punkte) + " "); if (millis() >= timestamp + 1000) { Countdown--; timestamp = millis(); } if (Countdown == 0) Spielzustand = 2; break; case 2: randomSeed(millis()); // den Randombefehl mit der aktuellen zufälligen Systemzeit setzen (macht diesen wirklich zufällig) Taste = random(4); schreibe("Rest: " + String(Restzeit) + "ms ", "Punkte:" + String(Punkte) + " Top:" + String(Top_Punkte) + " "); Spiele_Lied(Tastensound.Soundnumber() + Taste); LED[Taste].ein(); timestamp = millis(); Spielzustand = 3; break; case 3: Restzeit -= (millis() - timestamp); // Restzeit wird reduziert um die Zeit, die seit dem letzten Aufruf dieser Methode vergangen ist. timestamp = millis(); schreibe("Rest: " + String(Restzeit) + "ms ", "Punkte:" + String(Punkte) + " Top:" + String(Top_Punkte) + " "); for (int i = 0; i < 4; i++) { if (Tastatur[i].Tastenevent) { if (i == Taste) { // Die richtige Taste wurde gedrückt soundStop(); Punkte++; LED[Taste].aus(); if (Punkte > Top_Punkte) Top_Punkte = Punkte; Spielzustand = 2; } else { // die falsche Taste wurde gedrückt Spiele_Lied(Tastensound.Soundnumber() +4); for (auto &i : LED) i.ein(); delay (2500); for (auto &i : LED) i.aus(); if (Punkte > Top_Punkte) Top_Punkte = Punkte; Spielzustand = 0; } } } if (Restzeit > 1000000) { // Spielzeit ist vorbei (wegen unsigned wird Restzeit nicht negativ, sondern riesengroß) for (auto &i : LED) i.aus(); Spielzustand = 0; } break; } if (Tastatur[ESC].Tastenevent) { for (auto &i : LED) i.aus(); Spielzustand = 0; return (_ESC); } else return (Eigene_Menuenummer); } }; class Liedwahl { private: uint8_t _links, _rechts, _ESC, _Enter; bool _Lied_spielt = false; public: void set(uint8_t links_, uint8_t rechts_, uint8_t ESC_, uint8_t Enter_) { links = links; rechts = rechts; ESC = ESC; Enter = Enter; } uint8_t loop(Tastenklasse Tastatur[]) { if (Tastatur[links].pressed()) leiser(); if (Tastatur[rechts].pressed()) lauter(); if (Tastatur[oben].pressed()) for (auto &i : LED) i.heller(5); if (Tastatur[unten].pressed()) for (auto &i : LED) i.dunkler(5); if (Tastatur[Pfeil_links].Tastenevent) { _Lied_spielt = false; return (_links); } if (Tastatur[Pfeil_rechts].Tastenevent) { _Lied_spielt = false; return (_rechts); } if (Tastatur[ESC].Tastenevent) { soundStop(); if (_Lied_spielt) { _Lied_spielt = false; return (_Enter); } else return (_ESC); } if (Tastatur[Enter].pressed()) { if (not _Lied_spielt) { Spiele_Lied(_Enter); _Lied_spielt = true; } } return (_Enter); } }; class DiscoSpeedSet { private: uint8_t _ESC, _Arraynummer; public: unsigned long Discotimer = 200; void set(uint8_t ESC, uint8_t Arraynummer_) { ESC = ESC; Arraynummer = Arraynummer; } uint8_t loop(Tastenklasse Tastatur[]) { if (Tastatur[Pfeil_links].pressed() and _Discotimer > 0) _Discotimer--; if (Tastatur[Pfeil_rechts].pressed() and _Discotimer < 2000) _Discotimer++; schreibe("set Pause: " + String(_Discotimer) + "ms ", "Nur f\xF5r Typ hard"); if (Tastatur[oben].pressed()) { // obere Taste = heller LED[oben].ein(); for (auto &i : LED) i.heller(5); } else LED[oben].aus(); if (Tastatur[unten].pressed()) { // untere Taste = dunkler LED[unten].ein(); for (auto &i : LED) i.dunkler(5); } if (Tastatur[ESC].Tastenevent) { for (auto &i : LED) i.aus(); return (_ESC); } else return (_Arraynummer); } }; class DiscoTypeSet { private: uint8_t _ESC, _Arraynummer, DiscoTyp, Discolight; uint16_t Lichtspiele[10] = {0x0F0F, 0xEDB7, 0x6C93, 0x1248, 0xA5A5, 0xF5A0, 0xAA55, 0x2585, 0xA4A1, 0x0000}; // jede Stelle entspricht einem der vier hintereinander abzuspielenden Bilder (rückwärts) /* 0 0000 4 0100 8 1000 C 1100 1 0001 5 0101 9 1001 D 1101 2 0010 6 0110 A 1010 E 1110 3 0011 7 0111 B 1011 F 1111 */ uint8_t Anzahl_Lichtspiele = 10; unsigned long _timestamp; uint8_t Stufe; public: bool soft; void set(uint8_t ESC_, uint8_t Arraynummer_) { _ESC = ESC_; _Arraynummer = Arraynummer_; _timestamp = millis(); Discolight = 0; DiscoTyp = 3; soft = false; Stufe = 0; } uint8_t loop(Tastenklasse Tastatur[]) { if (Tastatur[Pfeil_links].Tastenevent && (DiscoTyp > 0)) { if (DiscoTyp == Anzahl_Lichtspiele) for (auto &i : LED) i.set_Helligkeit(200); // wenn von Soft auf Hard gewechselt wurde, dann Helligkeit einstellen DiscoTyp--; for (auto &i : LED) i.set_Helligkeit(200); } if (Tastatur[Pfeil_rechts].Tastenevent && (DiscoTyp < 2 * Anzahl_Lichtspiele - 1)) { DiscoTyp++; if (DiscoTyp == Anzahl_Lichtspiele) for (auto &i : LED) { i.set_Helligkeit(0); // wenn von Hart auf Soft gewechselt wurde, dann Helligkeit einstellen i.ein(); } } if (DiscoTyp >= Anzahl_Lichtspiele) schreibe("", " Typ: " + String(DiscoTyp - Anzahl_Lichtspiele) + " soft "); else { if (Tastatur[oben].pressed()) { // obere Taste = heller LED[oben].ein(); for (auto &i : LED) i.heller(5); } else LED[oben].aus(); if (Tastatur[unten].pressed()) { // untere Taste = dunkler LED[unten].ein(); for (auto &i : LED) i.dunkler(5); } schreibe("", " Typ: " + String(DiscoTyp) + " hard "); } if (Tastatur[ESC].Tastenevent) { for (auto &i : LED) i.aus(); return (_ESC); } else return (_Arraynummer); } void next_Discolight(unsigned long timestep) { if (DiscoTyp < Anzahl_Lichtspiele) { // hard if (millis() >= timestep + _timestamp) { _timestamp = millis(); if (++Discolight > 3) Discolight = 0; } if ((Lichtspiele[DiscoTyp] >> (4 * Discolight + 3)) & 1) LED[oben].ein(); else LED[oben].aus(); if ((Lichtspiele[DiscoTyp] >> (4 * Discolight + 2)) & 1) LED[rechts].ein(); else LED[rechts].aus(); if ((Lichtspiele[DiscoTyp] >> (4 * Discolight + 1)) & 1) LED[unten].ein(); else LED[unten].aus(); if ((Lichtspiele[DiscoTyp] >> (4 * Discolight + 0)) & 1) LED[links].ein(); else LED[links].aus(); } else // soft { for (auto &i : LED) i.ein(); Stufe ++; if (Stufe < 8) { if ((Lichtspiele[DiscoTyp - Anzahl_Lichtspiele] >> (4 * Discolight + 3)) & 1) LED[oben].strahle(); else LED[oben].verblasse(); if ((Lichtspiele[DiscoTyp - Anzahl_Lichtspiele] >> (4 * Discolight + 2)) & 1) LED[rechts].strahle(); else LED[rechts].verblasse(); if ((Lichtspiele[DiscoTyp - Anzahl_Lichtspiele] >> (4 * Discolight + 1)) & 1) LED[unten].strahle(); else LED[unten].verblasse(); if ((Lichtspiele[DiscoTyp - Anzahl_Lichtspiele] >> (4 * Discolight + 0)) & 1) LED[links].strahle(); else LED[links].verblasse(); } else { Stufe = 0; if (++Discolight > 3) Discolight = 0; } } } }; void lauter() { Serial.write(0x7e); Serial.write(0xff); Serial.write(0x04); Serial.write(0x04); Serial.write(0x00); Serial.write(0xef); delay(30); } void leiser() { Serial.write(0x7e); Serial.write(0xff); Serial.write(0x04); Serial.write(0x05); Serial.write(0x00); Serial.write(0xef); delay(30); } void soundPlay() { Serial.write(0x7e); Serial.write(0xff); Serial.write(0x06); Serial.write(0x0d); Serial.write(0x00); Serial.write(0x00); Serial.write(0x00); Serial.write(0xef); } void soundStop() { Serial.write(0x7e); Serial.write(0xff); Serial.write(0x06); Serial.write(0x0e); Serial.write(0x00); Serial.write(0x00); Serial.write(0x00); Serial.write(0xef); } // Auf der SD-Karte im Verzeichnis „01“ stehen abspielbare Dateien – Beispiel: „000.mp3“ (nr=0) oder „017.mp3“ (nr=17) void soundSelect(uint8_t nr) { Serial.write(0x7e); Serial.write(0xff); Serial.write(0x06); Serial.write(0x0f); Serial.write(0x00); Serial.write(0x01); Serial.write(nr); Serial.write(0xef); } void Spiele_Lied(uint8_t Songnumber) { soundStop(); delay(20); soundSelect(Songnumber); delay(20); soundPlay(); } String Textzeile1 = “ „, Textzeile2 = “ „; void schreibe(const String& a, const String& b) { // lcd.clear(); if (a != Textzeile1) { lcd.setCursor(0, 0); lcd.print(a); } if (b != Textzeile2) { lcd.setCursor(0, 1); lcd.print(b); } } void schreibe_loops(uint8_t Zahl) { char Text[16] {„Nachspielen „}; Text[13] = (Zahl – Zahl % 10) / 10 + 48; Text[14] = Zahl % 10 + 48; schreibe(„“, Text); } // Definition unserer globalen Variablen: Tastenklasse Key[8]; uint8_t Spielzustand = 0, Zaehler = 0, Vorgabetaste[50], akt_elem = 0; // Spiel-1: Spielzustand 0 = spielt vor, 1 = nachspielen unsigned long timestamp; Menuepunkt Menue[8]; Liedwahl Lied[17]; Senso_Classic Spiel_Senso_Classic; Reaktionsspiel Spiel_Reaktion; DiscoTypeSet DiscoElement; DiscoSpeedSet Einstellungsmenue_discospeed; uint8_t akt_Menue = 0; void setup() { // Hier im setup() wird alles Mögliche konfiguriert: lcd.init(); // initialisierung das LCD mit I²C-Schnittstelle lcd.backlight(); // LCD-Hintergrundbeleuchtung einschalten Serial.begin(9600, SERIAL_8N1); // Serielle Kommunikation mit dem MP3-Player einstellen // Hardwaremäßige Pinbelegung am Arduino UNO: // Belegung von Anto und Jan Key[oben].Setup(7); // 7 identisch Key[rechts].Setup(2); // 2 identisch Key[unten].Setup(4); // 4 identisch Key[links].Setup(8); // 9 Key[ESC].Setup(17); // A3 identisch Key[Pfeil_links].Setup(16); // A2 identisch Key[Pfeil_rechts].Setup(15); // A1 identisch Key[Enter].Setup(14); // A0 identisch // LEDs alle auf PWM-Ausgängen: LED[oben].Setup(6, 100); // 6 identisch LED[rechts].Setup(3, 100); // 3 identisch LED[unten].Setup(5, 100); // 5 identisch LED[links].Setup(9, 100); // 10 // Initilisierug aller Menüpunkte: Menue[0].set(0, 1, 0, 3, 0); // Hauptmenü-Spiele spielen set(links, rechts, ESC, Enter, eigene Arraynummer) Menue[1].set(0, 2, 1, 100, 1); // Hauptmenü-Lieder abspielen Menue[2].set(1, 2, 2, 5, 2); // Hauptmenü-Einstellungen Menue[3].set(3, 4, 0, 50, 3); // Spiele spielen-Senso Classic Menue[4].set(3, 4, 0, 51, 4); // Spiele spielen-Reaktionsspiel Menue[5].set(5, 6, 2, 98, 5); // Einstellungen-disco Typ Menue[6].set(5, 7, 2, 99, 6); // Einstellungen-disco speed Menue[7].set(6, 7, 2, 97, 7); // Einstellung des Tastensoundtyps Spiel_Senso_Classic.set(3, 50); // Classic Senso spielen set(ESC, eigene Arraynummer) Spiel_Reaktion.set(4, 51); // reaktionsspiel set(ESC, eigene Arraynummer) Tastensound.set(7, 97, 2); // Tastensound festlegen set(ESC, eigene Arraynummer, Anzahl verschiedener Soundstiele) DiscoElement.set(5, 98); // disco Typ festlegen set(ESC, eigene Arraynummer) Einstellungsmenue_discospeed.set(6, 99); // disco speed-Set speed set(ESC, eigene Arraynummer) Lied[0].set(100, 101, 1, 100); // Bei den Menüpunkten Lied steht immer die Arraynummer + 100 ! Lied[1].set(100, 102, 1, 101); // Das ist gleichzeitig die Songnummer Lied[2].set(101, 103, 1, 102); Lied[3].set(102, 104, 1, 103); Lied[4].set(103, 105, 1, 104); Lied[5].set(104, 106, 1, 105); Lied[6].set(105, 107, 1, 106); Lied[7].set(106, 108, 1, 107); Lied[8].set(107, 109, 1, 108); Lied[9].set(108, 110, 1, 109); Lied[10].set(109, 111, 1, 110); Lied[11].set(110, 112, 1, 111); Lied[12].set(111, 113, 1, 112); Lied[13].set(112, 114, 1, 113); Lied[14].set(113, 115, 1, 114); Lied[15].set(114, 116, 1, 115); Lied[16].set(115, 116, 1, 116); timestamp = millis(); } void loop() { for (auto &i : Key) i.Eventabfrage(); // Über alle Tasten (setzt die Tastenevents): for (auto &i : LED) i.set(DiscoElement.soft); // LEDs ansteuern auf den im letzten Durchlauf gewählten Zustand if (akt_Menue < 50) akt_Menue = Menue[akt_Menue].loop(Key); // allgemeine Menüs else if (akt_Menue == 50) akt_Menue = Spiel_Senso_Classic.loop(Key); // Senso_Classic else if (akt_Menue == 51) akt_Menue = Spiel_Reaktion.loop(Key); // Reaktionsspiel else if (akt_Menue == 97) akt_Menue = Tastensound.loop(Key); // Tastensounds auswählen else if (akt_Menue == 98) akt_Menue = DiscoElement.loop(Key); // Disco Typ // Ab hier alles, bei dem das Discolicht läuft else if (akt_Menue == 99) akt_Menue = Einstellungsmenue_discospeed.loop(Key); // Disco speed else akt_Menue = Lied[akt_Menue – 100].loop(Key); // MP3-Player // Alle akt_Menue über 100 gehören den absielbaren Liedern. if (akt_Menue >= 98) DiscoElement.next_Discolight(Einstellungsmenue_discospeed._Discotimer); // Discolicht anzeigen String ENTER_TO_PLAY=“ Enter to play „; switch (akt_Menue) { // Die Displayansteuerung findet hier statt, damit die Texte im Programm und nicht im Datenspeicher sind, der sonst überläuft: case 0: schreibe (“ Hauptmenue „, “ Spiele spielen „); break; case 1: schreibe (“ Hauptmenue „, „Lieder abspielen“); break; case 2: schreibe (“ Hauptmenue „, “ Einstellungen „); break; case 3: schreibe („Spiel ausw\xE1hlen „, “ Senso Classic „); break; case 4: schreibe („Spiel ausw\xE1hlen „, “ Reaktionsspiel „); break; case 5: schreibe (“ Einstellungen „, „W\xE1hle disco Typ „); break; case 6: schreibe (“ Einstellungen „, „set disco speed „); break; case 7: schreibe (“ Einstellungen „, “ Tastensounds „); break; case 50: schreibe (“ Senso Classic „, „“); break; case 51: schreibe(„“, „“); break; case 97: schreibe („W\xE1hle Tastent\xEFne“, „“); break; case 98: schreibe („W\xE1hle disco Typ“, „“); break; case 99: schreibe („“, „“); break; case 100: schreibe (ENTER_TO_PLAY, “ Mashup Germany „); break; case 101: schreibe (ENTER_TO_PLAY, „Bohemian Rapsodi“); break; case 102: schreibe (ENTER_TO_PLAY, “ J\xE1germeister „); break; case 103: schreibe (ENTER_TO_PLAY, “ Monster „); break; case 104: schreibe (ENTER_TO_PLAY, “ Bommerlunder „); break; case 105: schreibe (ENTER_TO_PLAY, “ Coconutsong „); break; case 106: schreibe (ENTER_TO_PLAY, “ Unity „); break; case 107: schreibe (ENTER_TO_PLAY, „We will rock 01 „); break; case 108: schreibe (ENTER_TO_PLAY, “ Teenagers „); break; case 109: schreibe (ENTER_TO_PLAY, „We are the champ“); break; case 110: schreibe (ENTER_TO_PLAY, „We will rock you“); break; case 111: schreibe (ENTER_TO_PLAY, “ Killer Queen „); break; case 112: schreibe (ENTER_TO_PLAY, “ Bites the dust „); break; case 113: schreibe (ENTER_TO_PLAY, “ Bicycle race „); break; case 114: schreibe (ENTER_TO_PLAY, „Crazy … thing „); break; case 115: schreibe (ENTER_TO_PLAY, „Dont stop me now“); break; case 116: schreibe (ENTER_TO_PLAY, “ Bottomed girls „); break; } }
Der Elch als Weihnachtsschmuck ist ein kleines Projekt, welches mit einem Arduino Uno oder Klone, einer LED mit Vorwiderstand und einem kleinen Servo auskommt. Die Besonderheit liegt darin, dass der Elch mit dem USB-Kabel, mit dem er über die Arduino IDE programmiert wird, mit dem PC verbunden bleibt. Dann wird das Python3-Skript Elch_1.py gestartet, welches im aktuellen Verzeichnis die Datei Elch.png benötigt. Zwischen dem PC und dem Arduino wird nun eine serielle Verbindung aufgebaut, die als Beispiel für eine fehlertolerante und besonders zuverlässige serielle Datenübertragung herangezogen werden kann.
Jetzt kann man wunderschön ein Setup für den Elch finden, welches einem besonders gefällt: Man schaltet die Automatik aus, fährt den Kopf in die gewünschte obere Position und speichert diese ab und macht dasselbe mit der unteren Position. Anschließend werden noch die Nickfrequenz des Kopfes und die Blinkfrequenz der LED eingestellt. Wer will kann auch im Stillstand einfach nur die Helligkeit der LED regeln. Wenn alles gespeichert ist, dann läuft der Elch auch ohne das Python Skript mit jeder USB-Stromversorgung oder einem Arduino Uno geeigneten Netzteil.
Damit eine serielle Verbindung zustande kommt, muss immer zuerst der Elch eingesteckt sein und dann das Python Skript gestartet werden.
Arduino Code
include include uint8_t A = 0, B = 0, Position, Helligkeit, Automatik = 1; // Jetzt lesen wir aus dem EEPORM unsere beiden Automatikpositionen: uint8_t Automatik_Helligkeit_0 = EEPROM[0]; uint8_t Automatik_Position_0 = EEPROM[1]; uint8_t Automatik_Helligkeit_1 = EEPROM[2]; uint8_t Automatik_Position_1 = EEPROM[3]; uint8_t Schrittweite_LED = EEPROM[4]; uint8_t Schrittweite_Kopf = EEPROM[5]; uint8_t anzufahrende_Helligkeit = Automatik_Helligkeit_1; uint8_t anzufahrende_Position = Automatik_Position_1; Servo Hals; unsigned long Zeit = millis(); void fahre(uint8_t LED, uint8_t Halsposition) { analogWrite(3, LED); Hals.writeMicroseconds(1000 + 10 * Halsposition); } void setup() { Serial.begin(19200); Hals.attach(2); // Pin 3 ist PWM, dafür muss nichts angegeben werden Helligkeit = Automatik_Helligkeit_0; Position = Automatik_Position_0; fahre(Helligkeit, Position); } // Wir bekommen über die serielle Schnittstelle Befehle in Form von einzelnen Bytes, von denen die linken 4 Bits // ein Befehl sind und die rechten vier Bits die dazugehörigen Daten. // Wir benötigen zum Empfangen eines ganzen Bytes dann z.B. mindestens zwei Befehle um 8 Bits für ein ganzes Byte // zu übertragen. // Wir definieren jetzt selbst Befehle, die wir benoetigen, dafür haben wir nur vier Bits zur Verfuegung. // Die Befehle haben also Nummern. void loop() { int i = Serial.read(); if (i != -1) { uint8_t EGB = i; // EGB=eingelesenes Byte – die linken 4 Bit geben den Befehl, die rechten den Wert switch (EGB >> 4) {// Jeder case ist ein Befehl – Wir lesen den in den linken vier Bits stehenden Befehl case 0x0: // Befehl 0: Die rechten 4 Bit von A setzen A = (A & 0xf0) | (uint8_t(EGB & 0x0f)); break; case 0x1: // Befehl 1: Die linken 4 Bit von A setzen A = (A & 0x0f) | (uint8_t(EGB & 0x0f)) << 4; break; case 0x2: // Befehl 2: Die rechten 4 Bit von B setzen B = (B & 0xf0) | (uint8_t(EGB & 0x0f)); break; case 0x3: // Befehl 3: Die linken 4 Bit von B setzen B = (B & 0x0f) | (uint8_t(EGB & 0x0f)) << 4; break; case 0x4: // Befehl 4: Automatik Ein/Aus Automatik = (uint8_t(EGB & 0x01)); if (Automatik == 1) { anzufahrende_Position = Automatik_Position_0; anzufahrende_Helligkeit = Automatik_Helligkeit_0; } break; case 0x5: // Befehl 5: Ausführen if (Automatik == 0) { Position = A; Helligkeit = B; fahre(Helligkeit, Position); } break; case 0x6: // Befehl: Derzeitige Position als Automatik_0 speichern if (Automatik == 0) { Automatik_Helligkeit_0 = B; Automatik_Position_0 = A; } break; case 0x7: // Wegen der Übertragung in 7bit ASCII geht bei den Befehlen nicht mehr als 0x7f // Daher werden beim Befehl 0x07 die unteren 4 Bits für weitere Befehlsvarianten // verwendet: switch (EGB & 0x0f) { case 0x00: // Befehl: Derzeitige Position als Automatik_1 speichern if (Automatik == 0) { Automatik_Helligkeit_1 = B; Automatik_Position_1 = A; } break; case 0x01: // Befehl: Automatikpositionen ins EEPROM schreiben EEPROM[0] = Automatik_Helligkeit_0; EEPROM[1] = Automatik_Position_0; EEPROM[2] = Automatik_Helligkeit_1; EEPROM[3] = Automatik_Position_1; EEPROM[4] = Schrittweite_LED; EEPROM[5] = Schrittweite_Kopf; break; case 0x02: // Befehl: Geschwindigkeitswerte schreiben Schrittweite_LED = B; Schrittweite_Kopf = A; break; } break; } } if (Automatik == 1) { if (millis() > Zeit + 20) { Zeit = millis(); if (anzufahrende_Helligkeit > Helligkeit) { if (anzufahrende_Helligkeit – Schrittweite_LED <= Helligkeit) Helligkeit = anzufahrende_Helligkeit; else Helligkeit += Schrittweite_LED; } else { if (anzufahrende_Helligkeit + Schrittweite_LED >= Helligkeit) Helligkeit = anzufahrende_Helligkeit; else Helligkeit -= Schrittweite_LED; } if (anzufahrende_Helligkeit == Helligkeit) { if (anzufahrende_Helligkeit == Automatik_Helligkeit_0) anzufahrende_Helligkeit = Automatik_Helligkeit_1; else anzufahrende_Helligkeit = Automatik_Helligkeit_0; } if (anzufahrende_Position > Position) { if (anzufahrende_Position – Schrittweite_Kopf <= Position) Position = anzufahrende_Position; else Position += Schrittweite_Kopf; } else { if (anzufahrende_Position + Schrittweite_Kopf >= Position) Position = anzufahrende_Position; else Position -= Schrittweite_Kopf; } if (anzufahrende_Position == Position) { if (anzufahrende_Position == Automatik_Position_0) anzufahrende_Position = Automatik_Position_1; else anzufahrende_Position = Automatik_Position_0; } fahre(Helligkeit, Position); } } }
Python Code
!/usr/bin/python –– coding: utf-8 –– als root machen: chmod o+rw /dev/ttyUSB0 oder alternativ den benutzer in die gruppe dialout hinzufuegen from tkinter import * import serial import sys import time Schnittstelle initialisieren Schnittstelle_gefunden=False print(„“) for Schnittstelle in („/dev/ttyUSB0″,“/dev/ttyUSB1″,“/dev/ttyACM0″,“/dev/ttyACM1“): print(„Ich versuche die Serielle Schnittstelle „+Schnittstelle) try: ser=serial.Serial(port=Schnittstelle,baudrate=19200,bytesize=serial.EIGHTBITS,parity=serial.PARITY_NONE,stopbits=1, timeout=10,xonxoff=False,rtscts=False,dsrdtr=False,write_timeout=1,inter_byte_timeout=None) Schnittstelle_gefunden=True except: pass if Schnittstelle_gefunden: break if not Schnittstelle_gefunden: sys.exit(„Ich konnte keine Verbindung zum Arduino aufbauen“) else: print(„Verbindung zum Arduino steht -> 2 Sekunden warten …“) time.sleep(2) die linken 4 Bits geben immer an, um was es sich handelt, die rechten sind die Daten def writeA(i): # i ist ein 1-Byte Integer # wird verwendet, um das darzustellende Zeichen zu uebertragen ser.write(chr(0x00 | (i & 0x0f)).encode(„ascii“)) # das ergibt für den Empfaenger den Befehl, die rechten vier Bits von A zu setzen) ser.write(chr(0x10 | (i & 0xf0)>>4).encode(„ascii“)) # das ergibt für den Empfaenger den Befehl, die linken vier Bits von A zu setzen def writeB(i): # i ist ein 1-Byte Integer # wird verwendet, um die Displayzeile anzugeben (0..1) ser.write(chr(0x20 | (i & 0x0f)).encode(„ascii“)) # das ergibt für den Empfaenger den Befehl, die rechten vier Bits von A zu setzen) ser.write(chr(0x30 | (i & 0xf0)>>4).encode(„ascii“)) # das ergibt für den Empfaenger den Befehl, die linken vier Bits von A zu setzen def go(): ser.write(chr(0x50).encode(„ascii“)) # der Arduino soll das Zeichen darstellen def move_head_with_mouse(*a): writeA(Kopf_Schieber.get()) go() def LED_Helligkeit(*a): writeB(LED_Schieber.get()) go() def Automatik(*a): if v.get()==0: ser.write(chr(0x40).encode(„ascii“)) writeA(Kopf_Schieber.get()) writeB(LED_Schieber.get()) go() else: ser.write(chr(0x41).encode(„ascii“)) def save_Point_0 (*a): ser.write(chr(0x60).encode(„ascii“)) def save_Point_1 (*a): ser.write(chr(0x70).encode(„ascii“)) def save_to_EEPROM (*a): ser.write(chr(0x71).encode(„ascii“)) def speed_function(*a): writeA(Kopf_speed.get()) writeB(LED_speed.get()) ser.write(chr(0x72).encode(„ascii“)) def Programm_Beenden(*a): main.destroy() main=Tk(className=“ die Elch Programmiersoftware“) Anlegen der Widget-Objekte Bild=PhotoImage(file=“./Elch.png“) Bildobjekt=Label(main, image=Bild) Kopf_Schieber = Scale(main, from_=100, to=0, orient=VERTICAL, length=200, resolution=1, command=move_head_with_mouse) LED_Schieber = Scale(main, from_=0, to=255, orient=HORIZONTAL, length=200, resolution=1, command=LED_Helligkeit) Kopf_speed = Scale(main, from_=1, to=16, orient=VERTICAL, length=100, resolution=1, command=speed_function) LED_speed = Scale(main, from_=0, to=100, orient=VERTICAL, length=100, resolution=1, command=speed_function) Kopf_Schieber.set(„100.0“) LED_Schieber.set(„0“) Kopf_speed.set(„4“) LED_speed.set(„50“) v= IntVar() Automatikschalter=Checkbutton(main, text=“Automatik „, variable=v, command=Automatik) Automatikschalter.var=v Save_0 = Button(main, text=“save as Pos.0″, command=save_Point_0) Save_1 = Button(main, text=“save as Pos.1″, command=save_Point_1) save_to_EEPROM= Button(main, text=“save to EEPROM“, command=save_to_EEPROM) Beenden = Button(main, text=“Beenden“, command=Programm_Beenden) Seriellen Puffer löschen: ser.read(ser.inWaiting()) Aufrufen der Widget-Objekte Bildobjekt.pack() Kopf_Schieber.place(x=435,y=30) LED_Schieber.place(x=25,y=70) Kopf_speed.place(x=80,y=280) LED_speed.place(x=25,y=280) Automatikschalter.place(x=200,y=365) Automatikschalter.deselect() Save_0.place(x=366,y=255, width=120) Save_1.place(x=366,y=290, width=120) save_to_EEPROM.place(x=366,y=325, width=120) Beenden.place(x=366,y=360, width=120) ser.write(chr(0x40).encode(„ascii“)) # Automatik ausschalten move_head_with_mouse() LED_Helligkeit() go() main.mainloop()
Die Diskettenorgel ist ein aus acht alten Diskettenlaufwerken bestehender MIDI Player, der über einen Raspberry Pi MiniPC MIDI Signale per USB-serieller Schnittstelle an einen Arduino UNO übergibt. Dieser steuert dann die einzelnen Schrittmotoren der alten Diskettenlaufwerke mit unterschiedlichen Geschwindigkeiten, abgeglichen zur Tonhöhe des Midifiles an.
Dadurch ist es möglich ganze Lieder abzuspielen und über das Touchdisplay die Wiedergabe zu steuern.
Das gewünschte Bügelbild wird auf die Steckplatte mit den gewünschten Farben gesteckt, dort wo LED´s genutzt werden sollen, werden durchsichtige Bügelperlen eingesetzt. Eine Seite wird gebügelt. 3mm LEDs werden dann in den gewünschten Positionen auf der nicht gebügelten Seite eingesteckt und verlötet.
Der Vorwiderstand wird dann für 5V USB Spannung berechnet.
Der LED Tisch ist eine Matrix aus 12×12 RGB LEDs, welche über einen Teensy Mikrocontroller und mehrere Shields (USB, OctoWS2811, Arduino Proto) angesteuert werden.