ESP32+PS2 wireless controller adapter board+microPython

When I bought it, I came with a connector. The wire inside this connector is thick, and ordinary DuPont wire cannot be used.
Be sure not to connect the positive and negative poles incorrectly. You can also input 3.3v voltage:

from machine import Pin
import time

di = Pin(26,Pin.IN,Pin.PULL_UP) # dat/di Connect ESP32 to pin 26 or connect ESP8266’s D5 pin=14
do = Pin(27,Pin.OUT) # cmd/do Connect ESP32 to pin 27 or connect ESP8266’s D6 pin=12
cs = Pin(14,Pin.OUT) # cs ESP32 is connected to pin 14 or D7 pin of ESP8266=13
clk = Pin(12,Pin.OUT) # clk Connect ESP32 to pin 12 or connect ESP8266’s D8 pin=15

def DO_H():
  do.value(1)
def DO_L():
  do.value(0)
def CS_H ():
  cs.value(1)
def CS_L():
  cs.value(0)
def CLK_H ():
  clk.value(1)
def CLK_L():
  clk.value(0)
#These are our button constants
PSB_SELECT = 1
PSB_L3 = 2
PSB_R3 = 3
PSB_START = 4
PSB_PAD_UP = 5
PSB_PAD_RIGHT = 6
PSB_PAD_DOWN = 7
PSB_PAD_LEFT = 8
PSB_L2 = 9
PSB_R2 = 10
PSB_L1 = 11
PSB_R1 = 12
PSB_GREEN = 13
PSB_RED = 14
PSB_BLUE = 15
PSB_PINK = 16
PSB_TRIANGLE = 13
PSB_CIRCLE = 14
PSB_CROSS = 15
PSB_SQUARE = 16
PSS_RX = 5
PSS_RY = 6
PSS_LX = 7
PSS_LY = 8
mask=[
    PSB_SELECT,
    PSB_L3,
    PSB_R3,
    PSB_START,
    PSB_PAD_UP,
    PSB_PAD_RIGHT,
    PSB_PAD_DOWN,
    PSB_PAD_LEFT,
    PSB_L2,
    PSB_R2,
    PSB_L1,
    PSB_R1 ,
    PSB_GREEN,
    PSB_RED,
    PSB_BLUE,
    PSB_PINK ]
comd=[0x01,0x42]
data=[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]

def ps2_init():
  CLK_H()
  DO_H()
  time.sleep_ms(100)

def ps2_cmd(cmd):
  global data
  data[1]=0
  for ref in (1,2,4,8,16,32,64,128):
    if (ref & amp; cmd):
      DO_H()
    else:
      DO_L()
    CLK_H()
    time.sleep_us(50)
    CLK_L()
    time.sleep_us(50)
    CLK_H()
    if(di.value()==1):
      data[1]=ref|data[1]

def ps2_red():
  global data
  globalcomd
  CS_L()
  ps2_cmd(comd[0])
  ps2_cmd(comd[1])
  CS_H()
  if(data[1]==57):
    return 0 #red light
  else:
    return 1 #not red

def ps2_read():
  global data
  globalcomd
  byte=0
  ref=0x01
  CS_L()
  ps2_cmd(comd[0])
  ps2_cmd(comd[1])
  for byte in (2,3,4,5,6,7,8):
    for ref in (1,2,4,8,16,32,64,128):
     CLK_H()
     CLK_L()
     time.sleep_us(50)
     CLK_H()
     if(di.value()==1):
      data[byte]= ref|data[byte]
    time.sleep_us(50)
  CS_H()

def ps2_clear():#ok
  global data
  for i in range(0,9,1):
    data[i]=0

def ps2_andata(button):
  global data
  return data[button]

def ps2_key():
  global data
  global mask
  ps2_clear()
  ps2_read()
  handkey=(data[4]<<8)|data[3]
  for index in (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15):
    if (( handkey & amp;(1<<(mask[index]-1)))==0):
      return index + 2
  return 0
  

print("Waiting for key press...")
while True:
  if ps2_key():
    print(ps2_key())
  time.sleep(0.1) 

Another version of the PS2 code supports simultaneous detection of multiple keys, which is easier to use:

Thanks to the author, the original code address is as follows: https://github.com/shallwe/micropython_ps2
The following is my modified code to facilitate calling. Can be saved as ps2.py for later use

import time
from machine import Pin

class PS2Controller:
    # These are our button constants
    SELECT = 1
    L3 = 2
    R3 = 3
    START=4
    UP = 5
    RIGHT = 6
    DOWN = 7
    LEFT = 8
    L2=9
    R2 = 10
    L1 = 11
    R1 = 12
    TRIANGLE = 13
    CIRCLE = 14
    CROSS = 15
    SQUARE = 16
    KEYS = dict([
                  (SELECT, "SELECT"), (L3, "L3"), (R3, "R3"), (START, "START"),
                  (UP, "UP"), (RIGHT, "RIGHT"), (DOWN, "DOWN"), (LEFT, "LEFT"),
                  (L2, "L2"), (R2, "R2"), (L1, "L1"), (R1, "R1"),
                  (TRIANGLE, "TRIANGLE"), (CIRCLE, "CIRCLE"), (CROSS, "CROSS"), (SQUARE, "SQUARE") ])

    CTRL_CLK = 10
    CTRL_BYTE_DELAY = 16

    CMD_SHORT_POLL = [0x01, 0x42, 0x00, 0x00, 0x00]
    CMD_ENTER_CONFIG = [0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00]
    CMD_SET_MODE = [0X01, 0x44, 0x00,
                        0x01, # 00 normal; 01 red or analog
                        0x03, # 03 lock;ee no lock
                        0x00, 0x00, 0x00, 0x00]
    CMD_SET_BYTES_LARGE = [0x01, 0x4F, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00]
    CMD_EXIT_CONFIG = [0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A]
    CMD_ENABLE_RUMBLE = [0x01, 0x4D, 0x00, 0x00, 0x01]
    CMD_TYPE_READ = [0x01, 0x45, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A]
    CMD_READ_DATA = [0X01, 0X42, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00]

    MAX_READ_DELAY = 1500

    VALID_MODES = [0X41, 0X73]

    def __init__(self, di_pin_no=26, do_pin_no=27, cs_pin_no=14, clk_pin_no=12): # DI=DAT, DO=CMD You can adjust the pins to your own corresponding pins here
        self.di_pin_no = di_pin_no
        self.do_pin_no = do_pin_no
        self.cs_pin_no = cs_pin_no
        self.clk_pin_no = clk_pin_no
        self.di = Pin(self.di_pin_no, Pin.IN) # DI = DAT
        self.do = Pin(self.do_pin_no, Pin.OUT) # DO = CMD
        self.cs = Pin(self.cs_pin_no, Pin.OUT)
        self.clk = Pin(self.clk_pin_no, Pin.OUT)
        self.buff_out = [0x01, 0x42]
        self.buff_in = [0] * 9
        self.pressed_keys = []
        self.read_delay = 1
        self.last_read_ms = 0
        self.lx = 0
        self.ly = 0
        self.rx = 0
        self.ry = 0

    @property
    def red_mode(self): # analog mode
        return self.buff_in[1] & 0xf0 == 0x70

    def do_h(self):
        self.do.value(1)

    def do_l(self):
        self.do.value(0)

    def cs_h(self):
        self.cs.value(1)

    def cs_l(self):
        self.cs.value(0)

    def clk_h(self):
        self.clk.value(1)

    def clk_l(self):
        self.clk.value(0)


    # noinspection PyUnresolvedReferences
    def delay_byte(self):
        time.sleep_us(self.read_delay)

    # noinspection PyUnresolvedReferences
    def delay_clk(self):
        time.sleep_us(self.CTRL_CLK)

    # noinspection PyUnresolvedReferences
    def delay_read(self):
        time.sleep_us(self.CTRL_BYTE_DELAY)
        # time.sleep_ms(self.read_delay)

    def cmd(self, cmd):
        ret = 0
        for i in range(8):
            if cmd & 1 << i:
                self.do_h()
            else:
                self.do_l()
            self.clk_l()
            self.delay_clk()
            if self.di.value():
                ret |= 1 << i
            self.clk_h()
        self.do_h()
        self.delay_byte()
        return ret

    """
    :param
    pure True means not delay
    """

    def cmd_group(self, cmds):
        self.cs_l()
        self.delay_byte()
        for cmd in cmds:
            self.cmd(cmd)
        self.cs_h()
        self.delay_read()

    def init(self):
        self.di = Pin(self.di_pin_no, Pin.IN)
        self.do = Pin(self.do_pin_no, Pin.OUT)
        self.cs = Pin(self.cs_pin_no, Pin.OUT)
        self.clk = Pin(self.clk_pin_no, Pin.OUT)
        self.do_h()
        self.clk_h()

        self.read_once()
        self.read_once()
        #
        if self.buff_in[1] not in [0x41, 0x73]:
            print("control type not ok, expect 41 73 79, bug get ", "{:02x}".format(self.buff_in[1]))
            return 1

        self.read_delay = 1

        for i in range(10):
            # self.cmd_group(self.CMD_SHORT_POLL)
            # self.cmd_group(self.CMD_SHORT_POLL)
            # self.cmd_group(self.CMD_SHORT_POLL)
            self.cmd_group(self.CMD_ENTER_CONFIG)
            self.delay_byte()

            self.do_h()
            self.clk_h()
            self.cs_l()

            self.delay_byte()
            #
            temp = [0] * len(self.CMD_TYPE_READ)
            for j in range(9):
                for cmd in self.CMD_TYPE_READ:
                    temp[j] = self.cmd(cmd)
            self.cs_h()

            self.cmd_group(self.CMD_SET_MODE)
            # self.cmd_group(self.CMD_ENABLE_RUMBLE)
            self.cmd_group(self.CMD_EXIT_CONFIG)
            self.read_once()
            if self.buff_in[1] in self.VALID_MODES:
                print("read_delay configured,", self.read_delay)
                break
            else:
                self.read_delay + = 1
                print("read_delay + + ,", self.read_delay)

    def reconfig(self):
        print("reconfig")
        self.cmd_group(self.CMD_ENTER_CONFIG)
        self.cmd_group(self.CMD_SET_MODE)
        self.cmd_group(self.CMD_EXIT_CONFIG)

    def p(self, debug=True):
        if debug:
            for d in self.buff_in:
                print("{:08b}".format(d))

        key_raw = (self.buff_in[4] << 8) | self.buff_in[3]
        for i in range(1, 17):
            if not key_raw & 1 << i - 1:
                self.pressed_keys.append(i)

        if self.red_mode:
            self.rx = self.buff_in[5] - 128
            self.ry = self.buff_in[6] - 128
            self.lx = self.buff_in[7] - 128
            self.ly = self.buff_in[8] - 128
        if self.pressed_keys:
            out = "keys:" + ','.join(self.KEYS[k] for k in self.pressed_keys) + "; "
        else:
            out = ""
        # for key in self.pressed_keys:
        # print(key, self.KEYS[key])
        if self.red_mode and (out or any(x != 0 for x in [self.rx, self.ry, self.lx, self.ly])):
            out + = "pos: (lx,ly):{},{}; (rx,ry): {},{}".format(self.lx, self.ly, self.rx, self.ry )

        if out:
            print(out)

    def read_once(self, debug=False):
        now = time.ticks_ms()
        delay = now - self.last_read_ms
        if delay > self.MAX_READ_DELAY:
            print(now, self.last_read_ms, delay)
            self.reconfig()
        elif delay < self.read_delay:
            # noinspection PyUnresolvedReferences
            time.sleep_ms(self.read_delay - delay)

        self.buff_in = [0] * 9
        self.pressed_keys.clear()

        for j in range(5):
            # for i in range(1):
            self.do_h()
            self.clk_h()
            self.cs_l()
            self.delay_byte()

            for i, c in enumerate(self.CMD_READ_DATA):
                self.buff_in[i] = self.cmd(c)

            self.cs_h()

            if self.buff_in[1] in self.VALID_MODES:
                break
            else:
                print("mode: {:08b}, retry_J: {}".format(self.buff_in[1], j))
                self.reconfig()
                self.delay_read()
        if self.buff_in[1] not in self.VALID_MODES and self.read_delay < 10:
            self.read_delay + = 1

        self.last_read_ms = time.ticks_ms()
        #self.p(debug)

        #Add below for yourself, the code in the original P()
        key_raw = (self.buff_in[4] << 8) | self.buff_in[3]
        for i in range(1, 17):
            if not key_raw & 1 << i - 1:
                self.pressed_keys.append(i)

        if self.red_mode:
            self.rx = self.buff_in[5] - 128
            self.ry = self.buff_in[6] - 128
            self.lx = self.buff_in[7] - 128
            self.ly = self.buff_in[8] - 128
        if self.pressed_keys: #Pay attention to the following lines: and, do not modify them, they will be marked and used later!
            out = "keys:" + ','.join(self.KEYS[k] for k in self.pressed_keys) + ": "
        else:
            out = "keys:none:"
        # for key in self.pressed_keys:
        # print(key, self.KEYS[key])
        if self.red_mode and (out or any(x != 0 for x in [self.rx, self.ry, self.lx, self.ly])):
            out + = "pos(lx,ly):{},{}: pos(rx,ry):{},{}:".format(self.lx, self.ly, self.rx, self. ry)
        if out:
            print(out)
            
        #return self.buff_in
        return out

'''
# Call the method and create a new file with the following lines of code, such as: ps2_test.py
from ps2 import PS2Controller
import time
ps2ctl = PS2Controller(di_pin_no=26, do_pin_no=27, cs_pin_no=14, clk_pin_no=12)
ps2ctl.init()
while True:
    key_car= ps2ctl.read_once() # The received character format is keys:UP,RIGHT: pos(lx,ly):0,-1: pos(rx,ry): 0,-1:
    #print("Key detected:",key_car)
    key_list= key_car.split(':') # Use: to split the string and write the keys input by key_list[1] in the array key_list, key_list[3] left joystick coordinates, key_list[5] right joystick rod coordinates
    if key_list[1]=="UP":
      print(key_car,"Forward")
      
    if key_list[1]=="UP,LEFT":
      print(key_car,"upper left")
    time.sleep(0.2)
''' 

running result:

Original link: https://www.jianshu.com/p/30723e5624ae