MicroPython: changed the logic to synchronize the time because some devices don't implement long integer and failed defining the 'set_time()' function.

Sat, 20 Feb 2021 11:52:49 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 20 Feb 2021 11:52:49 +0100
changeset 8117
aaa5e0eacd4e
parent 8116
ef415e4efd70
child 8118
0ecebcd8875e

MicroPython: changed the logic to synchronize the time because some devices don't implement long integer and failed defining the 'set_time()' function.

eric6/MicroPython/CircuitPythonDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/EspDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/GenericMicroPythonDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonCommandsInterface.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicroPythonWidget.py file | annotate | diff | comparison | revisions
eric6/MicroPython/MicrobitDevices.py file | annotate | diff | comparison | revisions
eric6/MicroPython/PyBoardDevices.py file | annotate | diff | comparison | revisions
--- a/eric6/MicroPython/CircuitPythonDevices.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/CircuitPythonDevices.py	Sat Feb 20 11:52:49 2021 +0100
@@ -11,7 +11,6 @@
 import os
 
 from PyQt5.QtCore import pyqtSlot
-from PyQt5.QtWidgets import QDialog
 
 from E5Gui import E5MessageBox, E5FileDialog
 
@@ -28,16 +27,19 @@
     """
     DeviceVolumeName = "CIRCUITPY"
     
-    def __init__(self, microPythonWidget, parent=None):
+    def __init__(self, microPythonWidget, deviceType, parent=None):
         """
         Constructor
         
         @param microPythonWidget reference to the main MicroPython widget
         @type MicroPythonWidget
+        @param deviceType device type assigned to this device interface
+        @type str
         @param parent reference to the parent object
         @type QObject
         """
-        super(CircuitPythonDevice, self).__init__(microPythonWidget, parent)
+        super(CircuitPythonDevice, self).__init__(
+            microPythonWidget, deviceType, parent)
         
         self.__workspace = self.__findWorkspace()
         
--- a/eric6/MicroPython/EspDevices.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/EspDevices.py	Sat Feb 20 11:52:49 2021 +0100
@@ -27,16 +27,18 @@
     """
     Class implementing the device for ESP32 and ESP8266 based boards.
     """
-    def __init__(self, microPythonWidget, parent=None):
+    def __init__(self, microPythonWidget, deviceType, parent=None):
         """
         Constructor
         
         @param microPythonWidget reference to the main MicroPython widget
         @type MicroPythonWidget
+        @param deviceType device type assigned to this device interface
+        @type str
         @param parent reference to the parent object
         @type QObject
         """
-        super(EspDevice, self).__init__(microPythonWidget, parent)
+        super(EspDevice, self).__init__(microPythonWidget, deviceType, parent)
     
     def setButtons(self):
         """
--- a/eric6/MicroPython/GenericMicroPythonDevices.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/GenericMicroPythonDevices.py	Sat Feb 20 11:52:49 2021 +0100
@@ -23,12 +23,14 @@
     """
     Class implementing the device interface for generic MicroPython boards.
     """
-    def __init__(self, microPythonWidget, vid, pid, parent=None):
+    def __init__(self, microPythonWidget, deviceType, vid, pid, parent=None):
         """
         Constructor
         
         @param microPythonWidget reference to the main MicroPython widget
         @type MicroPythonWidget
+        @param deviceType device type assigned to this device interface
+        @type str
         @param vid vendor ID
         @type int
         @param pid product ID
@@ -36,8 +38,8 @@
         @param parent reference to the parent object
         @type QObject
         """
-        super(GenericMicroPythonDevice, self).__init__(microPythonWidget,
-                                                       parent)
+        super(GenericMicroPythonDevice, self).__init__(
+            microPythonWidget, deviceType, parent)
         
         self.__directAccess = False
         self.__deviceVolumeName = ""
--- a/eric6/MicroPython/MicroPythonCommandsInterface.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/MicroPythonCommandsInterface.py	Sat Feb 20 11:52:49 2021 +0100
@@ -747,11 +747,13 @@
             raise OSError(self.__shortError(err))
         return ast.literal_eval(out.decode("utf-8"))
     
-    def syncTime(self):
+    def syncTime(self, deviceType):
         """
         Public method to set the time of the connected device to the local
         computer's time.
         
+        @param deviceType type of board to sync time to
+        @type str
         @exception OSError raised to indicate an issue with the device
         """
         # rtc_time[0] - year    4 digit
@@ -763,83 +765,88 @@
         # rtc_time[6] - second  0..59
         # rtc_time[7] - yearday 1..366
         # rtc_time[8] - isdst   0, 1, or -1
+        if deviceType == "pyboard":
+            # Pyboard (pyboard doesn't have machine.RTC()).
+            # The pyb.RTC.datetime function takes the arguments in the
+            # order: (year, month, day, weekday, hour, minute, second,
+            # subseconds)
+            # http://docs.micropython.org/en/latest/library/pyb.RTC.html
+            # #pyb.RTC.datetime
+            set_time = "\n".join([
+                "def set_time(rtc_time):",
+                "    import pyb",
+                "    rtc = pyb.RTC()",
+                "    rtc.datetime(rtc_time[:7] + (0,))",
+            ])
+        elif deviceType == "esp":
+            # The machine.RTC documentation was incorrect and doesn't agree
+            # with the code, so no link is presented here. The order of the
+            # arguments is the same as the pyboard except for LoBo MPy.
+            set_time = "\n".join([
+                "def set_time(rtc_time):",
+                "    import machine",
+                "    rtc = machine.RTC()",
+                "    try:",                 # ESP8266 may use rtc.datetime()
+                "        rtc.datetime(rtc_time[:7] + (0,))",
+                "    except Exception:",    # ESP32 uses rtc.init()
+                "        import os",
+                "        if 'LoBo' in os.uname()[0]:",      # LoBo MPy
+                "            clock_time = rtc_time[:3] +"
+                " rtc_time[4:7] + (rtc_time[3], rtc_time[7])",
+                "        else:",
+                "            clock_time = rtc_time[:7] + (0,)",
+                "        rtc.init(clock_time)",
+            ])
+        elif deviceType == "circuitpython":
+            set_time = "\n".join([
+                "def set_time(rtc_time):",
+                "    import rtc",
+                "    import time",
+                "    clock = rtc.RTC()",
+                "    clock_time = rtc_time[:3] + rtc_time[4:7] + (rtc_time[3],"
+                " rtc_time[7], rtc_time[8])",
+                "    clock.datetime = time.struct_time(clock_time)",
+            ])
+        elif deviceType in ("bbc_microbit", "calliope"):
+            # BBC micro:bit and Calliope mini don't support time commands
+            return
+        elif deviceType == "rp2040":
+            # Raspberry Pi Pico (RP2040) - machine.RTC doesn't exist
+            set_time = "\n".join([
+                "def set_time(rtc_time):",
+                "    setup_0 = rtc_time[0] << 12 | rtc_time[1] << 8 |"
+                " rtc_time[2]",
+                "    setup_1 = (rtc_time[3] % 7) << 24 | rtc_time[4] << 16 |"
+                " rtc_time[5] << 8 | rtc_time[6]",
+                "    machine.mem32[0x4005c004] = setup_0",
+                "    machine.mem32[0x4005c008] = setup_1",
+                "    machine.mem32[0x4005c00c] |= 0x10",
+            ])
+        elif deviceType == "pycom":
+            # PyCom's machine.RTC takes its arguments in a slightly
+            # different order than the official machine.RTC.
+            # (year, month, day, hour, minute, second[, microsecond[,
+            # tzinfo]])
+            # https://docs.pycom.io/firmwareapi/pycom/machine/rtc/
+            # #rtc-init-datetime-none-source-rtc-internal-rc
+            set_time = "\n".join([
+                "def set_time(rtc_time):",
+                "    import pycom",
+                "    rtc_time2 = rtc_time[:3] + rtc_time[4:7]",
+                "    import machine",
+                "    rtc = machine.RTC()",
+                "    rtc.init(rtc_time2)",
+            ])
+        else:
+            # no set_time() support for generic boards
+            return
+        
         now = time.localtime(time.time())
         commands = [
-            "\n".join([
-                "def set_time(rtc_time):",
-                "    rtc = None",
-                # Pyboard (pyboard doesn't have machine.RTC()).
-                # The pyb.RTC.datetime function takes the arguments in the
-                # order:
-                # (year, month, day, weekday, hour, minute, second, subseconds)
-                # http://docs.micropython.org/en/latest/library/pyb.RTC.html
-                # #pyb.RTC.datetime
-                "    try:",
-                "        import pyb",
-                "        rtc = pyb.RTC()",
-                "        rtc.datetime(rtc_time)",
-                "    except Exception:",
-                "        try:",
-                # PyCom's machine.RTC takes its arguments in a slightly
-                # different order than the official machine.RTC.
-                # (year, month, day, hour, minute, second[, microsecond[,
-                # tzinfo]])
-                # https://docs.pycom.io/firmwareapi/pycom/machine/rtc/
-                # #rtc-init-datetime-none-source-rtc-internal-rc
-                "            import pycom",
-                "            rtc_time2 = (rtc_time[0], rtc_time[1],"
-                " rtc_time[2], rtc_time[4], rtc_time[5], rtc_time[6])",
-                "            import machine",
-                "            rtc = machine.RTC()",
-                "            rtc.init(rtc_time2)",
-                "        except:",
-                "            try:",
-                # The machine.RTC documentation was incorrect and doesn't agree
-                # with the code, so no link is presented here. The order of the
-                # arguments is the same as the pyboard except for LoBo MPy.
-                "                import machine",
-                "                rtc = machine.RTC()",
-                "                try:",      # ESP8266 may use rtc.datetime()
-                "                    rtc.datetime(rtc_time)",
-                "                except Exception:",  # ESP32 uses rtc.init()
-                "                    import os",
-                "                    if 'LoBo' in os.uname()[0]:",  # LoBo MPy
-                "                        clock_time = rtc_time[:3] +"
-                " rtc_time[4:7] + (rtc_time[3], 0)",
-                "                    else:",
-                "                        clock_time = rtc_time",
-                "                    rtc.init(clock_time)",
-                "            except:",
-                "                try:",
-                # Check for CircuitPython devices
-                "                    import rtc",
-                "                    import time",
-                "                    clock = rtc.RTC()",
-                "                    clock_time = rtc_time[:3] + rtc_time[4:7]"
-                " + (rtc_time[3], -1, -1)",
-                "                    clock.datetime = time.struct_time("
-                "clock_time)",
-                "                except:",
-                # Check for the Raspberry Pi Pico - machine.RTC doesn't exist
-                "                    try:",
-                "                        import os",
-                "                        if os.uname().sysname == 'rp2':",
-                "                            setup_0 = rtc_time[0] << 12 |"
-                " rtc_time[1] << 8 | rtc_time[2]",
-                "                            setup_1 = (rtc_time[3] % 7) << 24"
-                " | rtc_time[4] << 16 | rtc_time[5] << 8 | rtc_time[6]",
-                "                            machine.mem32[0x4005c004] ="
-                " setup_0",
-                "                            machine.mem32[0x4005c008] ="
-                " setup_1",
-                "                            machine.mem32[0x4005c00c] |="
-                " 0x10",
-                "                    except:",
-                "                        pass",
-            ]),
+            set_time,
             "set_time({0})".format((
-                now.tm_year, now.tm_mon, now.tm_mday, now.tm_wday + 1, 
-                now.tm_hour, now.tm_min, now.tm_sec, 0
+                now.tm_year, now.tm_mon, now.tm_mday, now.tm_wday + 1,
+                now.tm_hour, now.tm_min, now.tm_sec, now.tm_yday, now.tm_isdst
             )),
             "del set_time",
         ]
--- a/eric6/MicroPython/MicroPythonDevices.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/MicroPythonDevices.py	Sat Feb 20 11:52:49 2021 +0100
@@ -137,6 +137,7 @@
         "port_description": "",
     },
     
+    # TODO: add RP2040 boards (VID: 0x2e8a, PID: 0x0005)
     "generic": {
         # only manually configured devices use this
         "ids": [],
@@ -279,41 +280,55 @@
     """
     if deviceType == "esp":
         from .EspDevices import EspDevice
-        return EspDevice(microPythonWidget)
+        return EspDevice(microPythonWidget, deviceType)
     elif deviceType == "circuitpython":
         from .CircuitPythonDevices import CircuitPythonDevice
-        return CircuitPythonDevice(microPythonWidget)
+        return CircuitPythonDevice(microPythonWidget, deviceType)
     elif deviceType in ("bbc_microbit", "calliope"):
         from .MicrobitDevices import MicrobitDevice
         return MicrobitDevice(microPythonWidget, deviceType)
     elif deviceType == "pyboard":
         from .PyBoardDevices import PyBoardDevice
-        return PyBoardDevice(microPythonWidget)
+        return PyBoardDevice(microPythonWidget, deviceType)
+    # TODO: add RP2040 boards (VID: 0x2e8a, PID: 0x0005)
     elif deviceType == "generic":
         from .GenericMicroPythonDevices import GenericMicroPythonDevice
-        return GenericMicroPythonDevice(microPythonWidget, vid, pid)
+        return GenericMicroPythonDevice(microPythonWidget, deviceType,
+                                        vid, pid)
     else:
         # nothing specific requested
-        return MicroPythonDevice(microPythonWidget)
+        return MicroPythonDevice(microPythonWidget, deviceType)
 
 
 class MicroPythonDevice(QObject):
     """
     Base class for the more specific MicroPython devices.
     """
-    def __init__(self, microPythonWidget, parent=None):
+    def __init__(self, microPythonWidget, deviceType, parent=None):
         """
         Constructor
         
         @param microPythonWidget reference to the main MicroPython widget
         @type MicroPythonWidget
+        @param deviceType device type assigned to this device interface
+        @type str
         @param parent reference to the parent object
         @type QObject
         """
         super(MicroPythonDevice, self).__init__(parent)
         
+        self._deviceType = deviceType
         self.microPython = microPythonWidget
     
+    def getDeviceType(self):
+        """
+        Public method to get the device type.
+        
+        @return type of the device
+        @rtype str
+        """
+        return self._deviceType
+    
     def setButtons(self):
         """
         Public method to enable the supported action buttons.
--- a/eric6/MicroPython/MicroPythonWidget.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/MicroPythonWidget.py	Sat Feb 20 11:52:49 2021 +0100
@@ -1391,20 +1391,21 @@
         @param quiet flag indicating to not show a message
         @type bool
         """
-        try:
-            self.__interface.syncTime()
-            
-            if not quiet:
-                with E5OverridenCursor():
-                    E5MessageBox.information(
-                        self,
-                        self.tr("Synchronize Time"),
-                        self.tr("<p>The time of the connected device was"
-                                " synchronized with the local time.</p>") +
-                        self.__getDeviceTime()
-                    )
-        except Exception as exc:
-            self.__showError("syncTime()", str(exc))
+        if self.__device and self.__device.hasTimeCommands():
+            try:
+                self.__interface.syncTime(self.__device.getDeviceType())
+                
+                if not quiet:
+                    with E5OverridenCursor():
+                        E5MessageBox.information(
+                            self,
+                            self.tr("Synchronize Time"),
+                            self.tr("<p>The time of the connected device was"
+                                    " synchronized with the local time.</p>") +
+                            self.__getDeviceTime()
+                        )
+            except Exception as exc:
+                self.__showError("syncTime()", str(exc))
     
     def __getDeviceTime(self):
         """
@@ -1414,24 +1415,27 @@
         @return date and time of the connected device
         @rtype str
         """
-        try:
-            dateTimeString = self.__interface.getTime()
+        if self.__device and self.__device.hasTimeCommands():
             try:
-                date, time = dateTimeString.strip().split(None, 1)
-                return self.tr(
-                    "<h3>Device Date and Time</h3>"
-                    "<table>"
-                    "<tr><td><b>Date</b></td><td>{0}</td></tr>"
-                    "<tr><td><b>Time</b></td><td>{1}</td></tr>"
-                    "</table>"
-                ).format(date, time)
-            except ValueError:
-                return self.tr(
-                    "<h3>Device Date and Time</h3>"
-                    "<p>{0}</p>"
-                ).format(dateTimeString.strip())
-        except Exception as exc:
-            self.__showError("getTime()", str(exc))
+                dateTimeString = self.__interface.getTime()
+                try:
+                    date, time = dateTimeString.strip().split(None, 1)
+                    return self.tr(
+                        "<h3>Device Date and Time</h3>"
+                        "<table>"
+                        "<tr><td><b>Date</b></td><td>{0}</td></tr>"
+                        "<tr><td><b>Time</b></td><td>{1}</td></tr>"
+                        "</table>"
+                    ).format(date, time)
+                except ValueError:
+                    return self.tr(
+                        "<h3>Device Date and Time</h3>"
+                        "<p>{0}</p>"
+                    ).format(dateTimeString.strip())
+            except Exception as exc:
+                self.__showError("getTime()", str(exc))
+                return ""
+        else:
             return ""
     
     @pyqtSlot()
--- a/eric6/MicroPython/MicrobitDevices.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/MicrobitDevices.py	Sat Feb 20 11:52:49 2021 +0100
@@ -39,9 +39,8 @@
         @param parent reference to the parent object
         @type QObject
         """
-        super(MicrobitDevice, self).__init__(microPythonWidget, parent)
-        
-        self.__deviceType = deviceType
+        super(MicrobitDevice, self).__init__(
+            microPythonWidget, deviceType, parent)
     
     def setButtons(self):
         """
@@ -68,7 +67,7 @@
         @return name of the device
         @rtype str
         """
-        if self.__deviceType == "bbc_microbit":
+        if self.getDeviceType() == "bbc_microbit":
             # BBC micro:bit
             return self.tr("BBC micro:bit")
         else:
@@ -187,7 +186,7 @@
         # Attempts to find the path on the file system that represents the
         # plugged in micro:bit board. To flash the DAPLink firmware, it must be
         # in maintenance mode, for MicroPython in standard mode.
-        if self.__deviceType == "bbc_microbit":
+        if self.getDeviceType() == "bbc_microbit":
             # BBC micro:bit
             if firmware:
                 deviceDirectories = Utilities.findVolume("MAINTENANCE",
@@ -204,7 +203,7 @@
                 deviceDirectories = Utilities.findVolume("MINI",
                                                          findAll=True)
         if len(deviceDirectories) == 0:
-            if self.__deviceType == "bbc_microbit":
+            if self.getDeviceType() == "bbc_microbit":
                 # BBC micro:bit is not ready or not mounted
                 if firmware:
                     E5MessageBox.critical(
@@ -366,7 +365,7 @@
         """
         Private slot to reset the connected device.
         """
-        if self.__deviceType == "bbc_microbit":
+        if self.getDeviceType() == "bbc_microbit":
             # BBC micro:bit
             self.microPython.commandsInterface().execute([
                 "import microbit",
@@ -386,7 +385,7 @@
         @return documentation URL of the device
         @rtype str
         """
-        if self.__deviceType == "bbc_microbit":
+        if self.getDeviceType() == "bbc_microbit":
             # BBC micro:bit
             return Preferences.getMicroPython("MicrobitDocuUrl")
         else:
@@ -401,7 +400,7 @@
             entry
         @rtype list of tuple of (str, str)
         """
-        if self.__deviceType == "bbc_microbit":
+        if self.getDeviceType() == "bbc_microbit":
             return [
                 (self.tr("MicroPython Firmware for BBC micro:bit V1"),
                  Preferences.getMicroPython("MicrobitMicroPythonUrl")),
--- a/eric6/MicroPython/PyBoardDevices.py	Fri Feb 19 17:34:33 2021 +0100
+++ b/eric6/MicroPython/PyBoardDevices.py	Sat Feb 20 11:52:49 2021 +0100
@@ -33,16 +33,19 @@
         "Pyboard-Firmware-Update"
     )
     
-    def __init__(self, microPythonWidget, parent=None):
+    def __init__(self, microPythonWidget, deviceType, parent=None):
         """
         Constructor
         
         @param microPythonWidget reference to the main MicroPython widget
         @type MicroPythonWidget
+        @param deviceType device type assigned to this device interface
+        @type str
         @param parent reference to the parent object
         @type QObject
         """
-        super(PyBoardDevice, self).__init__(microPythonWidget, parent)
+        super(PyBoardDevice, self).__init__(microPythonWidget, deviceType,
+                                            parent)
         
         self.__workspace = self.__findWorkspace()
     
@@ -225,6 +228,9 @@
         """
         connected = self.microPython.isConnected()
         
+        act = menu.addAction(self.tr("Activate Bootloader"),
+                             self.__activateBootloader)
+        act.setEnabled(connected)
         act = menu.addAction(self.tr("List DFU-capable Devices"),
                              self.__listDfuCapableDevices)
         act.setEnabled(not connected)
@@ -396,3 +402,16 @@
                     if res:
                         dlg.exec()
                         self.__showDfuDisableInstructions()
+    
+    @pyqtSlot()
+    def __activateBootloader(self):
+        """
+        Private slot to activate the bootloader and disconnect.
+        """
+        if self.microPython.isConnected():
+            self.microPython.commandsInterface().execute([
+                "import pyb",
+                "pyb.bootloader()",
+            ])
+            # simulate pressing the disconnect button
+            self.microPython.on_connectButton_clicked()

eric ide

mercurial