from machine import Pin, UART import struct import time # Wiring: # IMU TX -> ESP32 GPIO16 # IMU RX -> ESP32 GPIO17 # IMU GND -> ESP32 GND IMU_RX_PIN = 16 IMU_TX_PIN = 17 # CP2102 USB serial. Binary output is used because JSON is too large at 200 Hz. USB_BAUD = 230400 DIN_PINS = (25, 26, 27, 32) digital_inputs = [Pin(pin, Pin.IN, Pin.PULL_UP) for pin in DIN_PINS] # Two controllable digital outputs. Both default to LOW at boot. DOUT_PINS = (18, 19) digital_outputs = [Pin(pin, Pin.OUT, value=0) for pin in DOUT_PINS] FRAME_LEN = 11 HEADER = 0x55 # WIT normal-protocol frame types TYPE_ACC = 0x51 TYPE_GYRO = 0x52 TYPE_ANGLE = 0x53 # Latest raw values: acc XYZ, gyro XYZ, angle XYZ, temperature raw_values = [0] * 10 rx_buffer = bytearray() command_buffer = bytearray() def write_register(uart, address, value): uart.write(bytes(( 0xFF, 0xAA, address & 0xFF, value & 0xFF, (value >> 8) & 0xFF, ))) def has_valid_frame(uart, timeout_ms=400): test_buffer = bytearray() deadline = time.ticks_add(time.ticks_ms(), timeout_ms) while time.ticks_diff(deadline, time.ticks_ms()) > 0: count = uart.any() if count: chunk = uart.read(count) if chunk: test_buffer.extend(chunk) while len(test_buffer) >= FRAME_LEN: if test_buffer[0] != HEADER: del test_buffer[0] continue frame = test_buffer[:FRAME_LEN] if (sum(frame[:10]) & 0xFF) == frame[10]: return True del test_buffer[0] time.sleep_ms(1) return False def open_imu_uart(baudrate): return UART( 2, baudrate=baudrate, bits=8, parity=None, stop=1, rx=IMU_RX_PIN, tx=IMU_TX_PIN, timeout=0, rxbuf=2048, ) def start_imu_at_200hz(): # If data already arrives at 115200, enforce 200 Hz for this session. # No SAVE is needed here, avoiding repeated writes to sensor flash. uart = open_imu_uart(115200) if has_valid_frame(uart): write_register(uart, 0x69, 0xB588) time.sleep_ms(200) write_register(uart, 0x02, 0x000E) # ACC + GYRO + ANGLE only time.sleep_ms(50) write_register(uart, 0x03, 0x000B) # RRATE = 200 Hz time.sleep_ms(100) return uart # Factory/default examples use 9600 baud. uart.deinit() uart = open_imu_uart(9600) if not has_valid_frame(uart): raise RuntimeError("IMU not detected at 9600 or 115200 baud") # Official protocol sequence: # unlock -> change baud -> switch local UART -> unlock -> set 200 Hz -> save write_register(uart, 0x69, 0xB588) time.sleep_ms(200) write_register(uart, 0x04, 0x0006) # BAUD = 115200 time.sleep_ms(100) uart.deinit() uart = open_imu_uart(115200) time.sleep_ms(200) write_register(uart, 0x69, 0xB588) time.sleep_ms(200) write_register(uart, 0x02, 0x000E) # ACC + GYRO + ANGLE only time.sleep_ms(50) write_register(uart, 0x03, 0x000B) # RRATE = 200 Hz time.sleep_ms(100) write_register(uart, 0x00, 0x0000) # SAVE time.sleep_ms(500) return uart def update_raw_values(frame): frame_type = frame[1] x, y, z, extra = struct.unpack_from("= FRAME_LEN: if rx_buffer[0] != HEADER: del rx_buffer[0] continue frame = rx_buffer[:FRAME_LEN] if (sum(frame[:10]) & 0xFF) != frame[10]: del rx_buffer[0] continue del rx_buffer[:FRAME_LEN] if update_raw_values(frame): angle_updated = True return angle_updated def make_packet(): din_mask = 0 for index, pin in enumerate(digital_inputs): din_mask |= pin.value() << index dout_mask = 0 for index, pin in enumerate(digital_outputs): dout_mask |= pin.value() << index # Packet layout: # A5 5A | version | DIN | DOUT | ticks_ms | 10 signed int16 | checksum packet = struct.pack( "<2sBBBI10h", b"\xA5\x5A", 2, din_mask, dout_mask, time.ticks_ms() & 0xFFFFFFFF, *raw_values ) return packet + bytes((sum(packet) & 0xFF,)) def set_gripper(mode): # GPIO HIGH means the external transistor/optocoupler is conducting. # Always release both inputs briefly before changing direction. digital_outputs[0].value(0) digital_outputs[1].value(0) time.sleep_ms(20) if mode == 1: # OPEN: line 2 = 12 V, line 3 = 0 V digital_outputs[1].value(1) elif mode == 2: # CLOSE: line 2 = 0 V, line 3 = 12 V digital_outputs[0].value(1) def poll_usb_commands(uart): count = uart.any() if count: chunk = uart.read(count) if chunk: command_buffer.extend(chunk) # Command: C3 3C | mode | reserved | checksum # mode: 0=STOP, 1=OPEN, 2=CLOSE while len(command_buffer) >= 5: if command_buffer[0] != 0xC3 or command_buffer[1] != 0x3C: del command_buffer[0] continue command = command_buffer[:5] if (sum(command[:4]) & 0xFF) != command[4]: del command_buffer[0] continue del command_buffer[:5] mode = command[2] if mode <= 2: set_gripper(mode) imu_uart = start_imu_at_200hz() usb_uart = UART(0, baudrate=USB_BAUD, bits=8, parity=None, stop=1, tx=1, rx=3) while True: poll_usb_commands(usb_uart) # The angle frame is used as the 200 Hz snapshot trigger. if poll_imu(imu_uart): usb_uart.write(make_packet()) time.sleep_ms(0)