Hauptmenue
Netcoast.ch
Elektronik


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:
  1. Der Zug soll sich gemächlich fortbewegen.
  2. Durch die Motorisierung soll die Ästhetik so gut wie möglich bewahrt werden.
  3. 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

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:

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:

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
Bauteile Empfänger
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();
    }
  }
}