25 from eric7.SystemUtilities import PythonUtilities |
25 from eric7.SystemUtilities import PythonUtilities |
26 |
26 |
27 from ..MicroPythonWidget import HAS_QTCHART |
27 from ..MicroPythonWidget import HAS_QTCHART |
28 from . import FirmwareGithubUrls |
28 from . import FirmwareGithubUrls |
29 from .DeviceBase import BaseDevice |
29 from .DeviceBase import BaseDevice |
|
30 from .CircuitPythonDevices import CircuitPythonDevice |
30 |
31 |
31 |
32 |
32 class EspDevice(BaseDevice): |
33 class EspDevice(BaseDevice): |
33 """ |
34 """ |
34 Class implementing the device for ESP32 and ESP8266 based boards. |
35 Class implementing the device for ESP32 and ESP8266 based boards. |
46 @type QObject |
47 @type QObject |
47 """ |
48 """ |
48 super().__init__(microPythonWidget, deviceType, parent) |
49 super().__init__(microPythonWidget, deviceType, parent) |
49 |
50 |
50 self.__createEsp32Submenu() |
51 self.__createEsp32Submenu() |
|
52 |
|
53 self.__cpyDevice = None |
|
54 # needed to delegate some methods to a CircuitPython variant |
51 |
55 |
52 self.__statusTranslations = { |
56 self.__statusTranslations = { |
53 200: self.tr("beacon timeout"), |
57 200: self.tr("beacon timeout"), |
54 201: self.tr("no matching access point found"), |
58 201: self.tr("no matching access point found"), |
55 202: self.tr("authentication failed"), |
59 202: self.tr("authentication failed"), |
67 4: "WPA/WPA2", |
71 4: "WPA/WPA2", |
68 5: "WPA2 (CCMP)", |
72 5: "WPA2 (CCMP)", |
69 6: "WPA3", |
73 6: "WPA3", |
70 7: "WPA2/WPA3", |
74 7: "WPA2/WPA3", |
71 } |
75 } |
|
76 |
|
77 def __createCpyDevice(self): |
|
78 """ |
|
79 Private method to create a CircuitPython device interface. |
|
80 """ |
|
81 if self.hasCircuitPython() and self.__cpyDevice is None: |
|
82 self.__cpyDevice = CircuitPythonDevice( |
|
83 self.microPython, |
|
84 "esp32_circuitpython", |
|
85 "esp32", |
|
86 hasWorkspace=False, |
|
87 parent=self.parent(), |
|
88 ) |
|
89 |
|
90 def setConnected(self, connected): |
|
91 """ |
|
92 Public method to set the connection state. |
|
93 |
|
94 Note: This method can be overwritten to perform actions upon connect |
|
95 or disconnect of the device. |
|
96 |
|
97 @param connected connection state |
|
98 @type bool |
|
99 """ |
|
100 super().setConnected(connected) |
|
101 |
|
102 if self.hasCircuitPython(): |
|
103 self._submitMode = "paste" |
|
104 self.__createCpyDevice() |
72 |
105 |
73 def setButtons(self): |
106 def setButtons(self): |
74 """ |
107 """ |
75 Public method to enable the supported action buttons. |
108 Public method to enable the supported action buttons. |
76 """ |
109 """ |
339 def __showFirmwareVersions(self): |
372 def __showFirmwareVersions(self): |
340 """ |
373 """ |
341 Private slot to show the firmware version of the connected device and the |
374 Private slot to show the firmware version of the connected device and the |
342 available firmware version. |
375 available firmware version. |
343 """ |
376 """ |
|
377 if self.hasCircuitPython(): |
|
378 return self.__cpyDevice.showCircuitPythonVersions() |
|
379 |
344 if self.microPython.isConnected(): |
380 if self.microPython.isConnected(): |
345 if self._deviceData["mpy_name"] == "micropython": |
381 if self._deviceData["mpy_name"] == "micropython": |
346 url = QUrl(FirmwareGithubUrls["micropython"]) |
382 url = QUrl(FirmwareGithubUrls["micropython"]) |
347 elif self._deviceData["mpy_name"] == "circuitpython": |
383 elif self._deviceData["mpy_name"] == "circuitpython": |
348 url = QUrl(FirmwareGithubUrls["circuitpython"]) |
384 url = QUrl(FirmwareGithubUrls["circuitpython"]) |
469 @pyqtSlot() |
505 @pyqtSlot() |
470 def __resetDevice(self): |
506 def __resetDevice(self): |
471 """ |
507 """ |
472 Private slot to reset the connected device. |
508 Private slot to reset the connected device. |
473 """ |
509 """ |
474 if self.microPython.isConnected(): |
510 if self.microPython.isConnected() and not self.hasCircuitPython(): |
475 self.microPython.deviceInterface().execute( |
511 self.microPython.deviceInterface().execute( |
476 "import machine\nmachine.reset()\n", mode=self._submitMode |
512 "import machine\nmachine.reset()\n", mode=self._submitMode |
477 ) |
513 ) |
478 else: |
514 else: |
479 # perform a reset via esptool using flash_id command ignoring |
515 # perform a reset via esptool using flash_id command ignoring |
507 Public method to get the device documentation URL. |
543 Public method to get the device documentation URL. |
508 |
544 |
509 @return documentation URL of the device |
545 @return documentation URL of the device |
510 @rtype str |
546 @rtype str |
511 """ |
547 """ |
|
548 if self.hasCircuitPython(): |
|
549 return self.__cpyDevice.getDocumentationUrl() |
|
550 |
512 return Preferences.getMicroPython("MicroPythonDocuUrl") |
551 return Preferences.getMicroPython("MicroPythonDocuUrl") |
513 |
552 |
514 def getFirmwareUrl(self): |
553 def getFirmwareUrl(self): |
515 """ |
554 """ |
516 Public method to get the device firmware download URL. |
555 Public method to get the device firmware download URL. |
517 |
556 |
518 @return firmware download URL of the device |
557 @return firmware download URL of the device |
519 @rtype str |
558 @rtype str |
520 """ |
559 """ |
|
560 if self.hasCircuitPython(): |
|
561 return self.__cpyDevice.getFirmwareUrl() |
|
562 |
521 return Preferences.getMicroPython("MicroPythonFirmwareUrl") |
563 return Preferences.getMicroPython("MicroPythonFirmwareUrl") |
522 |
564 |
523 ################################################################## |
565 ################################################################## |
524 ## time related methods below |
566 ## time related methods below |
525 ################################################################## |
567 ################################################################## |
549 # (year, month, day, weekday, hour, minute, second, subseconds) |
591 # (year, month, day, weekday, hour, minute, second, subseconds) |
550 # __IGNORE_WARNING_M891__ |
592 # __IGNORE_WARNING_M891__ |
551 # https://docs.micropython.org/en/latest/library/machine.RTC.html#machine-rtc |
593 # https://docs.micropython.org/en/latest/library/machine.RTC.html#machine-rtc |
552 # |
594 # |
553 # LoBo variant of MPy deviates. |
595 # LoBo variant of MPy deviates. |
|
596 if self.hasCircuitPython(): |
|
597 return super()._getSetTimeCode() |
|
598 |
554 return """ |
599 return """ |
555 def set_time(rtc_time): |
600 def set_time(rtc_time): |
556 import machine |
601 import machine |
557 rtc = machine.RTC() |
602 rtc = machine.RTC() |
558 try: |
603 try: |
576 |
621 |
577 @return tuple containing a flag indicating the availability of WiFi |
622 @return tuple containing a flag indicating the availability of WiFi |
578 and the WiFi type (esp32) |
623 and the WiFi type (esp32) |
579 @rtype tuple of (bool, str) |
624 @rtype tuple of (bool, str) |
580 """ |
625 """ |
|
626 if self.hasCircuitPython(): |
|
627 self.__createCpyDevice() |
|
628 return self.__cpyDevice.hasWifi() |
|
629 |
581 return True, "esp32" |
630 return True, "esp32" |
582 |
631 |
583 def getWifiData(self): |
632 def getWifiData(self): |
584 """ |
633 """ |
585 Public method to get data related to the current WiFi status. |
634 Public method to get data related to the current WiFi status. |
587 @return tuple of three dictionaries containing the WiFi status data |
636 @return tuple of three dictionaries containing the WiFi status data |
588 for the WiFi client, access point and overall data |
637 for the WiFi client, access point and overall data |
589 @rtype tuple of (dict, dict, dict) |
638 @rtype tuple of (dict, dict, dict) |
590 @exception OSError raised to indicate an issue with the device |
639 @exception OSError raised to indicate an issue with the device |
591 """ |
640 """ |
|
641 if self.hasCircuitPython(): |
|
642 return self.__cpyDevice.getWifiData() |
|
643 |
592 command = """ |
644 command = """ |
593 def wifi_status(): |
645 def wifi_status(): |
594 import ubinascii |
646 import ubinascii |
595 import ujson |
647 import ujson |
596 import network |
648 import network |
663 @param password password needed to connect |
715 @param password password needed to connect |
664 @type str |
716 @type str |
665 @return tuple containing the connection status and an error string |
717 @return tuple containing the connection status and an error string |
666 @rtype tuple of (bool, str) |
718 @rtype tuple of (bool, str) |
667 """ |
719 """ |
|
720 if self.hasCircuitPython(): |
|
721 return self.__cpyDevice.connectWifi(ssid, password) |
|
722 |
668 command = """ |
723 command = """ |
669 def connect_wifi(ssid, password): |
724 def connect_wifi(ssid, password): |
670 import network |
725 import network |
671 import ujson |
726 import ujson |
672 from time import sleep |
727 from time import sleep |
712 Public method to disconnect a device from the WiFi network. |
767 Public method to disconnect a device from the WiFi network. |
713 |
768 |
714 @return tuple containing a flag indicating success and an error string |
769 @return tuple containing a flag indicating success and an error string |
715 @rtype tuple of (bool, str) |
770 @rtype tuple of (bool, str) |
716 """ |
771 """ |
|
772 if self.hasCircuitPython(): |
|
773 return self.__cpyDevice.disconnectWifi() |
|
774 |
717 command = """ |
775 command = """ |
718 def disconnect_wifi(): |
776 def disconnect_wifi(): |
719 import network |
777 import network |
720 from time import sleep |
778 from time import sleep |
721 |
779 |
745 @param password password needed to authenticate |
803 @param password password needed to authenticate |
746 @type str |
804 @type str |
747 @return tuple containing a flag indicating success and an error message |
805 @return tuple containing a flag indicating success and an error message |
748 @rtype tuple of (bool, str) |
806 @rtype tuple of (bool, str) |
749 """ |
807 """ |
|
808 if self.hasCircuitPython(): |
|
809 return self.__cpyDevice.writeCredentials(ssid, password) |
|
810 |
750 nvsCommand = """ |
811 nvsCommand = """ |
751 def save_wifi_creds(ssid, password): |
812 def save_wifi_creds(ssid, password): |
752 import esp32 |
813 import esp32 |
753 |
814 |
754 nvs = esp32.NVS('wifi_creds') |
815 nvs = esp32.NVS('wifi_creds') |
807 Public method to remove the saved credentials from the connected device. |
868 Public method to remove the saved credentials from the connected device. |
808 |
869 |
809 @return tuple containing a flag indicating success and an error message |
870 @return tuple containing a flag indicating success and an error message |
810 @rtype tuple of (bool, str) |
871 @rtype tuple of (bool, str) |
811 """ |
872 """ |
|
873 if self.hasCircuitPython(): |
|
874 return self.__cpyDevice.removeCredentials() |
|
875 |
812 nvsCommand = """ |
876 nvsCommand = """ |
813 def delete_wifi_creds(): |
877 def delete_wifi_creds(): |
814 import esp32 |
878 import esp32 |
815 |
879 |
816 nvs = esp32.NVS('wifi_creds') |
880 nvs = esp32.NVS('wifi_creds') |
836 Public method to check, if the internet can be reached. |
900 Public method to check, if the internet can be reached. |
837 |
901 |
838 @return tuple containing a flag indicating reachability and an error string |
902 @return tuple containing a flag indicating reachability and an error string |
839 @rtype tuple of (bool, str) |
903 @rtype tuple of (bool, str) |
840 """ |
904 """ |
|
905 if self.hasCircuitPython(): |
|
906 return self.__cpyDevice.checkInternet() |
|
907 |
841 command = """ |
908 command = """ |
842 def check_internet(): |
909 def check_internet(): |
843 import network |
910 import network |
844 import socket |
911 import socket |
845 |
912 |
871 |
938 |
872 @return tuple containing the list of available networks as a tuple of 'Name', |
939 @return tuple containing the list of available networks as a tuple of 'Name', |
873 'MAC-Address', 'channel', 'RSSI' and 'security' and an error string |
940 'MAC-Address', 'channel', 'RSSI' and 'security' and an error string |
874 @rtype tuple of (list of tuple of (str, str, int, int, str), str) |
941 @rtype tuple of (list of tuple of (str, str, int, int, str), str) |
875 """ |
942 """ |
|
943 if self.hasCircuitPython(): |
|
944 return self.__cpyDevice.scanNetworks() |
|
945 |
876 command = """ |
946 command = """ |
877 def scan_networks(): |
947 def scan_networks(): |
878 import network |
948 import network |
879 |
949 |
880 wifi = network.WLAN(network.STA_IF) |
950 wifi = network.WLAN(network.STA_IF) |
926 if interface not in ("STA", "AP"): |
996 if interface not in ("STA", "AP"): |
927 raise ValueError( |
997 raise ValueError( |
928 "interface must be 'AP' or 'STA', got '{0}'".format(interface) |
998 "interface must be 'AP' or 'STA', got '{0}'".format(interface) |
929 ) |
999 ) |
930 |
1000 |
|
1001 if self.hasCircuitPython(): |
|
1002 return self.__cpyDevice.deactivateInterface(interface) |
|
1003 |
931 command = """ |
1004 command = """ |
932 def deactivate(): |
1005 def deactivate(): |
933 import network |
1006 import network |
934 from time import sleep |
1007 from time import sleep |
935 |
1008 |
964 (IPv4 address, netmask, gateway address, DNS server address) |
1037 (IPv4 address, netmask, gateway address, DNS server address) |
965 @type tuple of (str, str, str, str) |
1038 @type tuple of (str, str, str, str) |
966 @return tuple containing a flag indicating success and an error message |
1039 @return tuple containing a flag indicating success and an error message |
967 @rtype tuple of (bool, str) |
1040 @rtype tuple of (bool, str) |
968 """ |
1041 """ |
|
1042 if self.hasCircuitPython(): |
|
1043 return self.__cpyDevice.startAccessPoint( |
|
1044 ssid, security=security, password=password, ifconfig=ifconfig |
|
1045 ) |
|
1046 |
969 if security is None or password is None: |
1047 if security is None or password is None: |
970 security = 0 |
1048 security = 0 |
971 password = "" |
1049 password = "" |
972 if security > 4: |
1050 if security > 4: |
973 security = 4 # security >4 cause an error thrown by the ESP32 |
1051 security = 4 # security >4 cause an error thrown by the ESP32 |
1005 Public method to stop the access point interface. |
1083 Public method to stop the access point interface. |
1006 |
1084 |
1007 @return tuple containg a flag indicating success and an error message |
1085 @return tuple containg a flag indicating success and an error message |
1008 @rtype tuple of (bool, str) |
1086 @rtype tuple of (bool, str) |
1009 """ |
1087 """ |
|
1088 if self.hasCircuitPython(): |
|
1089 return self.__cpyDevice.stopAccessPoint() |
|
1090 |
1010 return self.deactivateInterface("AP") |
1091 return self.deactivateInterface("AP") |
1011 |
1092 |
1012 def getConnectedClients(self): |
1093 def getConnectedClients(self): |
1013 """ |
1094 """ |
1014 Public method to get a list of connected clients. |
1095 Public method to get a list of connected clients. |
1015 |
1096 |
1016 @return a tuple containing a list of tuples containing the client MAC-Address |
1097 @return a tuple containing a list of tuples containing the client MAC-Address |
1017 and the RSSI (if supported and available) and an error message |
1098 and the RSSI (if supported and available) and an error message |
1018 @rtype tuple of ([(bytes, int)], str) |
1099 @rtype tuple of ([(bytes, int)], str) |
1019 """ |
1100 """ |
|
1101 if self.hasCircuitPython(): |
|
1102 return self.__cpyDevice.getConnectedClients() |
|
1103 |
1020 command = """ |
1104 command = """ |
1021 def get_stations(): |
1105 def get_stations(): |
1022 import network |
1106 import network |
1023 |
1107 |
1024 ap = network.WLAN(network.AP_IF) |
1108 ap = network.WLAN(network.AP_IF) |
1048 |
1132 |
1049 @return flag indicating the availability of Bluetooth |
1133 @return flag indicating the availability of Bluetooth |
1050 @rtype bool |
1134 @rtype bool |
1051 @exception OSError raised to indicate an issue with the device |
1135 @exception OSError raised to indicate an issue with the device |
1052 """ |
1136 """ |
|
1137 if self.hasCircuitPython(): |
|
1138 self.__createCpyDevice() |
|
1139 return self.__cpyDevice.hasBluetooth() |
|
1140 |
1053 command = """ |
1141 command = """ |
1054 def has_bt(): |
1142 def has_bt(): |
1055 try: |
1143 try: |
1056 import bluetooth |
1144 import bluetooth |
1057 if hasattr(bluetooth, 'BLE'): |
1145 if hasattr(bluetooth, 'BLE'): |
1078 @return list of tuples containing the translated status data label and |
1166 @return list of tuples containing the translated status data label and |
1079 the associated value |
1167 the associated value |
1080 @rtype list of tuples of (str, str) |
1168 @rtype list of tuples of (str, str) |
1081 @exception OSError raised to indicate an issue with the device |
1169 @exception OSError raised to indicate an issue with the device |
1082 """ |
1170 """ |
|
1171 if self.hasCircuitPython(): |
|
1172 return self.__cpyDevice.getBluetoothStatus() |
|
1173 |
1083 command = """ |
1174 command = """ |
1084 def ble_status(): |
1175 def ble_status(): |
1085 import bluetooth |
1176 import bluetooth |
1086 import ubinascii |
1177 import ubinascii |
1087 import ujson |
1178 import ujson |
1163 |
1257 |
1164 @return flag indicating the new state of the Bluetooth interface |
1258 @return flag indicating the new state of the Bluetooth interface |
1165 @rtype bool |
1259 @rtype bool |
1166 @exception OSError raised to indicate an issue with the device |
1260 @exception OSError raised to indicate an issue with the device |
1167 """ |
1261 """ |
|
1262 if self.hasCircuitPython(): |
|
1263 return self.__cpyDevice.deactivateBluetoothInterface() |
|
1264 |
1168 command = """ |
1265 command = """ |
1169 def deactivate_ble(): |
1266 def deactivate_ble(): |
1170 import bluetooth |
1267 import bluetooth |
1171 |
1268 |
1172 ble = bluetooth.BLE() |
1269 ble = bluetooth.BLE() |