Skip to content

Instantly share code, notes, and snippets.

@jerome-labidurie
Last active May 5, 2024 19:21
Show Gist options
  • Save jerome-labidurie/0be82269249b269f77c9fbbdf6e2230b to your computer and use it in GitHub Desktop.
Save jerome-labidurie/0be82269249b269f77c9fbbdf6e2230b to your computer and use it in GitHub Desktop.
Lego PoweredUp control from HomeAssistantt

Control Lego Tramway from HomeAssistant

Architecture

flowchart LR
   phone[[phone]] <--wifi--> HA
   HA <--> mqtt
   mqtt <--wifi--> ha-int
   legoino <--BT--> Tram[[Lego Tramway]]
   dfplayer <--serial--> dfplayermini[[DFPlayerMini\nSDCard]]

   subgraph esp32
      legoino((legoino))
      dfplayer((dfplayer))
      ha-int((ha-int)) <--> legoino
      ha-int <--> dfplayer
   end

   subgraph NAS
      HA(hass)
      mqtt(mqtt broker)
   end
Loading

Hardware

I used what I had laying around :

  • Wemos LOLIN32 lite
  • DFRobot DFPlayer mini (optional, a switch is present in the code)
  • Lego 88009 & Lego 88011

Software

The esp32 code is based on some wonderfull libraries !

On HA part, you will need the mqtt integration configured

Usage

  • first, configure the constants in the code
    • ssid / password
    • mqtt_broker / mqtt_port
    • led : to match the builtin led of your board
  • flash your esp32
  • set your 88009 to pairing mode
  • plug the esp32
  • after a short time, you should see some new device/entities in HA
  • enjoy !

The builtin led matches the availability of the tram control

  • off : during connection to wifi & bluetooth
  • on : all is connected
/**
* Lego (Powered Up) train control from Home Assistant / MQTT with BellRing
*
* Licence: GPLv3
*
* needed libraries:
* legoino https://github.com/corneliusmunz/legoino
* > 1.1.0, tested with commit 4daae4f683b087b8c443a4c813934e3dfff41d69
* home-assistant-integration https://github.com/dawidchyrzynski/arduino-home-assistant
* 1.3.0
* DFRobotDFPlayerMini https://github.com/DFRobot/DFRobotDFPlayerMini/
*/
/** HUB led signification
* Flashing white light with short flashes. It is the pareo mode. Pressing the green button tries to connect via Bluetooth for 20 seconds
* Flashing white light with long blinks. The device is resetting
* Permanent blue light. Bluetooth has been connected and everything works fine
* Orange light flashing. Low battery. In this case there are 6 AAA batteries (LR03 at 1,5V)
* Red, green, blue flashing light. The application of the tablet or mobile has forced the firmware update of the Move Hub. The color of the blink will alternate. According the official page the firmware update should take no more than 15 minutes
*
* source: https://www.ikkaro.com/en/move-hub/
*/
// comment that to remove DFPlayer mini sound management
#define DFPLAYER
#include <WiFi.h>
#include <ArduinoHA.h>
#include "Lpf2Hub.h"
#ifdef DFPLAYER
// audio
#include "DFRobotDFPlayerMini.h"
HardwareSerial mySoftwareSerial(1);
DFRobotDFPlayerMini myDFPlayer;
uint8_t volume = 10;
#endif
// constants
// hardcoded values are baaaaaad, we should use something like WiFiManager
const char* ssid = "MySSID";
const char* password = "SecretPassword";
const char* mqtt_broker = "192.168.1.20";
const uint16_t mqtt_port = 1883;
const char* uniqId = "LegoTramway";
const uint8_t led = 22; // builtin led, may replace by LED_BUILTIN
// globals
WiFiClient client;
HADevice device(uniqId);
HAMqtt mqtt(client, device);
HASwitch moteur("tram_moteur"); // motor control
HANumber inSpeed("tram_speed"); // motor speed
#ifdef DFPLAYER
HAButton buttonPlay("tram_play"); // play the sound
HANumber inVolume("tram_volume"); // volume for the sound
#endif
int32_t speed = 20; // default tram speed
// create a hub instance
Lpf2Hub myTrainHub;
// motor is on port A
byte port = (byte)PoweredUpHubPort::A;
// callback for motor on/off
void onMotorCommand(bool state, HASwitch* sender) {
Serial.print("switch: ");
Serial.println(state);
if (myTrainHub.isConnected()) {
if (state) {
myTrainHub.setBasicMotorSpeed(port, speed);
} else {
myTrainHub.stopBasicMotor(port);
}
sender->setState(state); // report state back to the Home Assistant
}
}
// callback for input number speed change
void onSpeedCommand(HANumeric number, HANumber* sender) {
if (number.isSet()) {
speed = number.toInt32();
sender->setState(number); // report the selected option back to the HA panel
Serial.print("set speed: ");
Serial.println(speed);
}
}
#ifdef DFPLAYER
// callback for play button
void onPlayCommand(HAButton* sender) {
// play
Serial.println("Play...");
myDFPlayer.play(1); //Play the first mp3
}
// callback for volume slider
void onVolumeCommand(HANumeric number, HANumber* sender) {
if (number.isSet()) {
volume = number.toUInt8();
myDFPlayer.volume(volume); //Set volume value (0~30).
sender->setState(number); // report the selected option back to the HA panel
Serial.print("set vol: ");
Serial.println (volume);
}
}
#endif
// set availab for tramway entities
void setAvailability (bool ava) {
// on lolin32 lite, led is active LOW
digitalWrite(led, (ava)? LOW : HIGH );
#ifdef DFPLAYER
// dfplayer should stay available, set only for lego motor/speed
moteur.setAvailability(ava);
inSpeed.setAvailability(ava);
#else
device.setAvailability (ava); // for whole device
#endif
}
// setup home-assistant integration
void setupHA(void) {
// HA device configuration
device.setName("Lego Tramway");
device.setSoftwareVersion("1.0.0");
device.setModel("88009");
#ifndef DFPLAYER
device.enableSharedAvailability(); // configure for whole device
device.enableLastWill(); // for when device unplug
#endif
setAvailability(false);
// config motor switch
moteur.setIcon("mdi:tram");
moteur.setName("Moteur");
moteur.onCommand(onMotorCommand);
// config speed slider
inSpeed.onCommand(onSpeedCommand);
inSpeed.setName("Vitesse");
inSpeed.setIcon ("mdi:speedometer");
inSpeed.setMin(-100);
inSpeed.setMax(100);
inSpeed.setStep(5);
inSpeed.setState(speed);
#ifdef DFPLAYER
// config Button Play
buttonPlay.setIcon("mdi:bell-ring");
buttonPlay.setName("Play");
buttonPlay.onCommand(onPlayCommand);
// config volume slider
inVolume.onCommand(onVolumeCommand);
inVolume.setName ("Volume");
inVolume.setIcon ("mdi:volume-medium");
inVolume.setMin(0);
inVolume.setMax(30);
inVolume.setState(volume);
#endif
}
// setup Wifi
void setupWifi(void) {
Serial.println("Connecting to: " + String(ssid));
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void setup() {
Serial.begin(115200);
delay(10);
pinMode(led, OUTPUT);
setupWifi();
mqtt.begin(mqtt_broker, mqtt_port);
setupHA();
myTrainHub.init(); // initalize the PoweredUpHub instance
#ifdef DFPLAYER
// for DFAudio
mySoftwareSerial.begin(9600, SERIAL_8N1, 16, 17); // speed, type, RX, TX
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(myDFPlayer.readType(),HEX);
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
while(true);
}
myDFPlayer.volume(volume); //Set volume value (0~30).
myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
myDFPlayer.play(1); //Play the first mp3
#endif
}
void loop() {
mqtt.loop();
// connect flow. Search for BLE services and try to connect if the uuid of the hub is found
if (myTrainHub.isConnecting()) {
Serial.println("Connecting to HUB...");
myTrainHub.connectHub();
if (myTrainHub.isConnected()) {
Serial.println("Connected to HUB");
Serial.print("Hub address: ");
Serial.println(myTrainHub.getHubAddress().toString().c_str());
Serial.print("Hub name: ");
Serial.println(myTrainHub.getHubName().c_str());
setAvailability(true);
} else {
Serial.println("Failed to connect to HUB");
setAvailability(false);
}
}
if (!myTrainHub.isConnected() && !myTrainHub.isConnecting() && ! myTrainHub.isScanning()) {
// setAvailability(false); // seems to make BT connection unreliable, not sure why :(
Serial.println("Reinit BT...");
myTrainHub.init(); // (re)initalize the PoweredUpHub instance
}
}
@jerome-labidurie
Copy link
Author

Hello,
You might be able to stich something together on a PC with some other libraries.

Good luck

@nerumo
Copy link

nerumo commented Jan 1, 2024

thanks for the hint, started a repo for this. initially working :)

https://github.com/nerumo/lego-powered-up-to-home-assistant

I had to use a different binding library since this one didn't support a "number component" for the motor power

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment