--- a/src/eric7/MicroPython/Devices/RP2040Devices.py Sat Mar 11 15:28:47 2023 +0100 +++ b/src/eric7/MicroPython/Devices/RP2040Devices.py Sun Mar 12 14:56:04 2023 +0100 @@ -22,6 +22,7 @@ from eric7.EricWidgets import EricMessageBox from eric7.EricWidgets.EricApplication import ericApp +from ..EthernetDialogs import WiznetUtilities from ..MicroPythonWidget import HAS_QTCHART from . import FirmwareGithubUrls from .DeviceBase import BaseDevice @@ -69,6 +70,11 @@ 8: self.tr("AP connected"), 9: self.tr("AP failed"), }, + "picowiz": { + 0: self.tr("switched off"), + 1: self.tr("switched on, inactive"), + 2: self.tr("switched on, active"), + }, } self.__securityTranslations = { @@ -1175,6 +1181,292 @@ return clientsList, "" ################################################################## + ## Methods below implement Ethernet related methods + ################################################################## + + def hasEthernet(self): + """ + Public method to check the availability of Ethernet. + + @return flag indicating the availability of Ethernet + @rtype bool + @exception OSError raised to indicate an issue with the device + """ + command = """ +def has_eth(): + try: + import network + if hasattr(network, 'WIZNET5K'): + return True, 'picowiz' + except ImportError: + pass + + return False, '' + +print(has_eth()) +del has_eth +""" + + out, err = self._interface.execute( + command, mode=self._submitMode, timeout=10000 + ) + if err: + if not err.startswith(b"Timeout "): + raise OSError(self._shortError(err)) + else: + return False # pimoroni firmware loaded but no pico wireless present + return ast.literal_eval(out.decode("utf-8")) + + def getEthernetStatus(self): + """ + Public method to get Ethernet status data of the connected board. + + @return list of tuples containing the translated status data label and + the associated value + @rtype list of tuples of (str, str) + @exception OSError raised to indicate an issue with the device + """ + command = """{0} +def ethernet_status(): + import network + import ubinascii + import ujson + + w5x00_init() + + res = {{ + 'active': nic.active(), + 'connected': nic.isconnected(), + 'status': nic.status(), + 'ifconfig': nic.ifconfig(), + 'mac': ubinascii.hexlify(nic.config('mac'), ':').decode(), + 'hostname': network.hostname(), + }} + print(ujson.dumps(res)) + +ethernet_status() +del ethernet_status, w5x00_init +""".format( + WiznetUtilities.wiznetInit() + ) + + out, err = self._interface.execute(command, mode=self._submitMode) + if err: + raise OSError(self._shortError(err)) + + status = [] + ethStatus = json.loads(out.decode("utf-8")) + status.append((self.tr("Active"), self.bool2str(ethStatus["active"]))) + status.append((self.tr("Connected"), self.bool2str(ethStatus["connected"]))) + status.append( + ( + self.tr("Status"), + self.__statusTranslations["picowiz"][ethStatus["status"]], + ) + ) + status.append((self.tr("Hostname"), ethStatus["hostname"])) + status.append((self.tr("IPv4 Address"), ethStatus["ifconfig"][0])) + status.append((self.tr("Netmask"), ethStatus["ifconfig"][1])) + status.append((self.tr("Gateway"), ethStatus["ifconfig"][2])) + status.append((self.tr("DNS"), ethStatus["ifconfig"][3])) + status.append((self.tr("MAC-Address"), ethStatus["mac"])) + + return status + + def connectToLan(self, config): + """ + Public method to connect the connected device to the LAN. + + @param config configuration for the connection (either the string 'dhcp' + for a dynamic address or a tuple of four strings with the IPv4 parameters. + @type str or tuple of (str, str, str, str) + @return tuple containing a flag indicating success and an error message + @rtype tuple of (bool, str) + """ + command = """{0} +def connect_lan(config): + import time + + w5x00_init() + + nic.active(False) + nic.active(True) + nic.ifconfig(config) + max_wait = 140 + while max_wait: + if nic.isconnected(): + break + max_wait -= 1 + time.sleep(0.1) + print(nic.isconnected()) + +connect_lan({1}) +del connect_lan, w5x00_init +""".format( + WiznetUtilities.wiznetInit(), repr(config) if config == "dhcp" else config + ) + + with EricOverrideCursor(): + out, err = self._interface.execute( + command, mode=self._submitMode, timeout=15000 + ) + if err: + return False, err + + return out.strip() == b"True", "" + + def disconnectFromLan(self): + """ + Public method to disconnect from the LAN. + + @return tuple containing a flag indicating success and an error message + @rtype tuple of (bool, str) + """ + command = """{0} +def disconnect_lan(): + import time + + w5x00_init() + + nic.active(False) + time.sleep(0.1) + print(not nic.isconnected()) + +disconnect_lan() +del disconnect_lan, w5x00_init +""".format( + WiznetUtilities.wiznetInit(), + ) + + with EricOverrideCursor(): + out, err = self._interface.execute( + command, mode=self._submitMode, timeout=15000 + ) + if err: + return False, err + + return out.strip() == b"True", "" + + def checkInternetViaLan(self): + """ + Public method to check, if the internet can be reached (LAN variant). + + @return tuple containing a flag indicating reachability and an error string + @rtype tuple of (bool, str) + """ + command = """{0} +def check_internet(): + import network + import socket + + w5x00_init() + + if nic.isconnected(): + s = socket.socket() + try: + s.connect(socket.getaddrinfo('quad9.net', 80)[0][-1]) + s.close() + print(True) + except: + print(False) + else: + print(False) + +check_internet() +del check_internet, w5x00_init +""".format( + WiznetUtilities.wiznetInit(), + ) + + out, err = self._interface.execute( + command, mode=self._submitMode, timeout=10000 + ) + if err: + return False, err + + return out.strip() == b"True", "" + + def deactivateEthernet(self): + """ + Public method to deactivate the Ethernet interface of the connected device. + + @return tuple containg a flag indicating success and an error message + @rtype tuple of (bool, str) + """ + # The WIZnet 5x00 interface cannot be switched off explicitly. That means, + # disconnect from the LAN is all we can do. + + return self.disconnectFromLan() + + def writeLanAutoConnect(self, config): + """ + Public method to generate a script and associated configuration to connect the + device to the LAN during boot time. + + @param config configuration for the connection (either the string 'dhcp' + for a dynamic address or a tuple of four strings with the IPv4 parameters. + @type str or tuple of (str, str, str, str) + @return tuple containing a flag indicating success and an error message + @rtype tuple of (bool, str) + """ + command = """ +def modify_boot(): + add = True + try: + with open('/boot.py', 'r') as f: + for ln in f.readlines(): + if 'wiznet_connect' in ln: + add = False + break + except: + pass + if add: + with open('/boot.py', 'a') as f: + f.write('\\nimport wiznet_connect\\n') + f.write('nic = wiznet_connect.connectLan()\\n') + print(True) + +modify_boot() +del modify_boot +""" + ifconfig = "ifconfig = {0}\n".format("'dhcp'" if config == "dhcp" else config) + try: + # write secrets file + self.putData("/wiznet_config.py", ifconfig.encode("utf-8")) + # copy auto-connect file + self.put( + os.path.join( + os.path.dirname(__file__), "MCUScripts", "picoWiznetConnect.py" + ), + "/wiznet_connect.py", + ) + except OSError as err: + return False, str(err) + + # modify boot.py + out, err = self._interface.execute(command, mode=self._submitMode) + if err: + return False, err + + return out.decode("utf-8").strip() == "True", "" + + def removeLanAutoConnect(self): + """ + Public method to remove the saved IPv4 parameters from the connected device. + + Note: This disables the LAN auto-connect feature. + + @return tuple containing a flag indicating success and an error message + @rtype tuple of (bool, str) + """ + try: + self.rm("/wiznet_config.py") + except OSError as err: + return False, str(err) + + return True, "" + + ################################################################## ## Methods below implement NTP related methods ################################################################## @@ -1224,8 +1516,14 @@ import ntptime import machine - if not network.WLAN(network.STA_IF).isconnected(): + if hasattr(network, 'WLAN') and not network.WLAN(network.STA_IF).isconnected(): return False + elif hasattr(network, 'WIZNET5K'): + try: + if not nic.isconnected(): + return False + except NameError: + return False ntptime.host = server ntptime.timeout = timeout