diff --git a/internal_filesystem/lib/mpos/board/pinstates.py b/internal_filesystem/lib/mpos/board/pinstates.py new file mode 100644 index 00000000..0ca1ff99 --- /dev/null +++ b/internal_filesystem/lib/mpos/board/pinstates.py @@ -0,0 +1,139 @@ +import sys +import time + +import machine + + +def _adc_read(adc): + if hasattr(adc, "read_u16"): + return adc.read_u16() + return adc.read() + + +def _pin_snapshot(pin_id): + pin = machine.Pin(pin_id) + snapshot = {"pin": pin, "mode": None, "pull": None, "value": None} + for attr in ("mode", "pull"): + getter = getattr(pin, attr, None) + if callable(getter): + try: + snapshot[attr] = getter() + except Exception: + pass + try: + snapshot["value"] = pin.value() + except Exception: + pass + return snapshot + + +def _try_pin_snapshot(pin_id): + try: + return _pin_snapshot(pin_id), None + except Exception as exc: + return None, exc + + +def _restore_pin(snapshot): + pin = snapshot["pin"] + mode = snapshot.get("mode") + pull = snapshot.get("pull") + value = snapshot.get("value") + + try: + if hasattr(pin, "init"): + kwargs = {} + if mode is not None: + kwargs["mode"] = mode + if pull is not None: + kwargs["pull"] = pull + if value is not None and mode in (machine.Pin.OUT, getattr(machine.Pin, "OPEN_DRAIN", None)): + kwargs["value"] = value + if kwargs: + pin.init(**kwargs) + return + if value is not None and mode in (machine.Pin.OUT, getattr(machine.Pin, "OPEN_DRAIN", None)): + pin.value(value) + except Exception as exc: + print("pinstates: WARNING: failed to restore GPIO%02d: %r" % (pin.id(), exc)) + + +def _detect_board(): + impl = [repr(sys.implementation)] + impl.append(getattr(sys.implementation, "_machine", "")) + impl.append(getattr(sys.implementation, "machine", "")) + haystack = " ".join(impl).upper() + if "ESP32S3" in haystack: + return "esp32s3" + return "esp32" + + +def _candidate_pins(board, skiplist=None): + extra_skip = set(skiplist or []) + if board in ("esp32", "esp32-wroom", "esp32-wrover"): + skip = {6, 7, 8, 9, 10, 11, 20, 24, 28, 29, 30, 31} + return [p for p in range(0, 40) if p not in skip and p not in extra_skip] + if board in ("esp32s3", "esp32-s3"): + skip = {22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 45, 46} + return [p for p in range(0, 49) if p not in skip and p not in extra_skip] + raise ValueError("Unsupported board type: %r" % board) + + +def read_all_pins(skiplist=None): + board = _detect_board() + pins = _candidate_pins(board, skiplist=skiplist) + results = {"digital": {}, "analog": {}, "errors": {"digital": {}, "analog": {}}} + + for p in pins: + pin_snapshot, snapshot_error = _try_pin_snapshot(p) + if snapshot_error is not None: + results["errors"]["digital"][p] = repr(snapshot_error) + continue + try: + print("Reading digital GPIO%02d..." % p) + pin = machine.Pin(p, machine.Pin.IN) + results["digital"][p] = pin.value() + #time.sleep(1) + except Exception as exc: + results["errors"]["digital"][p] = repr(exc) + finally: + try: + _restore_pin(pin_snapshot) + except Exception as exc: + results["errors"]["digital"][p] = repr(exc) + + for p in pins: + pin_snapshot, snapshot_error = _try_pin_snapshot(p) + if snapshot_error is not None: + results["errors"]["analog"][p] = repr(snapshot_error) + continue + try: + print("Reading analog GPIO%02d..." % p) + adc = machine.ADC(machine.Pin(p)) + results["analog"][p] = _adc_read(adc) + #time.sleep(1) + except Exception as exc: + results["errors"]["analog"][p] = repr(exc) + finally: + try: + _restore_pin(pin_snapshot) + except Exception as exc: + results["errors"]["analog"][p] = repr(exc) + + print("=== Pin State Readout ===") + print("Board:", board) + print("=== Digital Reads ===") + for p in pins: + if p in results["digital"]: + print("GPIO%02d:" % p, results["digital"][p]) + else: + print("GPIO%02d:" % p, "ERR", results["errors"]["digital"].get(p)) + + print("=== Analog Reads ===") + for p in pins: + if p in results["analog"]: + print("GPIO%02d:" % p, results["analog"][p]) + else: + print("GPIO%02d:" % p, "ERR", results["errors"]["analog"].get(p)) + + return results diff --git a/internal_filesystem/lib/mpos/main.py b/internal_filesystem/lib/mpos/main.py index 6178bc84..7174909e 100644 --- a/internal_filesystem/lib/mpos/main.py +++ b/internal_filesystem/lib/mpos/main.py @@ -1,6 +1,8 @@ import _thread import lvgl as lv +from machine import Pin + import mpos.ui import mpos.ui.topmenu @@ -55,32 +57,21 @@ def single_address_i2c_scan(i2c_bus, address): def fail_save_i2c(sda, scl): - from machine import I2C, Pin + from machine import I2C print(f"Try to I2C initialized on {sda=} {scl=}") try: i2c0 = I2C(0, sda=Pin(sda), scl=Pin(scl)) except Exception as e: - print(f"Failed: {e}") + print(f"fail_save_i2c failed: {e}") return None else: - print("OK") + print("fail_save_i2c ok") return i2c0 - -def check_pins(*pins): - from machine import Pin - - print(f"Test {pins=}...") - for pin in pins: - try: - Pin(pin) - except Exception as e: - print(f"Failed to initialize {pin=}: {e}") - return True - print("All pins initialized successfully") - return True - +def restore_i2c(sda, scl): + Pin(sda, Pin.IN, pull=None) + Pin(scl, Pin.IN, pull=None) def detect_board(): import sys @@ -88,6 +79,18 @@ def detect_board(): return "linux" elif sys.platform == "esp32": + ''' + # Reading and storing all pinstates can be useful for board detection + # But reading some pins can break peripherals + # So it's disabled by default - it's more for development + try: + import mpos + from mpos.board import pinstates + mpos.pinstates = pinstates.read_all_pins(skiplist = [7,8]) + except Exception as e: + print("pinstates: WARNING: failed to read pins:", e) + ''' + # First do unique_id-based board detections because they're fast and don't mess with actual hardware configurations import machine unique_id_prefixes = machine.unique_id()[0:3] @@ -96,10 +99,6 @@ def detect_board(): if unique_id_prefixes == b'\x30\x30\xf9': return "unphone" - print("(emulated) lilygo_t_display_s3 ?") - if unique_id_prefixes == b'\x10\x01\x00' or unique_id_prefixes == b'\xc0\x4e\x30': - return "lilygo_t_display_s3" # display gets confused by the i2c stuff below - print("odroid_go ?") if unique_id_prefixes == b'\x30\xae\xa4': return "odroid_go" @@ -109,37 +108,53 @@ def detect_board(): # or: if single_address_i2c_scan(i2c0, 0x6A): # IMU currently not installed on prototype board return "fri3d_2026" + # Do I2C-based board detection + print("lilygo_t_watch_s3_plus ?") if i2c0 := fail_save_i2c(sda=10, scl=11): if single_address_i2c_scan(i2c0, 0x19): # IMU on 0x19, vibrator on 0x5A and scan also shows: [52, 81] return "lilygo_t_watch_s3_plus" # example MAC address: D0:CF:13:33:36:306 + restore_i2c(sda=10, scl=11) - # Then do I2C-based board detection print("matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 ?") if i2c0 := fail_save_i2c(sda=39, scl=38): if single_address_i2c_scan(i2c0, 0x14) or single_address_i2c_scan(i2c0, 0x5D): # "ghost" or real GT911 touch screen return "matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660" + restore_i2c(sda=39, scl=38) # fix pin 39 (data0) breaking lilygo_t_display_s3's display print("waveshare_esp32_s3_touch_lcd_2 ?") if i2c0 := fail_save_i2c(sda=48, scl=47): # IO48 is floating on matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 and therefore, using that for I2C will find many devices, so do this after matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 if single_address_i2c_scan(i2c0, 0x15) and single_address_i2c_scan(i2c0, 0x6B): # CST816S touch screen and IMU return "waveshare_esp32_s3_touch_lcd_2" + restore_i2c(sda=48, scl=47) # fix pin 47 (data6) and 48 (data7) breaking lilygo_t_display_s3's display print("m5stack_fire ?") if i2c0 := fail_save_i2c(sda=21, scl=22): if single_address_i2c_scan(i2c0, 0x68): # IMU (MPU6886) return "m5stack_fire" + restore_i2c(sda=21, scl=22) print("fri3d_2024 ?") if i2c0 := fail_save_i2c(sda=9, scl=18): if single_address_i2c_scan(i2c0, 0x6B): # IMU (plus possibly the Communicator's LANA TNY at 0x38) return "fri3d_2024" + restore_i2c(sda=9, scl=18) + # On devices without I2C, we use known GPIO states + + print("(emulated) lilygo_t_display_s3 ?") + try: + # 2 buttons have PCB pull-ups so they'll be high unless pressed + pin0 = Pin(0, Pin.IN) + pin14 = Pin(14, Pin.IN) + if pin0.value() == 1 and pin14.value() == 1: + return "lilygo_t_display_s3" # display gets confused by the i2c stuff below + except Exception as e: + print(f"lilygo_t_display_s3 detection got exception: {e}") print("Unknown board: couldn't detect known I2C devices or unique_id prefix") - # EXECUTION STARTS HERE print(f"MicroPythonOS {BuildInfo.version.release} running lib/mpos/main.py")