Problemstellung
Der LEGO Weihnachtszug (10254) ist meiner Meinung nach schön gelungen.
Noch schöner ist er, wenn er als Weihnachtsdekoration im Wohnzimmer herumfährt.
Und da gibt es leider von LEGO keine gute Lösung. Zum einen müssen ein paar Sachen umgebaut werden, so dass der Zug einfach nicht mehr so schön ist und
zum anderen ist er einfach viel zu schnell unterwegs.
Man kann ihn also nicht gemütlich vor sich hin tuckern lassen und das hat mich immer gestört.
Und da ich noch nichts passendes dazu im Netz gefunden habe, bin ich mich dann selber an die Lösungsfindung gemacht.
Und der erste Versuch sieht schon mal vielversprechend aus, wenn ich noch die Kabel besser verstecke, ist es ganz passabel:
Der Lösungsansatz
Da ich grundsätzlich eigentlich nur drei Sachen möchte, sollte dies einfach zu bewerkstelligen sein:
- Der Zug soll sich gemächlich fortbewegen.
- Durch die Motorisierung soll die Ästhetik so gut wie möglich bewahrt werden.
- Der gelieferte Strom muss Austauschbar und nachhaltig sein.
Motorisierung
Hier habe ich auf einen LEGO-Zugmotor-Replikat zurückgegriffen, welches sehr günstig zu beschaffen war.
Man kann sich auch auf den Originalen LEGO-Zugmotor stützen, welcher dazu gedacht ist. Ich habe es zwar nicht getestet, aber sehe keine Probleme mit dem Original.
Um den Motor zu betreiben und möglichst ohne lange Kabel zu vermeiden, habe ich kurzerhand den Stecker etwa in der Hälfte abgeschnitten.
Möchte man dies nicht, kann man auch einfach ein Zusatzkabel opfern um an die Drähte zu gelangen.
Bei dieser Variante möchte ich lediglich, dass der Zug Vorwärts fährt und mit nur einer Geschwindigkeit.
Möchte man den Zug über zum Beispiel eine ESP oder Arduino steuern, würde ich eine H-Brücke / Motortreiber (L293D) einsetzen.
Motor anschliessen
Dieser Motor verwendet 4 Kabel: VCC (9-Volt), GND, C1, C2.
VCC muss nicht mit 9-Volt versorgt werden, je weniger Spannung, je langsamer dreht er. Wie weit runter man effektiv gehen kann habe ich nicht getestet. Mein Zug läuft mit 3.3 Volt.
Die Drehrichtung vom Motor wird dadurch bestimmt, dass C1 nach VCC und C2 nach GND geht oder umgekehrt C1 nach GND und C2 nach VCC.
Mein Zug muss nur in eine Richtung fahren können, deshalb habe ich kurzerhand nach dem Bestimmen der Drehrichtung die Kabel zusammengelötet.
Alternativ kann man sich einen Schalter so zusammenlöten, dass dieser die Polung umdrehen kann. Wenn man keinen hat (so wie ich), könnte man auch zwei schiebeschalter verbinden
und so die Drehrichtung steuern (immer zeitgleich umschalten, sonst hat man einen Kurzschluss).
Die Kabellänge muss man nach eigenem Gutdünken wählen, ich habe am Ende einen Stecker angelötet, die Dupont-Stecker sind meiner Meinung nach hier nicht so passend.
Ästhetik
Was beim offiziellen Umbau speziell stört, ist, dass man diverse Komponenten so verbauen muss, dass der Charme vom Zug verloren geht.
Deshalb galt es für mich, hier möglichst wenige Kompromisse machen zu müssen.
Im Moment funktioniert das ziemlich gut, der LiPo-Akku lässt sich im Kohlewagen und der Buck-Konverter hat in der Lokomotive Platz.
Das Stromverbindungskabel werde ich noch schwarz machen, entweder mit einem Schrumpfschlauch oder mit schwarzem Isolierband.
Ich finde das ist so viel besser gelöst.
Strombezug / Akku
Bei der Stromversorgung habe ich mich ein bisschen schwer getan.
Meine Lösung ist hier ein LiPo-Akku 2S 7.4V 225mAh (mit integriertem Controller für Tiefentladung / Überladung) in Kombination mit einem Buck-Konverter (DC-DC),
welchen ich auf 3.3 Volt Ausgangsspannung eingestellt habe.
Akku
Bei der Akkuwahl liegt es lediglich daran, dass dieser bereits bei mir zu Hause rumgelegen hat, da ich noch ein RC-Car habe.
Ein LiPo-Akku mit eingebautem Controller hat viele Vorteile im Gegensatz zu NiMh-Akkus oder Batterien.
Die Batterien fallen weg, weil wir hier einfach zu viel Verschwendung haben.
Die NiMh-Akkus wären im Grunde genommen ok, aber da müsste ich einen Controller bauen, welcher die Tiefentladung überwachen würde.
Da bin aber leider noch nicht über eine simple Bauanleitung gestolpert.
Für die Kapazität vom Akku würde ich eine möglichst grosse noch einbaubare wählen.
225 mAh sind ein anfang, aber ich werde sicher auch nach einem grösseren umschauen. Im Falle könnte man dann auch die graue 4 x 8 Fliesse mit einem Distanzhalter etwas erhöhen,
damit ein dickerer Akku platz hat.
Buck-Konverter / Stepdown Converter
Den Buck-Konverter für DC-DC um die 7.4 Volt des Akkus auf 3.3 Volt herunter zu regeln habe ich von Aliexpress bestellt.
Nach kurzer Messung zieht der Legomotor etwa 65 mA, der Stopdown Converter wird sicher auch noch etwas beziehen, aber der fällt hier nicht so ins Gewicht.
Von einem Linearregler rate ich ab, der verbratet viel Leistung einfach in Wärme um. Bei meinem verwendeten LiPo-Akku lief der Zug mit einem Linearregler (LD33V) ca. 1h am Stück.
Beim Buck-Converter etwas über 2h am Stück.
Umsetzung und Bedienung der Zugkomposition
- Zugmotor wird mit einem Stecker versehen - VCC und C werden verbunden, GND und anderer C werden verbunden (so dass der Motor vorwärts fährt).
- Der Buck-Konverter (Stepdown Converter) erhält 2 Stecker - Ausgang geht zum Zugmotor, Eingang erhält den Stecker zum LiPo-Akku. Den Buck-Konverter bringe ich in der Lokomotive unter.
- Im Kohlewagen unter der grauen Platte verstecke ich den LiPo-Akku.
- (optional) hänge ich einen Schalter zwischen Akku und Buck-Konverter, damit der Zug nicht sofort abfahrt.
So sieht das zusammengebaut aus:
Optionale Erweiterungen und Ideen
Mit der ersten Umsetzung sind mir bereits ein paar Ideen gekommen, welche ich sicherlich noch ausprobieren werde:
- Fernbedienung (inkl. Richtungswechsel).
- WLan / Zeitschaltung.
Im Grundsatz ziemlich ähnlich wie die Idee mit der Fernbedienung. Ich würde ebenfalls einen ESP in Kombination mit einem L293D verwenden.
Diesen dann aber als Client in ein bestehendes WLan anbinden und über einen Webcall (GET oder POST) die Einschaltzeit beziehen.
- Timer (Zug nur für eine bestimmte Zeit fahren lassen).
Diese Variante führe ich nicht speziell detailierter aus. Um das zu erreichen würde ich einfach einen Mikrocontroller nehmen,
der möglichst wenig Strom verbraucht, ein einfaches Skript verwenden welches den Motor einfach eine Zeit lang laufen lässt.
ULN2003AN oder ähnliches nicht vergessen, damit es den Microcontroller nicht verheizt.
Fernsteuerung mit Richtungskontrolle und Geschwindigkeitskontrolle
Komfortabel wird es natürlich erst, sobald wir den Zug Fernsteuern können.
Ich habe mich dem Problem gewidmet, weil ich ebenfalls auch hierzu nichts direkt im WWW finden konnte.
Meine Lösung erweitert die ursprüngliche Idee den Zug mit einfach einem LiPo-Akku und einem An/Aus-Schalter zu bedienen.
Wir benötigen aber etwas mehr Bauteile, haben aber tolle Vorteile:
- Geschwindigkeitskontrolle.
- Richtungswechsel.
- Motorlaufzeit begrenzbar.
- ESPNow Kommunikationsprotokoll, sprich ist nicht im WLan und hat somit auch keine Verzögerungen.
- Man könnte einen Fahrplan einprogrammieren, dann aber wieder im WLan.
Schaltplan und Bauteile
Lösungsansatz
Ein Sender (ESP 12F) / Fernsteuerung ist mit dem Empfänger (ESP-01S) / Lokomotive über das ESPNow-Protokoll verbunden und kontrolliert die Steuerungsbefehle.
Es soll sich nicht mit dem privaten WLan verbinden müssen und ich will auch nicht ein Smartphone dafür einsetzen, deshalb ESPNow und eine eigene Fernbedienung.
Bauteile Sender
- Linearregler LD33V (kann auch ein StepDown-Regeler sein).
- Stromeingangsbuchse, bei mir USB-A-Weiblich.
- ESP 12F (bei mir ist es nur der Controller ohne Entwicklungsboard. Wählt man hier eine Entwicklungsboardvariante ist das sicher etwas komfortabler.).
3 Stk. 10 kOhm Widerstand für Standardbeschaltung.
1 Stk Schieberegler für Flash-Modus vom ESP-12F.
- 3 Stk. Push-Buttons.
- PCB-Platte
Bauteile Empfänger
- 2 Stk. StepDown-Converter (Buck-Converter), einer für LiPo zu 5.0 Volt und einer für 5.0 Volt zu 3.3 Volt.
- ESP-01s
- Logic-Level-Shifter weil ESP 3.3 Volt und L293D mit 5.0 Volt Logik arbeitet.
- L293D
- PCB-Platte
- Diverse Steckverbinder
- Adapter um vom L293D und Stromversorgung (LiPo) auf den Zugmotor zu kommen, ausser man lötet so wie ich die Kabel direkt an.
Schaltplan Sender:
Arduino-IDE Code für Sender und Empfänger
In meiner Variante möchte ich nicht, dass sich die ESP-Mikrocontroller mit dem WLan verbinden, sie sollen sich direkt miteinander verbinden.
Ich habe mich für ESPNow entschieden, das verhält sich, so wie ich es verstanden habe, dann wie wenn man zum Beispiel einen RC-Car verwendet.
Wir machen also keine Accesspoint-und-Client Verbindung, sondern eben eine Peer-to-Peer Verbindung.
Den ganzen ESPNow-Code habe ich von https://RandomNerdTutorials.com/esp-now-esp8266-nodemcu-arduino-ide/ bezogen und dann entsprechend nach meinen Bedürfnissen erweitert.
Um den Code zu übernehmen muss man die MAC-Adresse vom Empfänger-ESP im Code vom Sender-Sketch eintragen.
Schaltplan Empfänger:
Beispielcode Empfänger:
Arduino Sketch zum Herunterladen:
LEGOWeihnachtszug_ESPNow_Empfang.zip
Der aufgeführte Code hat ein Standard-Timeout von 30 Minuten if((millis()-LoklaufzeitStart)>1800000), das möchte man eventuell ausdokumentieren oder erhöhen.
/*
ESPNow-Teil ist von hier bezogen:
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
/*
Erklärung der Anpassung betreffend Variablen und des Skripts von slaps313:
Motorleistung -> Wert von 0 bis 1023, wird per PWM an den L293D geschickt und steuert das Tempo.
Motorrichtung -> True = Rückwärts, False = Vorwärts
Motorpins sind fix auf RX und TX (Pin 1 und 3) gelegt, Serielle Kommunikation ist so nicht möglich aber man hat auch keine Probleme wegen den System-Boot-Flashpins.
Das Skript ist so ausgelegt, dass die Lokomotive automatisch nach 30 Minuten ausstellt, wenn dies nicht gewünscht wird, muss die Zeile if((millis()-LoklaufzeitStart)>1800000) angepasst werden.
Vom Sender erhalten wir hier jeweils einfach den Wert für Motorleistung und Motorrichtung und wir verarbeiten ihn hier.
*/
#include <ESP8266WiFi.h >
#include <espnow.h>
volatile uint16_t Motorleistung = 0;
volatile uint16_t Motorrichtung = 0;
unsigned long LoklaufzeitStart;
typedef struct struct_message {
char a[1];
int Motorleistung = 0;
bool Motorrichtung = false;
char e[1];
} struct_message;
struct_message myData;
// Callback function that will be executed when data is received
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
memcpy(&myData, incomingData, sizeof(myData));
Motorleistung = myData.Motorleistung;
Motorrichtung = myData.Motorrichtung;
LoklaufzeitStart = millis();
}
void setup() {
delay(1000); //Wichtig, ohne das Delay startet der ESP teilweise nicht korrekt!
WiFi.mode(WIFI_STA);
if (esp_now_init() != 0) {
return;
}
esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);
esp_now_register_recv_cb(OnDataRecv);
}
void loop() {
if(Motorrichtung == true)
{
analogWrite(1, Motorleistung);
analogWrite(3, 0);
}
else
{
analogWrite(3, Motorleistung);
analogWrite(1, 0);
}
if((millis()-LoklaufzeitStart)>1800000)
{
//Lok nach x-Millisekunden ausschalten
Motorleistung = 0;
}
}
Beispielcode Sender:
Arduino Sketch zum Herunterladen:
LEGOWeihnachtszug_ESPNow_Sender.zip
In diesem Sketch muss man noch die MAC-Adresse vom Empfänger-ESP hinterlegen (uint8_t broadcastAddress[] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};).
/*
ESPNow-Teil ist von hier bezogen:
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
/*
Erklärung der Anpassung betreffend Variablen und des Skripts von slaps313:
Motorleistung -> Wert von 0 bis 1023 senden wir an den Empfänger und dieser schickt dies per PWM an den L293D für die Geschwindigkeitsregulierung.
Motorrichtung -> True = Rückwärts, False = Vorwärts
uint8_t broadcastAddress[] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC}; -> muss auf die Mac-Adresse des Empfangsmoduls angepasst werden.
Die Pins für RichtungsPin, TempomehrPin, TempowenigerPin sind INPUT_PULLUP, so benötigen wir keinen Widerstand dafür und können sie direkt an die Push-Buttons anschliessen.
Bei der Funktionalität für den RichtungsPin habe ich noch eingebaut, dass wenn Motorleistung mehr als 0 ist, zuerst mal diese auf 0 gesetzt werden soll und erst beim zweiten Mal drücken dann die Richtung wechselt.
Ich denke das ist intuitiver.
Bei der Abfrage if((millis()-Letztessignal) > 40) schaue ich lediglich, dass beim drücken der Tempo-Buttons der Wert nicht zu schnell in die Höhe geht.
Ohne das Delay von hier 40ms, kann man das Tempo nicht schön kontrollieren.
*/
#include <ESP8266WiFi.h>
#include <espnow.h>
uint8_t broadcastAddress[] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
//Fernsteuerungsvariablen:
int Motorleistung = 0;
bool Motorrichtung = false;
bool sendeMotorrichtungsbefehl = false;
bool sendeSteuerungsbefehl = false;
int RichtungsPin = 14;
int TempomehrPin = 12;
int TempowenigerPin = 13;
unsigned long Letztessignal = 0;
typedef struct struct_message {
char a[1];
int Motorleistung = 0;
bool Motorrichtung = false;
char e[1];
} struct_message;
struct_message myData;
// Callback when data is sent
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
if (sendStatus == 0){
sendeSteuerungsbefehl = false;
}
}
void setup() {
pinMode(RichtungsPin, INPUT_PULLUP);
pinMode(TempomehrPin, INPUT_PULLUP);
pinMode(TempowenigerPin, INPUT_PULLUP);
WiFi.mode(WIFI_STA);
// Init ESP-NOW
if (esp_now_init() != 0) {
return;
}
esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
esp_now_register_send_cb(OnDataSent);
esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE, 1, NULL, 0);
Letztessignal = millis();
}
void SendeSteuerbefehl()
{
strcpy(myData.a, "A");
myData.Motorleistung = Motorleistung;
myData.Motorrichtung = Motorrichtung;
strcpy(myData.e, "X");
esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
}
void loop() {
if((millis()-Letztessignal) > 40)
{
Letztessignal = millis();
if(digitalRead(RichtungsPin) == LOW && sendeMotorrichtungsbefehl == false)
{
//Richtung switchen, Tempo auf 0 setzen
//Wenn Tempo mehr als 0 ist, soll es zuerst einfach stoppen und erst wenn nochmals gedrückt wurde / oder Tempo sowieso 0 ist die Richtung wechseln
sendeSteuerungsbefehl = true;
if(Motorleistung > 0)
{
Motorleistung = 0;
sendeSteuerungsbefehl = true;
sendeMotorrichtungsbefehl = true;
}
else
{
sendeMotorrichtungsbefehl = true;
Motorleistung = 0;
if(Motorrichtung == false)
{
Motorrichtung = true;
}
else
{
Motorrichtung = false;
}
}
}
else
{
if(digitalRead(RichtungsPin) == HIGH)
{
sendeMotorrichtungsbefehl = false;
}
}
if(digitalRead(TempomehrPin) == LOW)
{
if(Motorleistung<1023)
{
Motorleistung = Motorleistung+1;
sendeSteuerungsbefehl = true;
}
}
if(digitalRead(TempowenigerPin) == LOW)
{
if(Motorleistung>0)
{
Motorleistung = Motorleistung-1;
sendeSteuerungsbefehl = true;
}
}
if(sendeSteuerungsbefehl == true)
{
SendeSteuerbefehl();
}
}
}