corneliusmunz / legoino

Arduino Library for controlling Powered UP and Boost controllers
MIT License
257 stars 34 forks source link

Using several Lpf2Hub instances possible? #54

Open fredlcore opened 3 years ago

fredlcore commented 3 years ago

First of all, I'm not sure if this is a doublette of #46 - if so, feel free to close this right now. I'm trying to connect my ESP32 to three Lego Duplo trains. The way I'm doing this is that I setup an array of Lfp2Hub objects like this:

#define HUBS 3
Lpf2Hub* myHubs[HUBS];
Lpf2Hub* myHub;

and then send all of them into init mode in setup():

  for (int x=0; x<HUBS; x++) {
    myHubs[x] = new Lpf2Hub;
    myHubs[x]->init();
  }

Subsequently, I can cycle through them in a for-loop:

  for (int hub=0; hub<HUBS; hub++) {
    myHub = myHubs[hub];
    if (myHub->isConnecting()) {
      ...
    }
  }

However, this only works for the first train to be connected, and this will always be the last element of the myHubs array (2 in this case). Also, once the first connection is made, any subsequent train that is turned on won't connect.

After reading #46, I was wondering if this also has to do with the blocking-/non-blocking approach. But if changing to a blocking model would be necessary to connect multiple hubs, wouldn't that mean that I'd have to a) define how many hubs I want to use and b) make sure that all of these hubs are connected before the code will continue to execute? Because that's what blocking means, isn't it? My goal would be that my kids can decide whether they put 1, 2 or 3 trains on the track and all active ones could be controlled (I would use the PS3 controller's "SELECT" button to switch between them). However, it seems that with blocking mode, they would have to always use the same number of trains, wouldn't it?

corneliusmunz commented 3 years ago

Hi @fredlcore! Connecting to multiple hubs is defenitely possible. Up to now you can have a connection to up to 9 hubs from one esp32. If you want to use more than three hubs, you have to configure the NimBLE-Arduino lib according to the followin procedure: https://github.com/corneliusmunz/legoino#connection-to-more-than-3-hubs

You can find ab example of connections to multiple hubs in the following sketch: https://github.com/corneliusmunz/legoino/blob/master/examples/MultipleTrainHubs/MultipleTrainHubs.ino

Now the issue #46 comes into place. Adding the non blocking scan has some benefits but also some drawbacks and you can "remove" the non blocking change by changing the following line in the Lpf2Hub.cpp file:

Non blocking scan start: pBLEScan->start(_scanDuration, scanEndedCallback);

Change to blocking scan start: pBLEScan->start(_scanDuration);

With this change the connection procedure should work for multiple hubs and you can check for each hub instance if it is connected with the myHub.isConnected() method. If you know the Hub Ids, you can specifically connect to a known id and can map that to a specific instance

myTrainHub1.init("90:84:2b:03:19:7f"); // initialize the listening train hub 1 // here you have to use your own device ids

You can find an example in the TwoTrainHub.ino sketch

fredlcore commented 3 years ago

Thanks for the detailed feedback! I'm not sure if I understand the blocking/non-blocking matters correctly, but looking at the code in MultipleTrainHubs.ino, I'm wondering whether it is not possible to send all three inits at the same time, like I did here: https://github.com/fredlcore/PS3-Lego-Duplo-Train-Control/blob/08bd8fa5e5a7657427cfdf943890201ffecae241/DuploTrainControl/DuploTrainControl.ino#L124-L127 Or is it a must that any subsequent inits can only be started after there has been a successful connection with the previous init?

The reason why I would like to do this with an array of Lpf2Hub objects is that it is easily scalable. So it does not matter whether it's one, two or three hubs one is using (or up to nine with the changes you mentioned above). And it would not require the nested if blocks etc. But maybe that's the reason why yours is working and mine is not. So in case you'd have the time to look at my code, I'd really appreciate it!

fredlcore commented 3 years ago

I just figured out the reason why my code was not working properly: I assumed that after issuing an init(), the scan would continuously run until a connection was made. In all these tests, it probably took me more than 10 seconds (the defaul scan duration) to start both Duplo trains, so only the first one got connected. Now that I re-issue init() every 11 seconds, it works and I can control both trains. Working with an array in that respect makes this matter easily scalable and also does not require one to do nested ifs etc.

fredlcore commented 3 years ago

@corneliusmunz: I now found a solution that works with non-blocking scan and gives you the freedom to initialize up to nine hubs without adding extra code - just by going through a loop using an array:

#define HUBS 3
for (int x=0; x<HUBS; x++) {
  myHubs[x] = new Lpf2Hub;
  myHubs[x]->init();
}

Later on, in the loop() function, I can use this to both execute individual code for each train as well as restart scanning after the initial scanning period is over, so that any disconnected device can easily reconnect:

for (int hub=0; hub<HUBS; hub++) {
  myHub = myHubs[hub];
  if (myHub->isConnected()) {
... *Do train control stuff here* ...
  } else { // not connected anymore? Then put this instance in scanning mode again
    if (myHub->isScanning() == false && myHub->isConnecting() == false && myHub->isConnected() == false) {
      myHub->init();
    }
  }
}

Maybe this is an (easier) option for your TwoTrainHubs or MultipleTrainHubs examples as well?