適当に色々書くブログ

曜日不定12時半投稿。2000年生まれのゲーム好きが適当に色々書きます。

弐寺記録その29 Raspberry Pi PicoでPS2の弐寺コントローラーをPCで使えるようにしたい その3

2024/02/28追記

鍵盤を同時押ししても押されるタイミングがズレるバグ(仕様?)が見つかりました。

目測ですが、最速と最遅で32msほどズレる為、同時押しを完璧なタイミングで押してもズレてしまいます。

他者のプログラム(サンプルプログラムの引用)の部分で問題が起きている可能性が高い為、修正は難しそうです......。

ダメだったらラズパイ3個積みます。

 

ちなみになぜ気が付かなかったのかというと、以下の理由が原因だと思われます。

・モニターなどを買い替え環境が変わり、慣れていないせいだと思っていた

・コントローラーが傾いており、そのせいだと思っていた

・インフィニタスをそんなにやっていなかった

・インフィニタス不信

 

更に追記

3個積むことにしました。

一応昔のコード(Nキーロールオーバー対応)もGitHubに残しておくので、直せそうな方はダウンロードしてみてください

 

 

最新の追記以外を下に移動しました。

 

 

2024/01/19追記

皿をアーケードよりも回さないと反応しないバグを修正しました。

IncrementalEncoderの引数である除数を初期値の4から1にしたらめちゃくちゃ反応するようになりました。

それに伴い、countMaxを1から4に変更しました。

不満がある場合はcountMaxの代入値を変更してください。1に近づけるほど反応しやすくなります。

 

皿を回し続けても反応しないバグ修正。

また、リポジトリを公開したので進捗が気になる方はこちらをご覧ください。

github.com

 

 

前回

melolololon.hatenablog.com

 

こんにちは。

前回は皿は反応するようになりました。

 

今回はゲームパッドとして認識させたりNKROキーボードとして認識させようとしてみました。

 

結論を書くと、NKROキーボードとして認識させることに成功し、もしかしたら同じやり方でゲームパッドとして対応させられるかも(未検証)という感じです。

 

 

ゲームパッドとして認識させようとした時は、ゲームパッドとして認識はしたのですが、ピンのケーブルと3.3Vのケーブルを繋いでも無反応。

 

その後NKROのプログラムを書いても無反応。

2時間程悩み、もしかしてと思い3.3VとGPではなく、

GNDとGPを繋いだら反応しました。

元々3,3VとGPを接続してキーボードが反応していたので、このやり方であっていると思い込んでいました。

サンプルプログラムと同じやり方で実装する場合は注意してください。

できるかはわかりませんが、入力確認だけ通常のキーボードの方法で行い、入力の保持をNKRO対応のプログラムで行えば3.3Vに繋いでも動かせるかもしれません。

 

GNDに繋いだらNKROキーボードとして正常に動作したのなら、ゲームパッドとして使いたい場合もGNDとGPを接続したら反応するかもしれませんね。

と思って試したのですが、反応しませんでした。

まぁNKROキーボードとして扱った方が1個のラズパイでコントローラーを2個接続できるので良いとしましょう。(公式コントローラー買わせるために公式コントローラー以外のコントローラー使用禁止みたいなことされても使えますしね)

 

以下NKROキーボードのプログラムが書かれているサイト。

Adafuit 様

learn.adafruit.com

 

ここに記述されているプログラムに

key_pins = ( board.KEY1, board.KEY2, board.KEY3, board.KEY4, board.KEY5, board.KEY6, board.KEY7, board.KEY8, board.KEY9, board.KEY10, board.KEY11, board.KEY12, )

という記述があるのですが、以下の様に書き直さないとエラーが出て動きません。

key_pins = [ board.GP1, board.GP2, board.GP3, board.GP4, board.GP5, board.GP6, board.GP7, board.GP8, boardGP9, board.GP10, board.GP11, board.GP12, ]

 

このサンプルを使いつつプログラムを記述。

以下記述したプログラム。

 

# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import keypad
import board
import usb_hid
from adafruit_hid.keyboard import Keyboard, find_device
from adafruit_hid.keycode import Keycode

key_pins = [
    board.GP2,
    board.GP3,
    board.GP4,
    board.GP5,
    board.GP6,
    board.GP7,
    board.GP8,
    board.GP9,
    board.GP10,
    board.GP11,
    board.GP12,
]

keys = keypad.Keys(key_pins, value_when_pressed=False, pull=True)

class BitmapKeyboard(Keyboard):
    def __init__(self, devices):
        device = find_device(devices, usage_page=0x1, usage=0x6)

        try:
            device.send_report(b'\0' * 16)
        except ValueError:
            print("found keyboard, but it did not accept a 16-byte report. check that boot.py is installed properly")

        self._keyboard_device = device

        # report[0] modifiers
        # report[1:16] regular key presses bitmask
        self.report = bytearray(16)

        self.report_modifier = memoryview(self.report)[0:1]
        self.report_bitmap = memoryview(self.report)[1:]

    def _add_keycode_to_report(self, keycode):
        modifier = Keycode.modifier_bit(keycode)
        if modifier:
            # Set bit for this modifier.
            self.report_modifier[0] |= modifier
        else:
            self.report_bitmap[keycode >> 3] |= 1 << (keycode & 0x7)

    def _remove_keycode_from_report(self, keycode):
        modifier = Keycode.modifier_bit(keycode)
        if modifier:
            # Set bit for this modifier.
            self.report_modifier[0] &= ~modifier
        else:
            self.report_bitmap[keycode >> 3] &= ~(1 << (keycode & 0x7))
        
    def release_all(self):
        for i in range(len(self.report)):
            self.report[i] = 0
        self._keyboard_device.send_report(self.report)

 


from board import *
import rotaryio
import usb_hid
import digitalio
import board
import time
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard import Keycode

 

#キーコード https://docs.circuitpython.org/projects/hid/en/4.1.6/api.html
keys1P = [Keycode.Q,Keycode.W,Keycode.E,Keycode.R,Keycode.T,Keycode.Y,Keycode.U,Keycode.ENTER,Keycode.BACKSPACE,Keycode.P,Keycode.L,Keycode.K,Keycode.J]
scrUp1P = Keycode.LEFT_SHIFT
scrDown1P = Keycode.CONTROL
kbd = BitmapKeyboard(usb_hid.devices)


encoder = rotaryio.IncrementalEncoder(GP0,GP1,1)
prePos = 0

#皿が反応するまでにどのくらい回すかを設定する為の数値
countMax = 4
#以下がcountMaxを超えたら皿が反応する
count = 0
preCount = 0

#回したらどのくらい反応させたままにするか
reactionCountMax = 1500
#以下がreactionCountMaxを超えたら皿の反応がなくなる
reactionCount = 0

#回転フラグ
upRot = 0
downRot = 0
finalRotUp = 0

#一旦止めないと再入力できないようにするための変数
rotAfterTheEndStop = 1

#短時間で同一方向に入力できないようにする変数
#入力後に以下の変数の加算が開始し、notCountChangeTime == notCountChangeTimeMaxになったら同じ方向への入力が可能になる
notCountChangeTime = 0
notCountChangeTimeMax = 3500
    
def checkButton():
    ev = keys.events.get()
    if ev is not None:
        key = keys1P[ev.key_number]
        if ev.pressed:
            kbd.press(key)
        else:
            kbd.release(key)

 

def checkEncoder():
    global prePos,preCount,count,reactionCount,upRot,downRot,continuousRotCount,finalRotUp,rotAfterTheEndStop,stopAfterTheEndRot,notCountChangeTime
    pos = encoder.position
    cPos = pos - prePos
    moveDir = 0#1下 -1上 0未回転 
    if cPos > 0:
        moveDir = 1
    elif cPos < 0:
        moveDir = -1
    else:
        moveDir = 0
          
    #上回転
    if moveDir == -1: 
        if count > 0:#プラスだったら0を代入
            count = 0
        count = count - 1
        if count < -countMax:
            reactionCount = 0#回したら反応時間をリセット
        
        if count < -countMax and rotAfterTheEndStop == 1 and finalRotUp == 1 or finalRotUp == 0 and count < -countMax:#条件満たしたら入力
            downRot = 0 #逆に回転していたら中止
            kbd.press(scrUp1P)
            kbd.release(scrDown1P)
            upRot = 1
            count = 0
            finalRotUp = 1
            rotAfterTheEndStop = 0
            notCountChangeTime = 0
    #下回転
    elif moveDir == 1:
        if count < 0:#マイナスだったら0を代入
            count = 0
        count = count + 1
        if count > countMax:
            reactionCount = 0#回したら反応時間をリセット
            
        if count > countMax and rotAfterTheEndStop == 1 and finalRotUp == 0 or finalRotUp == 1 and count > countMax:
            upRot = 0 #逆に回転していたら中止
            kbd.press(scrDown1P)
            kbd.release(scrUp1P)
            downRot = 1
            count = 0
            finalRotUp = 0
            rotAfterTheEndStop = 0
            notCountChangeTime = 0
            reactionCount = 0#回したら反応時間をリセット
    
    prePos = pos
    
    if preCount == count:
        notCountChangeTime = notCountChangeTime + 1
        if notCountChangeTime == notCountChangeTimeMax:
            notCountChangeTime = notCountChangeTimeMax
            rotAfterTheEndStop = 1
            count = 0
            
    preCount = count
     
    #回転時間加算と回転入力終了処理
    if upRot == 1:
        reactionCount = reactionCount + 1
        if reactionCount == reactionCountMax:#一定時間経ったら回転中止
            reactionCount = 0
            upRot = 0
            count = 0
            kbd.release(scrUp1P)
            continuousRotCount = 0
            
    elif downRot == 1:
        reactionCount = reactionCount + 1
        if reactionCount == reactionCountMax:#一定時間経ったら回転中止
            reactionCount = 0
            downRot = 0
            count = 0
            kbd.release(scrDown1P)
            continuousRotCount = 0
            
        
while True:
     checkEncoder()
     checkButton()

 

コードが汚かったり変数名がクソなのは気にしてはいけない。

まだ細かい調整中なので、気に食わない部分があったら数値を変更して調整してください。

これをRaspberry Pi Pico書き込むと弐寺コントローラーがキーボードとして使えます。

 

こちらがテストプレイの結果。

判定数値は-1.05。コンバーターを使っていた時は-2.75でプレイしていました。

結構繋がったので問題はないと思います。

ということで完成です。やったね。

 

自分がコントローラーを購入した時は7800円だったのですが、今だったら3000円くらいで購入できるので、

・コントローラー3000円

Raspberry Pi Pico880円

・USBケーブル700円

・鍵盤(三和7500円or芝3500円)

・抵抗約300円

・導線 1m400円くらい?

で作れると思います。工具を持っていない方は工具費用も追加。

 

今回はざっくりとした作り方しか書きませんでしたが、もしかしたら詳しい作り方も書くかもしれません。

それではまた。

 

 

2024/01/17追記

無駄な処理と変数を削除しました。

回した際のreactionCountのリセットタイミングを変更しました。

 

2024/01/05追記

notCountChangeTimeMaxを5000から3500に変更しました。

 

2024/01/03追記

連続して同じ方向に皿を回しても反応してしまうバグを修正しました。