Bliss Posted January 7 Posted January 7 (edited) 27 minutes ago, Gunners TekZone said: FYI... Using the CLI, I run into times when nothing happens. But I have found simply typing Lego1.IsRunning without the print() will show True or False. When false, it seems though only way to get it back running is to close down the CLI and start over. Ideas? I did not run into this situation yet but I'm not testing as much as you do I guess... I'm using Python 3.11. ... >>> from LegoClassB import LegoInterfaceB >>> Lego1=LegoInterfaceB() >>> Lego1.ComPort="COM14" >>> Lego1.StartLego() >>> Lego1.Inp[1].On False >>> Lego1.Out[1].On False >>> Lego1.Out[1].On=True >>> Lego1.Out[1].On=False >>> Lego1.Out[2].On=True >>> Lego1.Out[2].On=False >>> Lego1.IsRunning True Edited January 7 by Bliss Quote
Gunners TekZone Posted January 7 Posted January 7 (edited) 12 minutes ago, Bliss said: but I'm not testing as much as you do I guess... Hah... I deal with ME/CFS... Thus living on disability income and mostly homebound. When I am not sleeping most of my hours away, and feeling well enough to think on the wakening ones... ALL I tend to do is work with LEGO, particularly Mindstorms/Technic/Trains etc. as it seems to help me keep some mental focus. I like static LEGO builds, but my preference is things that do something, and help learn me some smrt tech skillz (as the kids never say). Better than endless YouTube or Netflix... But that sometimes happens. Edited January 7 by Gunners TekZone Quote
Bliss Posted January 7 Posted January 7 I just installed Python 3.13 and uninstalled 3.11... But there are still traces from old versions... The "Path" environment variable was still set with 3.10 ... I corrected this so pip is now back fonctionning... So make sure your PATH is set correctly... Quote
Bliss Posted January 7 Posted January 7 (edited) @Gunners TekZone So I installed Python 3.13 and uninstalled older versions and updated manually the PATH system variable in windows (Control Panel, in the serch bar, entre "Path", click modify environment variables, in the next window click at the bottom Env. Variables...) After that I reinstalled with a Dos Window, the python net using pip install pythonnet. I checked with the Python 3.13 CLI that I was able to import clr, AddReference to my LegoClassB and use LegoInterfaceB... Everything was OK... Then in Thonny now - Thonny uses its own Python exe... So, You have to go in menu run, Configure Interpreter, Interpreter Tab and specify the path and exe of the python 3.13 which is the one that has python net installed... - Now, in the Thonny Shell, it will show Python 3.13.1. - If you enter import clr at the >>> prompt it should work Edited January 10 by Bliss Quote
Gunners TekZone Posted January 7 Posted January 7 2 minutes ago, Bliss said: specify the path and exe of the python 3.13 Ah Ha... I missed that part... I took Local Python 3 as literally the one Windows recognised. I'll try that. Thanks Quote
Bliss Posted January 7 Posted January 7 (edited) 16 minutes ago, Gunners TekZone said: Ah Ha... I missed that part... I took Local Python 3 as literally the one Windows recognised. I'll try that. Thanks I'm guessing it is possible to install the pythonnet library (Which allow the use of import clr) in the thonny environment. First go back in the menu Run, Configure Interpreter and select the Thonny local python.exe as the python executable. In the menu tools, click on open system shell. I think it is possible from there... From the shell, I entered: pip install pythonnet and it did install in thonny libraries... Then from the Thonny local python 3.10, I was able to use import clr etc... Edited January 7 by Bliss Quote
Gunners TekZone Posted January 7 Posted January 7 (edited) 18 minutes ago, Bliss said: I'm guessing it is possible to install the pythonnet library (Which allow the use of import clr) in the thonny environment. I think, just putting in the correct path to the python.exe worked for that... since now I stall out here instead: import time import clr clr.AddReference(r"C:\Users\Gunner\Desktop\LegoClassB\bin\Release\LegoClassB") from LegoClassB import LegoInterfaceB Lego1 = LegoInterfaceB() Lego1.ComPort="COM1" Lego1.StartLego() Lego1.IsRunning Lego1.Out[1].On=True time.sleep(2) Lego1.Out[1].On=False time.sleep(2) Lego1.Out[1].On=True time.sleep(2) Lego1.Out[1].On=False Lego1.StopLego() >>> %Run LEGO_IntB_IDE_Test.py Traceback (most recent call last): File "C:\Users\Gunner\Desktop\LEGO_IntB_IDE_Test.py", line 4, in <module> from LegoClassB import LegoInterfaceB ImportError: cannot import name 'LegoInterfaceB' from 'LegoClassB' (unknown location) >>> And, Yes... that is the correct path, as it works in the CLI (well, not as a coherent program, but line by line. And the sleep parts are meaningless when waiting for the next command anyhow :P ) Edited January 7 by Gunners TekZone Quote
Bliss Posted January 7 Posted January 7 (edited) On 1/6/2025 at 9:41 PM, Gunners TekZone said: think, just putting in the correct path to the python.exe worked for that... since now I stall out here instead: import time import clr clr.AddReference(r"C:\Users\Gunner\Desktop\LegoClassB\bin\Release\LegoClassB") from LegoClassB import LegoInterfaceB Lego1 = LegoInterfaceB() And, Yes... that is the correct path, as it works in the CLI (well, not as a coherent program, but line by line...) When I paste the content inside the code box into NotePad++, it shows a stange character at the end of: from LegoClassB import LegoInterfaceB It happens often in the forum Code Window... I have to double check some time... (These character are not visible in the final post but we can see them in notepad++ and sometime, in the code edition window. Edited January 10 by Bliss Quote
Bliss Posted January 7 Posted January 7 Here is a link with some Python Programs I adapted to Thonny: Sample Lego Interface B Python Files Quote
Gunners TekZone Posted January 7 Posted January 7 (edited) On 1/6/2025 at 6:53 PM, Bliss said: When I paste the content inside the code box into NotePad++, it shows a stange character at the end of: from LegoClassB import LegoInterfaceB Usually I found Thonny throwing an error in that case. But I double checked,I have redownload the file, parked it at the root of C: to make the path simpler. And I even had a snack before attempting again... But it is Just. Not. Working. For. Me. :( import clr clr.AddReference(r'C:\LegoClassB\bin\Release\LegoClassB') from LegoClassB import LegoInterfaceB >>> %Run LegoExample_01.py Traceback (most recent call last): File "C:\Users\Gunner\Downloads\LegoExample_01.py", line 3, in <module> from LegoClassB import LegoInterfaceB ImportError: cannot import name 'LegoInterfaceB' from 'LegoClassB' (unknown location) >>> I have tried " ' / \ r" r' \\ etc. adnausm... It doesn't appear to be syntax related. It has to be something with my file system??? I even tried to start over with a different Python, Thonny, etc. install on my Windows 7 PC... But that turned into its own unmitigated disaster... Due being "Too old, go away!" (I paraphrase) ANYHOW... Again... I need to take a break. I am learning, but not sure how much fun I am having, heh. ====================================================================================================================================== OK... one last ditch attempt to understand... and a random @rs guess I opened both the LegoClassB files in Notepad++... mostly nonsense. But I do see path references to your setup. It is possible the code in whatever of these programs get "imported" is getting fouled on that???? Or is this grasping at straws? Edited January 15 by Gunners TekZone Quote
Bliss Posted January 7 Posted January 7 @Gunners TekZone But at some point you said it was working no? Is it working in the Python CLI now or not? Is it only in Thonny that you have problems? Is it working in Windows 10 but not in windows 7 ? (I think I read somewhere, pythonnet would not work well on win 7) Quote
Bliss Posted January 7 Posted January 7 (edited) @Gunners TekZone I copied the LegoClassB.dll (ONLY This DLL) in a Python work Folder along with the .py file (Same ones than in the dropbox link). In Thonny, on the Files pane on the Left, I selected this Pyton Folder and see listed the Dll and the .py files. This becomes the working directory. In the LegoTrain_01.py, the path was simply the name of the dll: import clr clr.AddReference(r"LegoClassB") from LegoClassB import LegoInterfaceB Lego1=LegoInterfaceB() Then I hit the "Play" button in Thonny and this is working here very well too. Edited January 7 by Bliss Quote
Gunners TekZone Posted January 7 Posted January 7 17 minutes ago, Bliss said: But at some point you said it was working no? Is it working in the Python CLI now or not? Is it only in Thonny that you have problems? Oh my... do I feel like a dodo... I like having a tight focus when I can, but sometimes a laser sharp focus is not always good. Of course, since CLI works, then there is nothing wrong with my path or the files. Doh!! Basically, since the CLI method only works line by line. I did briefly look at .bat files with Python scripts... but I am not successful there yet either. Thus my fixation on Thonny... But I did try briefly with IDLE (I don't like using it), but it has the exact same failure. So it doesn't appear IDE related. What oh what is remaining? Quote
Bliss Posted January 7 Posted January 7 Just answering my question about python compatibility with windows 7. Quote It looks like the most recent version of Python that will work on Windows 7 is Python 3.8.15 Quote
Gunners TekZone Posted January 7 Posted January 7 (edited) I installed Python 3.8.6 on my Win7 system. I tried to run this... import clr clr.AddReference(r"C:\Users\Gunner\Desktop\LegoClassB\bin\Release\LegoClassB") from LegoClassB import LegoInterfaceB Lego1 = LegoInterfaceB() Lego1.ComPort="COM1" Lego1.StartLego() Lego1.IsRunning Lego1.Out[1].On=True And the error I ran into on the Win7, Thonny, setup was a long ramble about ... something... Same error when trying to run in the CLI System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information. The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\Gunner\Downloads\LegoExample_01.py", line 2, in <module> clr.AddReference(r"C:\Users\Gunner\Desktop\LegoClassB\bin\Release\LegoClassB") System.IO.FileLoadException: Could not load file or assembly 'file:///C:\Users\Gunner\Desktop\LegoClassB\bin\Release\LegoClassB.dll' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515) File name: 'file:///C:\Users\Gunner\Desktop\LegoClassB\bin\Release\LegoClassB.dll' ---> System.NotSupportedException: An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information. at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) at System.Reflection.RuntimeAssembly.InternalLoadFrom(String assemblyFile, Evidence securityEvidence, Byte[] hashValue, AssemblyHashAlgorithm hashAlgorithm, Boolean forIntrospection, Boolean suppressSecurityChecks, StackCrawlMark& stackMark) at System.Reflection.Assembly.LoadFrom(String assemblyFile) at Python.Runtime.AssemblyManager.LoadAssemblyPath(String name) at Python.Runtime.CLRModule.AddReference(String name) Edited January 7 by Gunners TekZone Quote
Bliss Posted January 7 Posted January 7 (edited) @Gunners TekZone If we forget about win7 for a moment and come back to win10, I think the unknown location problem is related to: https://github.com/pythonnet/pythonnet/discussions/2420 If you put the whole directory of the LegoClassB.zip inside your python working directory, then it appears the PATH starting with the LegoClassB folder is already seen by Python because Python automatically uses the working path in its seach path for modules... The folder named LegoClassB conflicts with the DLL file Name. So in "from LegoClassB import LegoInterfaceB" line, Python prioritize the Folder as a package at first... So, remove the LegoClassB folder from your working directory and leave only the LegoClassb.dll in your working directory along with your .py files and just do like my second previous post. OR, rename the LegoClasB folder to make it different than the Dll file name. >>> import clr >>> clr.AddReference(r"C:\Users\sengx\source\repos\Python\LegoClassB\bin\Release\LegoClassB") <System.Reflection.RuntimeAssembly object at 0x000001B853287740> >>> from LegoClassB import LegoInterfaceB Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: cannot import name 'LegoInterfaceB' from 'LegoClassB' (unknown location) >>> Edited January 7 by Bliss Quote
Gunners TekZone Posted January 7 Posted January 7 32 minutes ago, Bliss said: leave only the LegoClassb.dll in your working directory along with your .py files I parked your LegoExample_01.py and LegoClassB.dll in a separate folder. Adjusted for my device settings and proper path... And that Worked!!... Thanks! Words just don't have the same impact So many tricks and quirks to remember, based on Language, version, OS, PEBKAC, cats interrupting train of thought (Yes, I will even blame my beloved furbabies for my issues, Hah!)... It is a wonder I like modern programming at all Now... MQTT... Dare I try that again?? Quote
Gunners TekZone Posted January 7 Posted January 7 (edited) AH HA!... Could this be the first time a LEGO DACTA Interface-B has been used to communication over a network via MQTT??? Why, YES!!... Thanks to @Bliss's enormous efforts developing the Python Interface code, another individual's efforts on Reddit helping me with a MQTT publishing issue on another LEGO project, and my sacrificing hours of precious sleep for some rair, focused, neuron activity (I'll pay for it later) pleading for this Interface-B to work, darnit, just work will ya... ah... er... anyhow... Again, YES... It just might be so Here is the script that I have tested on Windows10(64) and Windows7(32) NOTE: You MUST have the LegoClassB.dll, file from @Bliss's fabulous work, inside the same folder as this script. That file is really the heart of this project! I have put both in this link: MQTT Interface-B import random import time from timeloop import Timeloop from datetime import timedelta from paho.mqtt import client as mqtt_client import clr clr.AddReference(r'LegoClassB') # LegoClassB.dll needs to be in same folder as this script from LegoClassB import LegoInterfaceB ############################################################################## # GLOBAL VARIABLES / SETUP # ############################################################################## # -----------------------# # MQTT Configuration # # -----------------------# broker = 'xxx.xxx.xxx.xxx' # IP of your broker port = 1883 # Port for MQTT (default is 1883) topic = "test/relay" # Topic to subscribe/publish to # We create a random ID just so multiple clients on the same broker # don't stomp on each other: client_id = f'subscribe-{random.randint(0, 100)}' # Username / password for the broker (if needed) username = 'YOURUSERNAME' password = 'YORPASSWORD' # --------------------------# # Interface-B Configuration # # --------------------------# print("LEGO config") Lego1=LegoInterfaceB() Lego1.ComPort="COM1" Lego1.StartLego() time.sleep(1) Lego1.Out[1].Pow = 7 Lego1.Out[1].Dir = False print("Lego 1 Running: ", Lego1.IsRunning) # This will indicate the relay state at startup: buttonFlag = 0 # -----------------------# # The Big Global Client # # -----------------------# # We declare 'client' here. We'll fill it in `connect_mqtt()`. # This is so that any function that needs it can do `global client`. client = None ############################################################################## # MQTT SETUP FUNCTIONS # ############################################################################## def connect_mqtt(): # Connects to the MQTT broker exactly once and assigns the resulting # mqtt_client.Client instance to the global 'client'. # Returns the same client, but we also store it in the global variable. global client # Tells Python we're referring to the global variable above. def on_connect(client, userdata, flags, rc): #Callback that fires when a connection to the broker is established. if rc == 0: print("Connected to MQTT Broker!") else: print(f"Failed to connect. Return code={rc}") # Actually create the client object client = mqtt_client.Client(mqtt_client.CallbackAPIVersion.VERSION1, client_id) # May trigger a depreciation warning #client = mqtt_client.Client(client_id) # Assign credentials client.username_pw_set(username, password) # Assign the on_connect callback client.on_connect = on_connect # Connect to the broker client.connect(broker, port) return client def subscribe(): # Sets up subscription to a topic and defines how incoming messages are handled. global client # We'll need the client to call subscribe on it def on_message(client, userdata, msg): """ Callback that fires when a message arrives on a topic we subscribed to. """ print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic") # If the received payload is '1', turn the port ON else OFF # (Adjust logic if your messages are strings other than '1'/'0') if msg.payload.decode() == '1': Lego1.Out[1].On = True print("Light ON") else: Lego1.Out[1].On = False print("Light OFF") # Subscribe to the desired topic client.subscribe(topic) # Assign the on_message callback client.on_message = on_message ############################################################################## # FUNCTIONS TO PUBLISH MESSAGES # ############################################################################## def publish_message(): # Publishes the current state of 'buttonFlag' to the MQTT broker. #global client # We'll need the global client print("Publishing message:", buttonFlag) client.publish(topic, buttonFlag) ############################################################################## # BUTTON-RELATED CODE # ############################################################################## # Setup a periodic check for button press ButtonCheck = Timeloop() @ButtonCheck.job(interval=timedelta(seconds=1)) def sample_job_every_1s(): # Will cycle if left pressed # Called automatically when the button on port '1' is pressed. # We'll flip the buttonFlag from 1 to 0 or 0 to 1 and then publish. global buttonFlag # Tells Python to use the global variable if Lego1.Inp[1].On: print("Button Pressed!") # Flip the state of buttonFlag if buttonFlag == 1: buttonFlag = 0 Lego1.Out[1].On = True print("Send ON") else: buttonFlag = 1 Lego1.Out[1].On = False print("Send OFF") # Now publish our updated buttonFlag via MQTT publish_message() ############################################################################## # MAIN LOOP (run) # ############################################################################## def run(): # Main entry-point for our script. Connect to MQTT, subscribe, # and start the loop forever. global client # We'll store the client in the global variable # 1. Connect once connect_mqtt() # 2. Subscribe once subscribe() # 3. Start an infinite loop to process messages and keep the connection open client.loop_forever() ############################################################################## # START THE PROGRAM HERE # ############################################################################## if __name__ == '__main__': ButtonCheck.start(block=False) # This calls the timed button check function run() Edited January 8 by Gunners TekZone Quote
Bliss Posted January 7 Posted January 7 8 hours ago, Gunners TekZone said: AH HA!... Could this be the first time a LEGO DACTA Interface-B has been used to communication over a network via MQTT??? Congrats! One small step for man, one giant leap for old lego fans ;-) We can now think of using Smart Home Hubs and devices like Home Assistant, Hubitat, SmartThings, name it, that supports MQTT, to start/stop an "Old 9V Train" automatic sequence that uses Lego Interface B. We could add a button on Home Assistant (HA) UI (I'm using Home Assistant) or use HA Automation, or Node-Red Plugin that will use MQTT to interact with the Lego Box... That certainly gives some ideas... Quote
Gunners TekZone Posted January 7 Posted January 7 (edited) A teaser short video of the MQTT Interface-B. I am neither a coherent programmer or videographer... But I try. Gotta do a lot of rest and recoup now (thinking is hard)... But I am looking forward to doing more here, with these wonderful LEGO devices! Edited January 8 by Gunners TekZone Quote
Gunners TekZone Posted January 8 Posted January 8 11 hours ago, Bliss said: Congrats! Thank You! Your work made it all possible. FYI, after some experimenting, I found that with having both my script and your .dll in the same folder, all I needed for the clr path is: clr.AddReference(r'LegoClassB') # LegoClassB.dll needs to be in same folder as this script And as a test, I them renamed the folder... Worked. I then copied it over to my Windows 7 PC (just for kicks... BUT! It worked there as well!!) So this MQTT script has been tested as fully functional on Windows 10(64) and Windows 7(32) I likey! I updated my prior post with a zip file containing my script and your .dll Quote
Bliss Posted January 8 Posted January 8 (edited) 6 minutes ago, Gunners TekZone said: FYI, after some experimenting, I found that with having both my script and your .dll in the same folder, all I needed for the clr path is: clr.AddReference(r'LegoClassB') # LegoClassB.dll needs to be in same folder as this script I did the same observation as I mentionned in a previous post that I suspect you have not seen as we posted almost at the same time. Edited January 8 by Bliss Quote
Gunners TekZone Posted January 8 Posted January 8 (edited) Ah, yes... Seems I totally missed that post. Sorry. Edited January 8 by Gunners TekZone Quote
Bliss Posted January 9 Posted January 9 I had a look to Shamlian 10 years old Python program. It was not working at all. I guess Python versions changed a lot over the years. So I modified it to make it work under Python 3.13. Took few hours of debugging, googling etc... Original Link that has been already posted few time here. Then I decided to rewirte some part of the driver and made quite a few changes and I renamed the dacta.py file to legob file. LINK TO NEW LEGO B Python Driver All you have to do is to put the legob.py file in the same working folder as your project. For example, in Thonny python editor, open your working folder on the left pane and this file should copied there. Then you can test with Thonny Shell by entering the following at the >>> prompt: >>> from legob import LegoB >>> Lego1 = LegoB('COM1') You should get "Got confirmation string." if everything works >>> Lego1.out(1).on() this should Activate output A if you want to use letter for output port then enter the following: >>> A, B, C, D, E, F, G, H = 1, 2, 3, 4, 5, 6, 7, 8 Then: >>> Lego1.out(A).off() should turn off output A To get input states or values etc: >>> Lego1.inp(1).on # will print True or False. >>> Lego1.inp(8).val >>> Lego1.inp(6).rot to reset the rotation count to 0 or other value: >>> Lego1.inp(6).rot=0 There are also temperature: >>> Lego1.inp(3).tempc >>> Lego1.inp(3).tempf >>> Lego1.inp(0).on is the state of the Red Stop Button on the lego box. You can set alias to inp ou out ports: >>> MotorA = Lego1.out(A) Then to start the motor right at power 5: >>> MotorA.pow(5) >>> MotorA.onr All output commands: on(), onl() (on left), onr() (on right), off() (coast to stop), brk() (Brake stop), rev() (reverse), pow(#) set power where #=0 to 7, onfor(t) output will turn on for t tenth of seconds, l() (Set direction left), r() (Set direction right) To stop lego box: Lego1.close() I did not test thoroughly. Let me know if you find any issues. Quote
Gunners TekZone Posted January 9 Posted January 9 On 1/6/2025 at 1:30 PM, Bliss said: (Do the same for Lego2, Lego3, Lego4, Lego999 lol, but first lets make it work with Lego1) I had just sat down to type out my progress so far with endurance and multi-Interface-B testing your script in my MQTT code. All very stable!!! While my Python skills are barely above "Hello World", I was able to have the MQTT script running while also swapping button and action commands across my two Interface-Bs. One running the MQTT and the other a train.. bth running on my LEGO PC (Win7 32), and that makes me very happy that it works there instead of only on my primary Win10 64 laptop. I am still probably a week away from getting my third one, but it is at least in Canada now :) Quote I had a look to Shamlian 10 years old Python program. It was not working at all. I guess Python versions changed a lot over the years. So I modified it to make it work under Python 3.13. Took few hours of debugging, googling etc... Then I decided to rewirte some part of the driver and made quite a few changes and I renamed the dacta.py file to legob file. I had actually been working through that Shamlian code again, just today... And while I could get some limited connectivity (until timeout) and was trying to trace the logic though the module... That was as far as I could reliably get. So I basically came to the similar conclusion: That it was either more severely broken, due age, than I could track down, or I was clueless. Honestly, I suspected both answers where equally valid :P That was when I switched to the simpler multi-Interface-B tests. I am very glad you could do what I couldn't!! I am looking forward to testing your legob.py script!! Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.