Lok24

Control your trains without smart device - with Pybricks

Recommended Posts

Sorry, perhaps a missunderstanding,I removed it (as far as I remember) when it did'nt work any more, and I had no time to look for the problems.

5 minutes ago, Marlow said:

I would have thought, that that is the best way to distribute it and also to gather all bugs/bugfixes/suggestions in one place,

Yes, that's why I published it there, an there was no feedback at all about bugs, suggestions or something.

And again I have no idea why it did not work again now ;-) Assume something like motor(port). .....

Latest Version was MotorControl2_9.py from January 2023.

 

Share this post


Link to post
Share on other sites
8 minutes ago, Lok24 said:

And again I have no idea why it did not work again now ;-) Assume something like motor(port). .....

 

The initial fix for it to run on firmware 3.2 according to @and_ampersand_and was to comment out line 255:

" Motor(port).control.limits(speed=dspeed,acceleration=10000) "

That's the only line that fails in that script.

Obviously, it has some sort of purpose, so a remedy should be found for that using the 3.2 codebase.

And it only fails for technic motors, not for train motors. The script continues to work perfectly in 3.2 and 3.3beta, if one uses train motors without changing that one. With that change, all motors work.

/M

Edited by Marlow

Share this post


Link to post
Share on other sites

Thanks, as assumed, the motor(port) command, which was not supported any more.

The block looks like that:

            devs_max_speed = {38:1530,46:1890,47:1980,48:1367,49:1278,75:1367,76:1278 }
            dspeed = devs_max_speed.get(PUPDevice(port).info()['id'], 1000)
            motor[i].obj.stop()
            motor[i].obj.control.limits(speed=dspeed,acceleration=10000)
            motor[i].setSpeed(dspeed/100*0.9)

Seems that you use 2.7 which is not the latest Version....

Have a look:

# -----------------------------------------------
# MotorControl
#
# uses https://code.pybricks.com/ , LEGO City hub, LEGO remote control
# connect 1 or 2 motors of any kind to Port A and/or B
#
# Version 2_9
# -----------------------------------------------/
from pybricks.parameters import * # Color

# -----------------------------------------------
#  Set user defined values
# -----------------------------------------------

# define the two profiles
# profil_x = (minimun speed,maximum Speed,accelerate in steps of ..., wait for next acceleration(in ms)

Profil_A = (20,100,10,100)  #min,max,step,acc
Profil_B = (10,500,5,200)   #min,max,step,acc

# define direction of motors

dirMotorA = 1       # Direction 1 or -1
dirMotorB = -1       # Direction 1 or -1

autoacc = False      # accelarate continously when holding butten 

# -----------------------------------------------
#  Set general values
# -----------------------------------------------

# assign buttons to function1 
# syntax: function = "name"
# name may  be "A+","A-","A0","B+","B-","B0","CENTER"

UP = "A+"
DOWN = "A-"
STOP = "A0"
SWITCH = "CENTER"

mode=1              # start with function number...
watchdog = False    # "True" or "False": Stop motors when loosing remote connection
remoteTimeout =10   # hub waits x seconds for remote connect after starting hub
remoteName = ""     # connect this remote only

# Color and brightness of Hub LEDs
LEDconn = Color.GREEN*0.3       # if Hub connected, color * brightness
LEDnotconn = Color.RED*0.5      # if Hub is not connect, color * brightness

LED_A = Color.GREEN*0.3       # Remote Profil_A, color * brightness
LED_B = Color.RED*0.5      # Remote Profil_B, color * brightness

# -----------------------------------------------
#  Import classes and functions
# -----------------------------------------------

from pybricks.pupdevices import DCMotor, Motor, Remote
from pybricks.parameters import Port, Stop, Button, Color
from pybricks.hubs import CityHub
from pybricks.tools import wait, StopWatch
from pybricks.iodevices import PUPDevice
from uerrno import ENODEV

# -----------------------------------------------
#  function 1 / drive motors
# -----------------------------------------------

def function1():

    vmax = profile[mode].vmax
    vmin = profile[mode].vmin
    accdelay =  profile[mode].acc
    step = profile[mode].step
  
    global v
   
    if CheckButton(UP) and not CheckButton(STOP) : 
        for x in range (1, step + 1):
            v = v + 1
            if v > vmax :
                v = vmax
            if v > 0 and v < vmin:    
                v = vmin
            if abs(v) < vmin:
                v = 0
            drive()
            wait (accdelay)  
            if v==0: 
                break 
        # further acceleration if button keeps pressed
        while autoacc == False and CheckButton(UP) :    
            wait (100)
        # avoid changing direction when reaching "0"     
        while v == 0 and  CheckButton(UP):  
            wait (100)

    if CheckButton(DOWN) and not CheckButton(STOP):
        for x in range (1, step + 1):
            v = v-1
            if v < vmax*-1 :
                v = vmax*-1
            if v < 0 and v > vmin*-1:    
                v = vmin*-1
            if abs(v) < vmin :
                v = 0   
            drive()
            wait (accdelay)  
            if v==0: 
                break 
        # further acceleration if button keeps pressed
        while autoacc == False and CheckButton(DOWN) :    
            wait (100)
        # avoid changing direction when reaching "0"
        while v == 0 and  CheckButton(DOWN) :    
            wait (100)
  
    if CheckButton(STOP): 
        v = 0
        drive()
        wait (100)    
        
class setprofile():
    def __init__(self,pr):
        self.vmin=pr[0]
        self.vmax=pr[1]
        self.step=pr[2]
        self.acc=pr[3]

profile = [0,0,0]
profile[1] = setprofile(Profil_A)
profile[2] = setprofile(Profil_B)

# -----------------------------------------------
#  function 2
# -----------------------------------------------
           
'''
def function2():
    if CheckButton(UP):
        timer[1].set(3000)
    if timer[1].check(): 
        print("Do something")
'''

# -----------------------------------------------
# general program routines and classes
# -----------------------------------------------

# ----CheckButton -------------------------------------------

def CheckButton(x):
    try:
        button = remote.buttons.pressed()
        if x == "A+"  : x = Button.LEFT_PLUS
        if x == "A-" : x = Button.LEFT_MINUS
        if x == "A0"  : x = Button.LEFT

        if x == "B+"  : x = Button.RIGHT_PLUS
        if x == "B-" : x = Button.RIGHT_MINUS
        if x == "B0"  : x = Button.RIGHT
    
        if x == "CENTER"  : x = Button.CENTER
        
        if x in button:
            return True
        else:
            return False
    except:
        return()

# ----delay -------------------------------------------

class delay:
    def __init__(self,id,time=0,watch=StopWatch(),busy=False):
        self.id=id
        self.time=time
        self.watch=watch
        self.busy=busy
        print ("Init Timer",id)
    # set a timer        
    def set(self,x):
        if  self.busy == False:
            self.busy = True
            self.watch.reset()
            self.time = x
            print("Timer",timer[1].id, "set to",x)
    #check if timer is reached, then return "True"
    def check(self):
        if self.busy == True:
            if self.watch.time() > self.time:
                self.busy = False
                self.time=0 
                print("Timer",timer[1].id, "stopped")
                return(True)
        else:
            return(False)

# ----drive -------------------------------------------

def drive():
    global vold
    global v
    print (v)
    if vold != v:
        # for each motor 1,2 
        for x in range(1,3):
            # set speed and direction
            s = v*round(motor[x].getDir())
            # real motor commands depending on motor type
            if motor[x].getType() == "Motor" :
                motor[x].obj.run(s*motor[x].getSpeed()) #  in 2.7
            if motor[x].getType() == "DCMotor" : 
                motor[x].obj.dc(s) 
            if v == 0 and motor[x].getType() != "---":  
                print("stop",x)
                motor[x].obj.stop()      
            #if motor[x].getDir() != 0 and motor[x].getType() == "DCMotor" : motor[x].obj.dc(s) 
        vold = v
            
# ----portcheck -------------------------------------------

def portcheck(i):
    # list of motors, 1 +2 contain string "DC"
    devices = {
    1: "Wedo 2.0 DC Motor",
    2: "Train DC Motor",
    38: "BOOST Interactive Motor",
    46: "Technic Large Motor",
    47: "Technic Extra Large Motor",
    48: "SPIKE Medium Angular Motor",
    49: "SPIKE Large Angular Motor",
    75: "Technic Medium Angular Motor",
    76: "Technic Large Angular Motor",
}
    port = motor[i].getPort()
    # Try to get the device, if it is attached.
    try:
        device = PUPDevice(port)
    except OSError as ex:
        if ex.args[0] == ENODEV:
            # No device found on this port.
            motor[i].setType("---")
            print(port, ": not connected")
            return ("---")
        else:
            raise

    # Get the device id
    id = device.info()['id']
    
    # Look up the name.
    try:
        # get the attributes for tacho motors
        if "Motor" in devices[id] and not("DC" in devices[id]): 
            motor[i].setType("Motor")
            motor[i].obj = Motor(port)

            #new in 2.7
            # if motor[x].getDir() != 0 and motor[x].getType() == "Motor" : motor[x].obj.run(s*motor[x].getSpeed()) #  in 2.7
            devs_max_speed = {38:1530,46:1890,47:1980,48:1367,49:1278,75:1367,76:1278 }
            dspeed = devs_max_speed.get(PUPDevice(port).info()['id'], 1000)
            motor[i].obj.stop()
            motor[i].obj.control.limits(speed=dspeed,acceleration=10000)
            motor[i].setSpeed(dspeed/100*0.9)

        # and set type for simple DC Motors    
        if "DC" in devices[id]:
            motor[i].setType("DCMotor")
            motor[i].obj = DCMotor(port)
            
    except KeyError:
        motor[i].stype("unkown")
        print(port, ":", "Unknown device with ID", id)
    wait(100)    
    print ("--")
    print(port, ":", devices[id], motor[i].getType(),motor[i].getSpeed(),motor[i].getAcc())

# ---- device  -------------------------------------------
    
class device():
    # store the device infos for each motor
    def __init__(self,port,dir):
        self.port = port
        self.dir = dir
        self.type=""
        self.speed=99
        self.acc=99
        self.obj=""
                
    def setType(self,x) : self.type = x
    def setSpeed(self,x): self.speed = x
    def setAcc(self,x)  : self.acc = x
    
    def getType(self)   : return self.type
    def getPort(self)   : return self.port
    def getDir(self)    : return self.dir
    def getSpeed(self)  : return self.speed
    def getAcc(self)    : return self.acc

# -----------------------------------------------
# globals
# -----------------------------------------------

v = 0
vold = 0
#remoteConnected = False

# -----------------------------------------------
# Ininitialize
# -----------------------------------------------

hub = CityHub()

#define timers
timer = [0,0,0]
timer[1] = delay(1)
timer[2] = delay(2)
           
#define motors
motor = [0,0,0]
motor[1] = device(Port.A,dirMotorA)
motor[2] = device(Port.B,dirMotorB)

# get the port properties
portcheck(1)
portcheck(2)    

# -----------------------------------------------
# remote connect
# -----------------------------------------------

hub.light.on(Color.RED)
print (hub.system.name())
try:
    remote = Remote(name=remoteName,timeout=remoteTimeout*1000)
except OSError as ex:
    hub.system.shutdown()

# -----------------------------------------------
# main loop
# -----------------------------------------------

while True:
   
    # --check if remote is connected ---------------------------------
    
    try:
        button = remote.buttons.pressed()
        hub.light.on(LEDconn)
        remoteConnected = True
    except OSError as ex:
        hub.light.on(LEDnotconn)
        print("Remote not connected")
        remoteConnected = False
       
        if watchdog == True:
            v=0
            drive()
        try:
            # reconnect remote
            remote = Remote(timeout=1000)
            wait(100)
            print("Remote reconnected")
            remoteConnected = True
        except OSError as ex:
            print("Remote not connected")

            
    if CheckButton(SWITCH):
        mode = mode+1
        if mode > 2: 
            mode = 1
        print (mode)
        if mode == 1 : remote.light.on(LED_A)
        if mode == 2 : remote.light.on(LED_B)    
   
        while CheckButton(SWITCH):
            button = remote.buttons.pressed()
            wait (100)

    if mode == 1 : function1()     
    if mode == 2 : function1()
    
    wait(10)
    

 

Share this post


Link to post
Share on other sites

  I was greatly interested in this project, but as I'm not much of a programmer, and also don't know German, I didn't have much luck with it early on and hoped others would 'pick up the torch' and grow it.  Obviously I have even more interest now that I've had it working for the first time.  I hope you don't feel that your work was not appreciated.

  I have written a single Arduino program ever, relying heavily on example code and existing libraries.  Python, in this context, is very similar, but as with any other programmable controller platform and language, syntax and lack of key technical knowledge are the killer when starting out.  I will do my best to experiment and learn now that I have a concrete example that is working for me on my hardware.  I really hope others with in-depth knowledge of the LEGO-specifics and more fluency in Python can help drive this project forward and give it a permanent home.  The problems you ran into which you described above are exactly the kind of thing I'm talking about.  Even someone with experience such as yourself ran into trouble debugging code after the firmware side of it changed (essentially why the Linux Kernel drives me insane), enough so to quit in frustration.  As a rudimentary-level coder, I'm not equipped to help with that, but I can understand enough to test others' end product.

  LEGO themselves should have offered an official solution to do exactly what this project accomplished.  The only thing even close is how the default program on the six-port hubs allows you to run motors up and down in speed using just the arrow buttons on the hub itself.  This is a joy on the design workbench, as I can just manipulate physical parts when building and testing MOCs without the need for programming in the PoweredUp app or a mobile device.  It is also the only realistic scenario for many children to use the PuP platform outside the bounds of the pre-defined models, but perhaps also for many adults as well!

Share this post


Link to post
Share on other sites
16 minutes ago, Lok24 said:

Thanks, as assumed, the motor(port) command, which was not supported any more.

The block looks like that:


            devs_max_speed = {38:1530,46:1890,47:1980,48:1367,49:1278,75:1367,76:1278 }
            dspeed = devs_max_speed.get(PUPDevice(port).info()['id'], 1000)
            motor[i].obj.stop()
            motor[i].obj.control.limits(speed=dspeed,acceleration=10000)
            motor[i].setSpeed(dspeed/100*0.9)

Seems that you use 2.7 which is not the latest Version....

Have a look:

 

Yes, 2.7 was the version @BatteryPoweredBricks had a copy off.

I've just loaded your version 2.9 onto a hub using firmware 3.2 and that works with both train motors and also a Technic L motor without errors.

Reconnect of the remote also works, but only if the remote name is set. That was the same behavior on 2.7 for me. I could never reconnect with firmware 3.1. As of firmware 3.2 it works, as long as the remote name is set in the script.

/M

Share this post


Link to post
Share on other sites
12 minutes ago, UltraViolet said:

I hope you don't feel that your work was not appreciated.

No, I don't, but I don't use it any more (I have no application for such a program at all, as for the two others of the "family")

I not very familiar with Python, that's why the program is a little bit -hm- tricky?

The intention I explained already.

And I'm really trying to support, the issue mentioned her was fixed in 2.9 and I reported that in April here at Eurobricks.

5 minutes ago, Marlow said:

Reconnect of the remote also works, but only if the remote name is set. That was the same behavior on 2.7 for me.

Spooky! I'm sure that I never had set this value (cause I only have on remote).

Edited by Lok24

Share this post


Link to post
Share on other sites
12 minutes ago, Lok24 said:

And I'm really trying to support, the issue mentioned her was fixed in 2.9 and I reported that in April here at Eurobricks.

The issue most likely wasn't an issue for most, as 2.7 continued to work on firmware 3.2, at least for those with train motors. So those would not have updated, as long as everything is working.

I only came across the issue, once I tried to install the script in the hub of my crocodile, that uses a Lego Technic L-Motor. And by then, I had no way of updating seeing the download links were dead. Also, your download link still indicates, that 2.7 was the last version.
 

12 minutes ago, Lok24 said:

No, I don't, but I don't use it any more (I have no application for such a program at all, as for the two others of the "family")

No trains anymore ? That would be sad.

/M

Edited by Marlow

Share this post


Link to post
Share on other sites
4 minutes ago, Marlow said:

No trains anymore ? That would be sad.

I never had any layout, just built some locos.

The idea to that program came up with the crocodile  ......

It should be useful for those, who used any motor(s) with the city hub. (not for me ;-) )

And as, mentioned, "Battery box" uses the same base.

8 minutes ago, Marlow said:

And by, I had no way of updating seeing the download links were dead.

Yes, but it was mentioned here:

Anyway if there are a few "Python experts" we could discuss how to devolop that for further applications.

Share this post


Link to post
Share on other sites
15 minutes ago, Lok24 said:

The idea to that program came up with the crocodile  ......

It should be useful for those, who used any motor(s) with the city hub. (not for me ;-) )

 

It's useful for the Crocodile, but also for any of the steam locos, which generally are build that way and then for those of us, who use 2 motors in a loco to pull a little bit more railcars than normal. With PF, at least one could stack the connectors and just put a reverse polarity switch in for the 2nd motor. That doesn't work with PU anymore. Your script solves that for those.
 

15 minutes ago, Lok24 said:

Anyway if there are a few "Python experts" we could discuss how to devolop that for further applications.

@and_ampersand_and and myself are already looking at, how we get lights or color sensors to work. Well, mostly lights. Because that would be the next thing one may want in a Crocodile or steam loco.

/M

Edited by Marlow

Share this post


Link to post
Share on other sites
1 hour ago, Lok24 said:

 

Anyway if there are a few "Python experts" we could discuss how to devolop that for further applications.

Thanks for providing the updated script! I updated my github repo with it.

I plan on getting Lights working with the script at some point.

Share this post


Link to post
Share on other sites
2 minutes ago, and_ampersand_and said:

I plan on getting Lights working with the script at some point.

You should then add them to the device list in "def portcheck(i)"

and then apply to the "drive()" routine.

As a first idea.....

 

Share this post


Link to post
Share on other sites

  Now that I've been able to have the connection working, I've started testing some build scenarios to see how everything behaves.  I've immediately run into a challenge which raises interesting questions about the possible different approaches to driving and managing the motors with Pybricks versus standard firmware functions.

  I have a train motor bogie built from a WeDo motor.  When using basic variable speed coding in the Powered Up app through a City hub with stock firmware, the motor starts and runs fine.  On the other hand, If I use the Pybricks firmware and the code we are discussing here, the motor cannot overcome the gearing resistance to start spinning without me forcing it by hand, and it will stall fairly easily.  That got me thinking about how many modes or 'strategies' there are available in Pybricks (and in the hubs in general) to generate the variable voltage output to the motors.  The stock config audibly emits the pulse-width modulation sound we are familiar with from the Powered Up and Power Functions hub outputs.  The Pybricks code in our discussion, however, does not make this sound, or if it does, not at a level I can hear.  Taking a loosely educated guess, it seems like the Pybricks code is not using full voltage as the basis for the PWM output.

  Can anyone with enough knowledge of the code comment on this?  Is there any way to have the code modified to produce the PWM output in the way the stock firmware is generating it?  I read through the code again, and I gather this is buried in one of the Pybricks pre-defined functions which is being called.  So is it a matter of calling an alternate function, or does the content of the function itself need to be altered?  I vaguely recall something from another coding situation about 'speed' control versus 'power' control.  Would this have anything to do with it?  I'd greatly appreciate if someone were able to figure this out.

Share this post


Link to post
Share on other sites
1 hour ago, UltraViolet said:

  I vaguely recall something from another coding situation about 'speed' control versus 'power' control.  Would this have anything to do with it?

I won't be of any help here, but just this: Speed vs power control would result in exactly the contrary result.

1) Using speed control: If the motor (at low initial power setting) cannot reach the speed you want, then the hub increases power = PWM level (= on vs off time = more on time) and I'd expect wheel slip rather than motor stalling. Control means that the firmware monitors the rpm of the motor. When too low = not correct speed, then increase power = on vs off ratio towards more on time. And vice versa.

2) Using power "control": This is a selected = fixed PWM level - there is no control. The firmware just applies a fixed on vs off time to the motor, regardless of speed, which changes with load.

Now, when you cannot hear this hissing sound, then something is very odd. Does your WeDo motor produced that sound when using PUp firmware? The thing is, PWM means applying full DC voltage to the motor for a certain time and then go to zero voltage for another time. Rather quickly = generating that sound. As far as I know, none of the hardware available would allow reducing the voltage applied during a PWM on-time. 

But I may be wrong and others (like @Pybricks) have to switch on the light here.

All the best  + good luck,
Thorsten

Share this post


Link to post
Share on other sites

  The PWM sound is loud and clear when using Powered Up app with stock firmware, and completely absent as far as I can hear when using Lok24's code with Pybricks.  I wish I had an oscilloscope so I could tell what was different about the waveform, other than perhaps amplitude.  This is using two otherwise identical City Hubs, with a Keybrick One as the power source swapped back and forth between them.  Maximum output voltage on both hubs, measured with a digital multimeter, is 6.04V.

  I devised a method to test for the presence of PWM.  I used PV Productions adapter cables to patch an old 9V studded light bulb in parallel in the middle of the line.  This type of bulb blinks when fed reverse polarity.  The blinking interacts with the PWM signal, creating a variable flickering effect dependent on pulse width (this can also be used to create the lighting effect for a campfire, candle, or even a welder - You're welcome!).  The two hub configurations both exhibit the flickering light effect running in reverse direction, therefore PWM is the basis of the motor control either way, although I figured that must be the case.  Something is still clearly different between the two, though.  Perhaps the stock firmware/code alters the PWM effect at slower speeds to prevent stalling and improve starting torque, much like the software option available on the PFX Brick.  Something similar was available on straight-DC model railroad transformers, usually called "pulse power".  It was only active at low speeds for exactly the same reasons.

  Someone much more familiar with device handling and code functions within Pybricks is going to have to comment on this.  It's entirely possible the same behavior as stock can be activated with a single flag in code.

  I should add, if it helps explain any of this technically, that with Powered Up/stock firmware, if you start the output low enough that the motor is stalled, you will hear a pure 1200Hz tone.  Once the motor starts spinning, the tone gets interfered with and kinda warbles.  The loudness of the tone seems to decrease with speed increase.  This may be the frequency of the low-speed assist control, or it could be the main PWM signal.  That's as much as I have the tools to observe.  This tone is entirely absent on the Pybricks setup I'm comparing it to.

Share this post


Link to post
Share on other sites

Hi, correct, all motors use the Pybricks routines,

"run" or "dc", depending on the device ID (which depends on the cable in DC-Motor cases

here is an extract :

Portcheck: # detect motor id
if "Motor" in devices[id] and not("DC" in devices[id]): 
    motor[i].setType("Motor")
# and set type for simple DC Motors    
if "DC" in devices[id]:
    motor[i].setType("DCMotor")

drive: # send the real commands to the devices
if motor[x].getType() == "Motor" :
     motor[x].obj.run(s*motor[x].getSpeed()) #  in 2.7
if motor[x].getType() == "DCMotor" : 
    motor[x].obj.dc(s) 

 

 

Share this post


Link to post
Share on other sites
13 hours ago, ejayb said:

PyBricks uses a different PWM frequency which is quieter

https://github.com/pybricks/pybricks-micropython/commit/030dd66fc0066290a1ab842fd387d11e8a8a2aac

Have you tried creating PyBricks script from scratch?

  Thank-you very much for finding this information.  Changing the PWM frequency that much dramatically higher could certainly explain the difference in motor response.  They actually directly stated this change would affect the torque curve.  If I read right, this change was committed in the version 3 Pybricks firmware.  I'd really like to know if anyone testing multiple versions can replicate my observations.

  If the change in frequency was the culprit, it certainly should have been implemented in a way that allowed you to keep the old frequency if you wanted, rather than just forcing it to the new way and assuming everything would be fine.  I don't care whether or not I hear the PWM frequency - I just want the same motor performance.  I actually appreciate hearing the PWM because it allows me to tell whether the hub port is on at low speed steps and whether the motor is stalled.

  Do you guys feel this should be reported as an issue in the Pybricks GitHub?  The thing I don't understand is why an even higher frequency would work on the EV3 and not on PUP with a similar one.  There must be some other factor(s) at play in the operating parameters for the PUP devices which would result in weak motor response.

Share this post


Link to post
Share on other sites

Hi all, wanted to let you know that I've added support for 88005 Powered up Lights to Lok24's script. You can find the script here: https://github.com/and-ampersand-and/PyBricks-Train-Motor-Control-Script

Lights will work plugged into either (or both) ports. The B side of the controller will always control them, while the A side controls any motors. Holding up or down will increase/decrease the brightness. The red button turns the lights off or fully own.

By default, the lights start off, but you can change that by modifying `lightValue` in the user defined values section of the code.

Shoutout to @BatteryPoweredBricks for help testing the script!

Edited by and_ampersand_and

Share this post


Link to post
Share on other sites

I'm sure many of the folks here are quite familiar with using PyBricks but I made a tutorial for those that haven't used it before.  Sharing this with folks new to PyBricks should prove a lot easier than sending messages back and forth :pir_laugh2:
 

 

Share this post


Link to post
Share on other sites
10 minutes ago, BatteryPoweredBricks said:

I'm sure many of the folks here are quite familiar with using PyBricks but I made a tutorial for those that haven't used it before.  Sharing this with folks new to PyBricks should prove a lot easier than sending messages back and forth :pir_laugh2:

  Huge kudos to you for presenting this in a way that should allow just about anyone to get over the knowledge or fear barrier of using this (or PyBricks in general).  It is such a relief to see this project still in development, so much thanks again also to the rest of you guys working on the coding!

  I'm wondering if it's possible to add another parameter to the code.  One of my build applications is single-ended trams.  Under normal circumstances, no 'driver' should ever be permitted to move in the reverse direction.  Is there a way to provide a hard code flag that could enable/disable reverse direction (even if two motors set opposite are connected)?  Alternatively, or in addition to this, can an additional parameter be provided where a separate maximum speed level can be set for each direction, so by this I could at least heavily limit the reverse speed?  These parameters would help me 'get over the fear barrier' of handing the remote to my young nephews or to anyone at a show, as otherwise someone's quickly going to do a lot of damage no matter what I try to tell them.

Share this post


Link to post
Share on other sites
1 hour ago, BatteryPoweredBricks said:

I'm sure many of the folks here are quite familiar with using PyBricks but I made a tutorial for those that haven't used it before.  Sharing this with folks new to PyBricks should prove a lot easier than sending messages back and forth :pir_laugh2:

Nice!

Share this post


Link to post
Share on other sites
On 12/10/2023 at 3:27 PM, BatteryPoweredBricks said:

I'm sure many of the folks here are quite familiar with using PyBricks but I made a tutorial for those that haven't used it before.  Sharing this with folks new to PyBricks should prove a lot easier than sending messages back and forth :pir_laugh2:

Well, I was not, new it existed, but not having and advanced usage that would have needed programmation didn't looked further into it until you showcased it in your video.

I've got a custom engine that uses two motors, I was doing fine with the turning remote and press both button trick, but this is so much better.

Hadn't loaded a programm in a brick since the RCX era.

And yeah, I'm a programmer, could have looked into this earlier, but I'm also lazy, and you're video was the right push. Thanks.

Share this post


Link to post
Share on other sites

Hi

I just checked the hub-to.hub communication

The idea was to use a "master"-hub and a remote to control two trains with the remote.
As the master only uses one channel I submit a user-ID and hub-ID to address the "slaves"

Here's the code:

MASTER

from pybricks.hubs import TechnicHub
from pybricks.pupdevices import Motor, Remote
from pybricks.parameters import Port, Button
from pybricks.tools import wait

hub = TechnicHub(broadcast_channel=1)
my_remote = Remote()

userID = "Lok24"
hubID_A = "HUB1" 
hubID_B = "HUB2" 

vA = 0
vB = 0
vAold = 0
vBold = 0

while True:
    pressed = my_remote.buttons.pressed()

    vAold = vA 
    vBold = vB
    if Button.LEFT_PLUS in pressed   : vA = vA + 100 
    if Button.LEFT_MINUS in pressed  : vA = vA - 100
    if Button.LEFT in pressed        : vA = 0
    if Button.RIGHT_PLUS in pressed  : vB = vB + 100 
    if Button.RIGHT_MINUS in pressed : vB = vB - 100
    if Button.RIGHT in pressed       : vB = 0

    if vA != vAold:
        hub.ble.broadcast([userID,hubID_A,vA]) 
        wait(100)
    if vB != vBold:
        hub.ble.broadcast([userID,hubID_B,vB])
        wait(100)
    wait(10)

SLAVES

 

from pybricks.hubs import CityHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Color, Port
from pybricks.tools import wait

hub = CityHub(observe_channels=[1])
motorA = Motor(Port.A)

userID = "Lok24"
hubID = "HUB2"  # <<<<< must be different for each slave

while True:
    data = hub.ble.observe(1)
   
    if data is None:
        hub.light.on(Color.RED)
    else:
        hub.light.on(Color.GREEN)
        uID,hID,v = data
        if uID == userID and hID == hubID:
            motorA.run (v)
        
    wait(100)

 

Share this post


Link to post
Share on other sites
4 hours ago, Lok24 said:

The idea was to use a "master"-hub and a remote to control two trains with the remote.
As the master only uses one channel I submit a user-ID and hub-ID to address the "slaves"

Nice, I like the implementation of including hubId in the data so each hub knows which commands are for it. I was planning on looking into two trains on one remote as well, but was probably going to have the first train broadcasting to a second train, instead of a central hub broadcasting to two different trains. You way would solve the problem of the two trains getting to far away from each other and out of broadcast range as you could just move the master hub closer. Although I don't really know the broadcast range these hubs have or how far apart they'd have to be for the broadcast to fail.

Share this post


Link to post
Share on other sites

In any way you could install a watchdog routine by sending a broadcast every x seconds and stop the train, if no broadcasts received within the last x+a seconds.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.