Projekt: SloMo-Frame
Ich hatte so einen Slow-Motion-Frame auf einem YouTube-Video entdeckt und mich gefragt ob ich sowas auch selbst basteln könnte. Also habe ich im Sommer mal angefangen mich damit zu beschäftigen. Und nachdem ich einen ersten Prototyp gebaut hatte, kam ich zu dem Schluß dass das so schwer doch gar nicht sein kann. Und tatsächlich ist es recht einfach gewesen.
Rechts ein Foto von meinem SloMo-Frame.
Weiter lesen? Klicke die Headline um den ganzen Artikel zu sehen!
Materialliste
- Teensy 3.2 für 20,- EUR
- Elektromagnet 12,5 Ohm (Fundstück, Preis?)
- Einen L298N Treiberbaustein für umgerechnet 3,- EUR
- 1m LED Streifen Stripe Strip für 4,- EUR
- Rotary Encoder mit RGB LED für 4,25 EUR
- Rotary Encoder Breakout 3,11 EUR
- Rotary Encoder Drehknopf Transparent für 0,99 EUR
- Eine Taschentuch-Box vom KiK für 3,99 EUR
- Ausrangiertes Netzteil von einem alten Router (12V, 2A)
- Kabel-Schalter
- Diverse Holzprofile aus dem Bauhaus
- Ein Stück schwarzer Stoff
- Eine Dose schwarze Lackfarbe
- Diverse Kleinteile, Holzleim und Klebestreifen
Alles zusammen unter 55,- EUR.
Prinzip
Der SloMo-Frame funktioniert wie früher die Stop-Motion-Filme. Es wird aus einer Reihe von Bewegungen nur ein kleiner Ausschnitt angezeigt. Hier wird ein Objekt (Feder, Ast etc) in Schwingung versetzt. Zum Beispiel mit 50 Herz (hz). Jetzt wird dieses Objekt nur immer kurz beleuchtet. Zum Beispiel mit Lichtblitzen mit ebenfalls 50hz. Dann sieht der Betrachter immer nur einen kleinen Ausschnitt der ganzen Bewegung. Wenn ich das Licht jetzt mit zB 52hz moduliere, sieht man immer einen etwas anderen Ausschnitt und eine langsame Bewegung ergibt sich.
Versuch
Zuerst startete ich mit einem Versuch. Also etwas Fischertechnik aus der Jugend aus dem Keller geholt und einen Testaufbau gemacht:
Damit konnte ich das ganze Konzept mal ausprobieren. Man sieht im Hintergrund die drei LED auf dem Streifen, mit denen ich getestet habe (immer etwas dunkel machen um den Effekt zu sehen). Nachdem das Konzept bewiesen und der erste Code funktionierte, konnte es an das Bauen der magischen Kiste gehen.
Aufbau
Zuerst dachte ich an einen Rahmen oder Ring für die LEDs. Bei meinen Tests viel mir aber auf, dass ich die LEDs immer vor das Objekt gehalten habe. Und daher ist eine Beleuchtung von der Seite eigentlich kontraproduktiv. Nach einigem hin und her und Diskussionen und rumfragen entschied ich mich für eine Box und LED's ringsum mit 45° Winkel.
So habe ich die Box vorbereitet, bemalt und aufgebaut
Die Box wie sie gekauft wurde. Gutes Rohmaterial. Aus dem Deckel habe ich die kleine Frontplatte gebaut (Loch und Senkung für den Dreh-Encoder). Die vorhandenen Scharniere hab ich wieder verwendet. |
Zuerst habe ich die Box innen und aussen zweimal mit dem schwarzen Lack lackiert. Die zukünftigen Klebestellen innen habe ich ausgespart. |
So habe ich die Profilhölzer festgeleimt. |
Meine Kappsäge ist bei solchen Dingen unglaublich wertvoll. Alle Gehrungen für die Profilhölzer und auch die Abdeckung etc lassen sich damit prima und unkompliziert sägen :-) |
Hier noch die gesägten Blendhölzer, damit die Vorderseite nicht so gebastelt ausschaut. |
Nachdem die Schaltung komplett auf dem Breadboard lief, war es jetzt nur etwas Lötarbeit und Basteln um das alles zusammenzubringen.
Probleme
Es gab verschiedene Hindernisse. Das Loch für den Dreh-Encoder war etwas Tricky, da ich keinen Senker habe und der Encoder zu kurz war um durch die ganze Platte gesteckt zu werden. Also hab ich mit einem Holzbohrer so lange auf der Rückseite vorsichtig gekreist, bis eine passende Aussparung um das Loch entstand.
Bei ersten Tests ist sofort aufgefallen, dass die Rückseite innen durch den Lack so glänzt, dass es nicht gut anzusehen ist (siehe nächstes Bild). Nach Diskussion mit meiner Frau entschieden wir uns für schwarzen Stoff. Da haben wir dann ein Stück zurechtgeschnitten und mit doppelseitigem Klebeband auf die Rückseite innen geklebt. Der Stoff kann auch das Gebastel und die Schaltung unten abdecken.
Netzteil und Stromversorgung
Ich hab ein altes Netzteil von einem alten D-Link Switch gefunden. Das liefert 12V und 2A bei guter Gleichspannung. Die ganze SloMo-Box benötigt nur knapp 1A, aber das passt ja dann prima. Ich habe lediglich einen Kabel-Schalter dazwischen gelötet, damit man den SloMo Frame einfach ein- und ausschalten kann.
Der L298N Treiberbaustein hat glücklicherweise bereits einen Spannungsteiler auf 5V mit an Bord. Da kann ich die 5V für den Teensy direkt beziehen ohne eine weitere Schaltung aufbauen zu müssen. Für die Encoder LED mussten noch ein paar Wiederstände rein (680 Ohm). Etwas Schrumpfschlauch isoliert die Wiederstände.
Bedienung
Der SloMo-Frame wird mit nur einem Dreh-Drück-Knopf bedient. Durch die Leuchtfarbe zeigt er den aktuellen Bedienmodus an. Je nach Farbe ändert man einen der drei Parameter:
- Türkis: Frequenz mit der der Magnet schwingt
- Blau: Modulation (wie schnell sich etwas optisch bewegt)
- Grün: Blitzdauer, wie lange das Licht jeweils aufblitzen soll
Jedes anklicken wechselt den Modus eins weiter. Wenn eine Einstellung für 10 Sekunden bestehen bleibt, werden die Werte ins EEPROM geschrieben. Dadurch startet der SloMo-Frame beim nächsten Mal mit den selben Werten. Die speicherung wird durch ein kurzes Aufblitzen der LED's angezeigt.
Programmierung
Wie immer programmiere ich alles so, dass es unabhängig von der Frequenz/Geschwindigkeit des Microprozessors läuft. Also mittels vergangener Zeit. Die Befehle micros() und millis() sind da natürlich wichtig. Mittels statischer Variablen lässt sich eine Zeitsteuerung prima einrichten.
Hier der Code für meinen SloMo-Frame:
/**
* === SloMo Frame ===
*
* (w) 2018/2019 by
* Volker Schmid
* 78176 Blumberg
* Germany
*
* License: Public Domain
*
* Written for Teensy 3.2 (https://www.pjrc.com/teensy/)
*
* For project details and fotos, visit my German blog at
* http://blog.inspirant.de
*/
#define ENCODER_OPTIMIZE_INTERRUPTS
#include "Arduino.h"
#include <Encoder.h>
#include <EEPROM.h>
const int lights = 0; // lights Module
const int magnet = 1; // magnet Module
const int ledBlue = 5; // encoder LED
const int ledGreen = 6; // encoder LED
const int enc1pin = 2; // rot encoder A
const int enc2pin = 3; // rot encoder B
const int encPush = 4; // push button
enum PushStatus {
STATE_SPEED, // encoder edits frequency
STATE_MODULATION, // encoder edits modulation
STATE_DURATION // encoder edits flash duration
};
int state = STATE_SPEED;
// eeprom fields for storage (longs)
enum eepromFields {
EEPROM_SPEED = 0,
EEPROM_DURATION = 4,
EEPROM_MODULATION = 8
};
long speed_delay_ms = 8000; // frequency (wait microseconds)
long led_duration = 800; // how long LED will flash
long modulation_ms = 220; // slomo effect in microseconds
// time at which last changes were done (0=no changes yet)
unsigned long lastDirtyTime = 0;
// save values to eeprom after 10 seconds of no change
const int saveAfter = 10000; // 10.000 milliseconds = 10 sec
Encoder encoder(enc1pin, enc2pin); // init new encoder object
// Info: The setup function is called once at startup of the sketch
void setup() {
pinMode(lights, OUTPUT);
pinMode(magnet, OUTPUT);
pinMode(ledBlue, OUTPUT);
pinMode(ledGreen, OUTPUT);
digitalWrite(lights, LOW);
digitalWrite(magnet, LOW);
digitalWrite(ledBlue, LOW);
digitalWrite(ledGreen, LOW);
pinMode(encPush, INPUT);
encoder.write(10000); // simply some start value
// check for EEPROM values
if (EEPROM.get(EEPROM_SPEED, speed_delay_ms) > 0) {
// seems some values are stored
speed_delay_ms = EEPROM.get(EEPROM_SPEED, speed_delay_ms);
led_duration = EEPROM.get(EEPROM_DURATION, led_duration);
modulation_ms = EEPROM.get(EEPROM_MODULATION, modulation_ms);
}
}
// Returns true if encPush was released (!)
// Does a 250ms debounce.
int CheckButton() {
static int pushed = 0;
static unsigned long nextSwitch = millis() + 250;
if (digitalRead(encPush) == HIGH &&
pushed == 0 &&
millis() > nextSwitch) {
pushed = 1;
}
if (digitalRead(encPush) == LOW && pushed == 1){
pushed = 0;
nextSwitch = millis() + 250; // debounce
return true;
}
return false;
}
// Check encoder
// Return +1 or 0 or -1 due to movement
int GetEncoderDirection() {
static long lastValue = encoder.read();
if (encoder.read() > lastValue) {
lastValue = encoder.read();
lastDirtyTime = millis();
return 1;
}
if (encoder.read() == lastValue) {
return 0;
}
lastValue = encoder.read();
lastDirtyTime = millis();
return -1;
}
void DoEncoder() {
// Check which value to change
switch (state) {
case STATE_SPEED:
speed_delay_ms = speed_delay_ms - GetEncoderDirection() * 10;
if (speed_delay_ms < 1000) { speed_delay_ms = 1000; }
if (speed_delay_ms > 15000) { speed_delay_ms = 15000; }
if (led_duration > speed_delay_ms) { led_duration = speed_delay_ms; }
break;
case STATE_MODULATION:
modulation_ms = modulation_ms + GetEncoderDirection() * 10;
if (modulation_ms < 0) { modulation_ms = 0; }
if (modulation_ms > 3000) { modulation_ms = 3000; }
break;
case STATE_DURATION:
led_duration = led_duration + GetEncoderDirection() * 10;
if (led_duration < 10) { led_duration = 10; }
if (led_duration > speed_delay_ms) { led_duration = speed_delay_ms; }
break;
}
// Switch modes on push
if (CheckButton()) {
switch (state) {
case STATE_SPEED:
state = STATE_MODULATION;
digitalWrite(ledBlue, LOW);
digitalWrite(ledGreen, HIGH);
break;
case STATE_MODULATION:
state = STATE_DURATION;
digitalWrite(ledBlue, HIGH);
digitalWrite(ledGreen, LOW);
break;
case STATE_DURATION:
state = STATE_SPEED;
digitalWrite(ledBlue, LOW);
digitalWrite(ledGreen, LOW);
break;
}
}
}
// Flash LED lights if due
void DoLights() {
static unsigned long nextSwitch = micros();
static int lastState = 0;
if (micros() < nextSwitch) {
return;
}
if (lastState == 0) {
digitalWrite(lights, HIGH);
lastState = 1;
// short on
nextSwitch = micros() + led_duration;
} else {
digitalWrite(lights, LOW);
lastState = 0;
// long off
nextSwitch = micros() + (4 * speed_delay_ms) - led_duration - modulation_ms;
}
}
// Turn magent on/off if due
void DoMagnet() {
static unsigned long nextSwitch = micros();
static int lastState = 0;
if (micros() < nextSwitch) {
return;
}
nextSwitch = micros() + speed_delay_ms;
if (lastState == 0) {
digitalWrite(magnet, LOW);
lastState = 1;
} else {
digitalWrite(magnet, HIGH);
lastState = 0;
}
}
void SaveEeprom() {
if (lastDirtyTime == 0) {
return; // nothingh changed
}
if (millis() > lastDirtyTime + saveAfter - 100) {
// notify saving by lights for 1/10 second before
digitalWrite(lights, HIGH);
}
if (millis() > lastDirtyTime + saveAfter) {
// save values
EEPROM.put(EEPROM_SPEED, speed_delay_ms);
EEPROM.put(EEPROM_DURATION, led_duration);
EEPROM.put(EEPROM_MODULATION, modulation_ms);
lastDirtyTime = 0; // set to "nothing changed"
}
}
// Main loop
void loop() {
DoEncoder();
DoLights();
DoMagnet();
SaveEeprom();
}
Hier ein Video
In folgendem Video kann man sehen wie es funktioniert. Ich bin mit der Kamera bewusst mal zur Seite gefahren, damit man die LEDs innen sehen kann.
Der schwarze Balken der sich immer über das Bild bewegt kommt übrigens aus der Synchronisation der Kameraabtastung des Handys mit der Blinkfrequenz aus dem SloMo-Frame. Man hat das selbe Symptom, wenn man ältere Röhrenmonitore filmt.
Kommentare
Neuer Kommentar