Update 2022-02-21
After getting back into this, the original solution no longer works. Lego changed the directory structure for projects. Each upload creates:
- a random number directory with
projects
- a
__init__.mpy
file which is the compiled version of your sent code
The above means that we can no longer read from the uploaded code to run. What we can do instead is create files elsewhere on the file system to get our code to persist.
For example, this MVP will achieve what you need:
- Upload the following:
content = """
__version__ = "0.1.1-20220221"
def hello() -> None:
print("hello from {}".format(__version__))
"""
f = open("mindstorms/custom.py", "w")
f.write(content)
f.close()
- Create a second project with the following:
from mindstorms.custom import hello
hello()
The previous solution was brittle, and I'm unsure when the change that broke it actually occurred. I've tested this on Hub OS 3.1.43 and it works. I do not like this solution, since you're just writing out the file and it makes it a little worse for editing, but at least this is fine.
Why mindstorms.custom
? That seems like a fine place to put your own custom functionality, it is unlikely to be overwritten by the official Mindstorms distribution, and centralizes all of the code in one place. You could create your own directory off of root and put the file(s) there, but for the sake of ease-of-use, let's just do this.
As before, keeping the older answers below.
EDIT: spent some more time on this, and figured it out! I'm keeping the original answer below. Here's a short reproducible solution. Tested working with (taken from os.uname
)
sysname='LEGO Technic Large Hub'
nodename='LEGO Learning System Hub'
release='1.11.0'
version='v1.11-1139-gf7407e5a0 on 2020-06-19'
machine='LEGO Technic Large Hub with STM32F413xx'
Create a new python project with this content:
import os
import sys
def example() -> None:
print("imported")
# EOF
# upload the module to the Hub
print("beginning upload from {}...".format(__name__))
os.chdir("projects")
open("__init__.py", "w").close()
filename = "{}.py".format(__name__.split("/")[-1])
new_filename = "mystorms.py"
try:
os.remove(new_filename)
except:
print("{} does not exists".format(new_filename))
os.rename(filename, new_filename)
# remove everything after EOF
with open(new_filename, "r") as f:
content = f.read()
content = content.split("# EOF")[0]
with open(new_filename, "w") as f:
f.write(content)
print(os.listdir())
sys.exit()
The stuff after # EOF
does the actual "uploading" and ensures that when you try to import this elsewhere, you don't re-"upload" it. Note that, in regular python, you'd wrap this in if __name__ == "__main__"
, but Mindstorms doesn't use that convention when it's running.
Send that project to your Hub and run it. Note that you'll have those files that persist on your Hub, and I have no idea if there are memory constraints that you'll need to worry about.
Create a new python project with the following:
import os
import projects.mystorms as ms
print(os.uname())
ms.example()
The above should work. Note that if you need to update the file, you'll have to adjust the module code to delete the original if it exists, but that's a minor change. I have not done anything beyond the above, but this could lead to a way to get missing stdlib stuff into Mindstorms as well.
Haven't found a solution, but I did go through most of the standard library to see what else you can import which is kind of related... The below did not throw an error, but I have not yet attempted anything with the packages themselves.
import array
import builtins
import cmath
import ctypes
import errno
import gc
import hashlib
import heapq
import io
import json
import math
import os
import random
import re
import select
import struct
import sys
import time
The underlying theme seems to be limiting access to the filesystem and forcing any async operations to be done with the actual Mindstorms methods (e.g. hub.speaker.start_beep()
and the like). There is some directory structure (the 0-19 files), but I haven't figured out if that can be used to achieve what you need.
Also interesting to note is that you can't import typing
, but you can use type hints in your functions. I also strongly dislike that you can't copy-paste from the console.
os.uname()
does not match the version displayed in the desktop app. I've raised the issue with Lego and asked for details around how they do updates. – Pineal