--- a/src/eric7/MicroPython/Devices/MicrobitDevices.py Fri Mar 21 18:12:17 2025 +0100 +++ b/src/eric7/MicroPython/Devices/MicrobitDevices.py Sun Mar 23 14:55:14 2025 +0100 @@ -654,12 +654,12 @@ @rtype tuple of (str, tuple) @exception OSError raised to indicate an issue with the device """ - if self.hasCircuitPython(): + if self.hasCircuitPython() or self.supportsDirectories(): return super().lls( dirname=dirname, fullstat=fullstat, showHidden=showHidden ) else: - # BBC micro:bit with MicroPython does not support directories + # BBC micro:bit with old MicroPython does not support directories command = """ import os as __os_ @@ -667,8 +667,11 @@ return showHidden or (filename[0] != '.' and filename[-1] != '~') def stat(filename): - size = __os_.size(filename) - return (0, 0, 0, 0, 0, 0, size, 0, 0, 0) + try: + return __os_.stat(filename) + except AttributeError: + size = __os_.size(filename) + return (0, 0, 0, 0, 0, 0, size, 0, 0, 0) def listdir_stat(showHidden): files = __os_.listdir() @@ -701,8 +704,50 @@ if self.hasCircuitPython(): return super().pwd() else: - # BBC micro:bit with MicroPython does not support directories - return "" + try: + return super().pwd() + except OSError: + # BBC micro:bit with old MicroPython does not support directories + return "" + + def rm(self, filename): + """ + Public method to remove a file from the connected device. + + @param filename name of the file to be removed + @type str + """ + if filename.startswith("/") and not self.supportsDirectories(): + filename = filename[1:] + super().rm(filename) + + def putData(self, deviceFileName, content): + """ + Public method to write the given data to the connected device. + + @param deviceFileName name of the file to write to + @type str + @param content data to write + @type bytes + @return flag indicating success + @rtype bool + """ + if deviceFileName.startswith("/") and not self.supportsDirectories(): + deviceFileName = deviceFileName[1:] + return super().putData(deviceFileName, content) + + def getData(self, deviceFileName): + """ + Public method to read data from the connected device. + + @param deviceFileName name of the file to read from + @type str + @return data read from the device + @rtype bytes + """ + if deviceFileName.startswith("/") and not self.supportsDirectories(): + deviceFileName = deviceFileName[1:] + return super().getData(deviceFileName) ################################################################## ## time related methods below @@ -970,6 +1015,157 @@ """ return True + def _boardInformationCommands(self): + """ + Protected method defining the list of commands to be execute on the board + for determining information about the board. + + @return list of command strings to be executed + @rtype list of str + """ + # These are a subset of the generic ones. + return [ # needs to be splitted for boards with low memory + """def get_board_info(): + res = {} + + try: + import os + uname = os.uname() + res['sysname'] = uname.sysname + res['nodename'] = uname.nodename + res['release'] = uname.release + res['version'] = uname.version + res['machine'] = uname.machine + except AttributeError: + import sys + res['sysname'] = sys.platform + res['nodename'] = sys.platform + res['release'] = '.'.join(str(v) for v in sys.implementation.version) + res['version'] = sys.version.split(';', 1)[-1].strip() + res['machine'] = sys.implementation._machine + + return res + +print(get_board_info()) +del get_board_info +""", + """def get_board_info(): + res = {} + + import sys + res['py_platform'] = sys.platform + res['py_version'] = sys.version + + try: + res['mpy_name'] = sys.implementation.name + except AttributeError: + res['mpy_name'] = 'unknown' + try: + res['mpy_version'] = '.'.join((str(i) for i in sys.implementation.version)) + except AttributeError: + res['mpy_version'] = 'unknown' + try: + import pimoroni + res['mpy_variant'] = 'Pimoroni Pico' + try: + import version + res['mpy_variant_info'] = version.BUILD + res['mpy_variant_version'] = version.BUILD.split('-')[2][1:] + except ImportError: + res['mpy_variant_info'] = '' + res['mpy_variant_version'] = '' + except ImportError: + res['mpy_variant'] = '' + res['mpy_variant_info'] = '' + res['mpy_variant_version'] = '' + + return res + +print(get_board_info()) +del get_board_info +""", + """def get_board_info(): + res = {} + + try: + import ulab + res['ulab'] = ulab.__version__ + except ImportError: + res['ulab'] = None + + return res + +print(get_board_info()) +del get_board_info +""", + # micro:bit specific variants due to missing float support + """def get_board_info(): + res = {} + + import gc + gc.enable() + gc.collect() + mem_alloc = gc.mem_alloc() + mem_free = gc.mem_free() + mem_total = mem_alloc + mem_free + res['mem_total'] = mem_total + res['mem_used'] = mem_alloc + res['mem_free'] = mem_free + del gc, mem_alloc, mem_free, mem_total + + return res + +print(get_board_info()) +del get_board_info +""", + """def get_board_info(): + res = {} + + try: + import os + stat_ = os.statvfs('/flash') + res['flash_info_available'] = True + res['flash_total'] = stat_[2] * stat_[0] + res['flash_free'] = stat_[3] * stat_[0] + except (AttributeError, OSError): + res['flash_info_available'] = False + + return res + +print(get_board_info()) +del get_board_info +""", + """def get_board_info(): + res = {} + + import machine as mc + import microbit as mb + try: + if isinstance(mc.freq(), tuple): + res['mc_frequency'] = mc.freq()[0] + else: + res['mc_frequency'] = mc.freq() + except NotImplementedError: + res['mc_frequency'] = None + try: + res['mc_temp_c'] = mc.Temp().read() + except AttributeError: + try: + res['mc_temp_c'] = mb.temperature() * 100 + except AttributeError: + res['mc_temp_c'] = None + res['mc_id'] = mc.unique_id() + if 'mc_id' in res: + res['mc_id'] = ':'.join('{0:02X}'.format(x) for x in res['mc_id']) + else: + res['mc_id'] = None + + return res + +print(get_board_info()) +del get_board_info +""", + ] def getBoardInformation(self): """ @@ -979,12 +1175,50 @@ @rtype dict """ res = super().getBoardInformation() + + # post-process the results to determine the right float entries + + # 1. memory + res["mem_total_kb"] = res["mem_total"] / 1024.0 + res["mem_used_kb"] = res["mem_used"] / 1024.0 + res["mem_used_pc"] = res["mem_used"] / res["mem_total"] * 100.0 + res["mem_free_kb"] = res["mem_free"] / 1024.0 + res["mem_free_pc"] = res["mem_free"] / res["mem_total"] * 100.0 + + # 2. flash + if res["flash_info_available"]: + res["flash_total_kb"] = res["flash_total"] / 1024.0 + res["flash_free_kb"] = res["flash_free"] / 1024.0 + res["flash_used_kb"] = res["flash_total_kb"] - res["flash_free_kb"] + res["flash_free_pc"] = res["flash_free_kb"] / res["flash_total_kb"] * 100.0 + res["flash_used_pc"] = res["flash_used_kb"] / res["flash_total_kb"] * 100.0 + + # 3. machine + res["mc_frequency_mhz"] = ( + res["mc_frequency"] / 1000000.0 if res["mc_frequency"] is not None else None + ) with contextlib.suppress(KeyError): if res["mc_temp_c"] is not None: res["mc_temp_c"] = res["mc_temp_c"] / 100 # due to integer support only return res + def isMicrobit(self): + """ + Public method to check, if the device is a BBC micro:bit or Calliope mini. + + @return flag indicating a micro:bit device + @rtype bool + """ + if ( + ("micro:bit" in self.deviceName() or "Calliope" in self.deviceName()) + and not self.hasCircuitPython() + and not self.supportsDirectories() + ): + return True + + return False + def createDevice(microPythonWidget, deviceType, _vid, _pid, _boardName, serialNumber): """ @@ -1006,3 +1240,7 @@ @rtype MicrobitDevice """ return MicrobitDevice(microPythonWidget, deviceType, serialNumber) + + +# +# eflag: noqa = M-613