pocketterm35
差分
このページの2つのバージョン間の差分を表示します。
| 両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
| pocketterm35 [2026/06/29 01:47] – [キーマップ変更] araki | pocketterm35 [2026/07/10 05:19] (現在) – [キーマップ変更] araki | ||
|---|---|---|---|
| 行 68: | 行 68: | ||
| * ガンガンゲームを楽しみたい人 | * ガンガンゲームを楽しみたい人 | ||
| * ARM64とx64の違いがイマイチ判ってない人 | * ARM64とx64の違いがイマイチ判ってない人 | ||
| + | * DTBOがなんなのかわかってない人 | ||
| 画面サイズ640x480しかないので、汎用的なポータブルデバイスを欲しい人には向いていない | 画面サイズ640x480しかないので、汎用的なポータブルデバイスを欲しい人には向いていない | ||
| 行 154: | 行 155: | ||
| waveshare-35dpi-[345]b.dtbo は / | waveshare-35dpi-[345]b.dtbo は / | ||
| + | |||
| + | **カーネルなどの更新を行った場合、新しく / | ||
| + | |||
| + | リブートする前なら / | ||
| <file ini config.txt> | <file ini config.txt> | ||
| 行 761: | 行 766: | ||
| 原因をまじめに突き止めてもよかったのだが、codexを起動して「steamの設定を日本語にしたが、フォントがないのか□が表示されてしまう」とプロンプトしたらあっという間に設定をなおしてくれたので、どうやったのかはあまりちゃんと把握していないが、とにかく日本語が表示されるようになった | 原因をまじめに突き止めてもよかったのだが、codexを起動して「steamの設定を日本語にしたが、フォントがないのか□が表示されてしまう」とプロンプトしたらあっという間に設定をなおしてくれたので、どうやったのかはあまりちゃんと把握していないが、とにかく日本語が表示されるようになった | ||
| - | なので、同じトラブルに見舞われた人がいたら、codexに相談することをお勧めする | + | なので、同じトラブルに見舞われた人がいたら、codex((claude(Opus4.8)は解決できなかったのでcodex一択である))に相談することをお勧めする |
| - | {{ :: | + | {{ :: |
| - | なお、テストプレイのために XeGrader plus (体験版)を導入して遊んでみた | + | なお、テストプレイのために XeGrader plusを導入して遊んでみた |
| カーソルキーとスペースキーで操作できるので、この端末で遊ぶのにはもってこいだ | カーソルキーとスペースキーで操作できるので、この端末で遊ぶのにはもってこいだ | ||
| 行 848: | 行 853: | ||
| ジョイパッドとして認識されているわけでもないため、ゲームやPCエミュレータからジョイパッドとしても使えなければ、ゲームで一般的に使われる英字キーでもないので、まったく利用価値がない | ジョイパッドとして認識されているわけでもないため、ゲームやPCエミュレータからジョイパッドとしても使えなければ、ゲームで一般的に使われる英字キーでもないので、まったく利用価値がない | ||
| - | これはジョイパッドとして認識されるようにするか、最低でも、単純な英字キーとは別のものに割り当てるべきだろう | + | <del>これはジョイパッドとして認識されるようにするか、最低でも、単純な英字キーとは別のものに割り当てるべきだろう |
| + | |||
| + | これらの制御は RP2040で行われているようなので、ファームウェアを書き換えることで改善できると思われるので、今後の研究対象のひとつだと思っている</ | ||
| + | |||
| + | キーコードの割り当ては、RP2040側にインストールされている CircuitPythonにて行われている | ||
| + | |||
| + | 背面のRP2040リセットのボタンをいい感じに連打していると、CIRCUITPYというUSBメモリデバイスが現れることがある((標準のboot.pyによって、CIRCUITPYが出ないようにされているので、いい感じでRESETを連打することで、boot.pyの実行がキャンセルされることがあるので、そのタイミングでCIRCUITPYが現れる。本当にいい感じで連打してとしかいいようがない))ので、それが出てきたら、中の boot.pyや code.pyを変更したり、追加のライブラリを libフォルダーに入れたりしてカスタムすることができる | ||
| - | これらの制御は RP2040で行われているようなので、ファームウェアを書き換えることで改善できると思われるので、今後の研究対象のひとつだと思っている | ||
| ==== 電池容量のOS側からの検出 ==== | ==== 電池容量のOS側からの検出 ==== | ||
| 行 917: | 行 928: | ||
| つまりは、できること・できないことは Adafruit CircuitPython次第ということだ | つまりは、できること・できないことは Adafruit CircuitPython次第ということだ | ||
| - | boot.pyをいじれば、ゲームパッドなどもついかできそうだが、boot.pyを壊してしまったら CIRCUITPYドライブは現れない | + | なお、この部分は大変に長くなるので[[PocketTerm35のキーマップを変更する]]にまとめる |
| + | |||
| + | < | ||
| + | |||
| + | スクリーンロックがかかると、CIRCUITPYドライブはアンマウントされてしまうので、万一不具合がある状態だとキー入力ができず、スクリーンロックを解除できず、結果CIRCUITPYを修正できなくなってしまう | ||
| + | |||
| + | なので、スクリーンロックをオフにするか、万一に備えてUSBの外付けキーボードを用意しておくことを推奨する | ||
| + | |||
| + | やりたいことによって、code.pyを修正すれば済むのか、あるいはboot.pyを修正したり、ほかのライブラリをたしたりしないといけないのかが変わってくる | ||
| + | |||
| + | === CIRCUITPYをマウントする === | ||
| + | |||
| + | 実はこの操作は運任せである | ||
| + | |||
| + | まあ、気分はわかるのだが、Waveshareは、boot.pyの中で、disable_usb_drive()しているので、リセットを押してboot.pyが走る前にいい感じのところで再度リセットがかかれば、CIRCUITPYが現れる | ||
| + | |||
| + | この状態を逃さずに、バックアップを作成し作業用のコピーを作成する | ||
| + | |||
| + | なお、CIRCUITPYが出たりでなかったりするのは不便なので、ある程度の作業が終わるまでは、boot.pyから``storage.disable_usb_drive()``の行をコメントアウトしてしまうといいかもしれない | ||
| + | |||
| + | |||
| + | |||
| + | === キーボードが返すことができるキーコードの範囲で我慢する === | ||
| + | |||
| + | 例えば、左シフトであるべき一にTABキーがあって間違って押しちゃうし、大体、右の方のキーとSHIFTを同時押しするのもめんどくさくてやだなあと思っていたとする | ||
| + | |||
| + | このTABを普通におしたらシフト、Fnと押したらTABになるようにしたら便利そうだと思ったら、KEY_MAPという配列を書き換えればいい | ||
| + | |||
| + | 具体的には、KEY_MAPの定義の row 5を下のように書き換えればいい | ||
| + | |||
| + | Keycode.TABをKeycode.SHIFTに、Keycode.SHIFTを Keycode.RIGHT_SHIFTにそれぞれ置き換えただけである | ||
| + | |||
| + | 右シフトをRIGHT_SHIFTにする必要があるかどうかはどうでもいいかもしれないが、これで動作がそのようになる | ||
| + | |||
| + | 改造も小さくて、typoでもしない限りは、問題は起きないだろう | ||
| + | |||
| + | <code python> | ||
| + | [Keycode.SHIFT, | ||
| + | </ | ||
| + | |||
| + | |||
| + | === アクションキーはゲームパッドであるべきだ === | ||
| + | |||
| + | ゲームパッドをHIDデバイスとして追加するのはそれほど難しくもないが、めんどくさくはある | ||
| + | |||
| + | ゲームパッドは非常に多くの種類が存在してるのだが、PocketTerm35 にあるのは、四方向のデジタル方向キーと、A, | ||
| + | |||
| + | 方向キーをD Hatとして実装するのか、アナログスティック((だが0か全開かだけ))として実装するのかは難しいところだが、手元にあったSNES風のパッドがアナログスティック実装していたのでそれに倣うことにする | ||
| + | |||
| + | == ライブラリの準備 == | ||
| + | |||
| + | [[https:// | ||
| + | |||
| + | このとき、15行目あたりにある adafruit_hidを hidに変更する | ||
| + | |||
| + | <code python> | ||
| + | from hid import find_device | ||
| + | </ | ||
| + | |||
| + | 本来、CircuitPythonのhidのバンドルライブラリは adafruit_hidなのだが、PocketTerm35のものは hid になっているので、どちらかを変更しないといけないのだが、hidを adafruit_hidにすると code.pyの方に波及するので、ここでは追加ライブラリの hid_gamepad.pyの方を合わせてしまう | ||
| + | |||
| + | == Gamepadデバイスの追加 == | ||
| + | |||
| + | デバイスを追加する作業はboot.py に手を入れる必要がある | ||
| + | |||
| + | ほとんど呪文みたいなものなので、このままコピペすればいいと思う | ||
| + | |||
| + | <file python boot.py> | ||
| + | import usb_hid | ||
| + | |||
| + | # This is only one example of a gamepad report descriptor, | ||
| + | # and may not suit your needs. | ||
| + | GAMEPAD_REPORT_DESCRIPTOR = bytes(( | ||
| + | 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) | ||
| + | 0x09, 0x05, # Usage (Game Pad) | ||
| + | 0xA1, 0x01, # Collection (Application) | ||
| + | 0x85, 0x04, # | ||
| + | 0x05, 0x09, # Usage Page (Button) | ||
| + | 0x19, 0x01, # Usage Minimum (Button 1) | ||
| + | 0x29, 0x0A, # Usage Maximum (Button 10) ... サンプルは16ボタンだが参考にしたSNES風ゲームパッドが10ボタンだったので | ||
| + | 0x15, 0x00, # | ||
| + | 0x25, 0x01, # | ||
| + | 0x75, 0x01, # | ||
| + | 0x95, 0x10, # | ||
| + | 0x81, 0x02, # Input (Data, | ||
| + | 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) | ||
| + | 0x15, 0x81, # | ||
| + | 0x25, 0x7F, # | ||
| + | 0x09, 0x30, # Usage (X) | ||
| + | 0x09, 0x31, # Usage (Y) | ||
| + | 0x09, 0x32, # Usage (Z) | ||
| + | 0x09, 0x35, # Usage (Rz) | ||
| + | 0x75, 0x08, # | ||
| + | 0x95, 0x04, # | ||
| + | 0x81, 0x02, # Input (Data, | ||
| + | 0xC0, # End Collection | ||
| + | )) | ||
| + | |||
| + | gamepad = usb_hid.Device( | ||
| + | report_descriptor=GAMEPAD_REPORT_DESCRIPTOR, | ||
| + | usage_page=0x01, | ||
| + | usage=0x05, | ||
| + | report_ids=(4, | ||
| + | in_report_lengths=(6, | ||
| + | out_report_lengths=(0, | ||
| + | ) | ||
| + | |||
| + | usb_hid.enable( | ||
| + | (usb_hid.Device.KEYBOARD, | ||
| + | | ||
| + | | ||
| + | | ||
| + | ) | ||
| + | </ | ||
| + | |||
| + | == コードを変更する == | ||
| + | |||
| + | PocketTerm35のキーマップは、標準とFnキー押下時の二種類があり、キーマトリックスの配列の形で定義されている | ||
| + | |||
| + | 単純にキーボードのキーコードが返ればいいものであれば、単純にキーコードを書いておけばいいが、何か特別な処理をさせたい場合には、CUSTOM_KEYSに負数のコードを定義し、CUSTOM_KEYS_FUNCTION でマッピングされた関数がキーが押されたときに呼ばれるというような動きになっている | ||
| + | |||
| + | もともとのマッピングではこの動作でも問題はなかったが、ゲームパッド動作を割り当てようと思うと、押しっぱなし(オートリピート)がきかなくなってしまったため、CUSTOM_KEYS_FUNCTIONに関数ではなく、makeとbreakの二つの手続きのペアのタプルを登録できるようにし、キーの処理部でも、タプルが登録されていたら、離す処理を呼び出すように変更した | ||
| + | |||
| + | ここまでの準備をして、code.pyを書き換えた | ||
| + | |||
| + | まず、ゲームパッドを code.pyで扱えるように宣言する | ||
| + | |||
| + | from hid_gamepad import Gamepad と gp = Gamepad(usb_hid.devices) を追加するのがこの部分で必要なことである | ||
| + | |||
| + | <code python> | ||
| + | import usb_hid | ||
| + | from hid.keyboard import Keyboard | ||
| + | from hid.keycode import Keycode | ||
| + | from hid.consumer_control import ConsumerControl | ||
| + | from hid.consumer_control_code import ConsumerControlCode | ||
| + | from hid.keyboard_layout_us import KeyboardLayoutUS | ||
| + | from hid_gamepad import Gamepad | ||
| + | import board | ||
| + | import microcontroller | ||
| + | import digitalio | ||
| + | import time | ||
| + | import pwmio # | ||
| + | |||
| + | |||
| + | bl_pwm_value = 32767 # | ||
| + | ad_pwm_value = 32767 # | ||
| + | |||
| + | try: | ||
| + | # Set up a keyboard device. | ||
| + | kbd = Keyboard(usb_hid.devices) | ||
| + | layout = KeyboardLayoutUS(kbd) | ||
| + | consumer_control = ConsumerControl(usb_hid.devices) | ||
| + | gp = Gamepad(usb_hid.devices) | ||
| + | </ | ||
| + | |||
| + | そして、カスタムのキーコード(負数)を追加する | ||
| + | |||
| + | -112以降の " | ||
| + | |||
| + | <code python> | ||
| + | # Define custom function keys using a dictionary | ||
| + | CUSTOM_KEYS = { | ||
| + | # Add more custom keys as needed | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | " | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | そして、カスタムキーが操作されたときに呼び出される処理のマッピングが以下である | ||
| + | |||
| + | マッピングが関数のタプルになっているものは、押されたとき、離されたときの処理で、単純に関数だけが書かれているものは押されたときだけ呼び出される | ||
| + | |||
| + | <code pyhotn> | ||
| + | SPECIAL_KEY_FUNCTIONS = { | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | Keycode.CAPS_LOCK | ||
| + | ConsumerControlCode.SCAN_PREVIOUS_TRACK : scan_previous_track, | ||
| + | ConsumerControlCode.PLAY_PAUSE | ||
| + | ConsumerControlCode.SCAN_NEXT_TRACK | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | CUSTOM_KEYS[" | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | この変更を行ったために、メインループにも手を入れた | ||
| + | |||
| + | <code python> | ||
| + | # Main loop | ||
| + | try: | ||
| + | while True: | ||
| + | current_keys = set(scan_keyboard()) | ||
| + | |||
| + | # Handle keys that were released | ||
| + | for key in previous_keys - current_keys: | ||
| + | if key is not None and key >= 0: | ||
| + | kbd.release(key) | ||
| + | elif key is not None and key in SPECIAL_KEY_FUNCTIONS: | ||
| + | if isinstance(SPECIAL_KEY_FUNCTIONS[key], | ||
| + | (fun_make, fun_break) = SPECIAL_KEY_FUNCTIONS[key] | ||
| + | fun_break() | ||
| + | |||
| + | # Handle keys that are pressed | ||
| + | for key in current_keys - previous_keys: | ||
| + | if key is not None: | ||
| + | if key in SPECIAL_KEY_FUNCTIONS: | ||
| + | if isinstance(SPECIAL_KEY_FUNCTIONS[key], | ||
| + | (fun_make, fun_break) = SPECIAL_KEY_FUNCTIONS[key] | ||
| + | fun_make() | ||
| + | else: | ||
| + | SPECIAL_KEY_FUNCTIONS[key]() | ||
| + | elif key >= 0: # 过滤掉负值键码 | ||
| + | kbd.press(key) | ||
| + | |||
| + | </ | ||
| + | |||
| + | キーマップは以下のようになる | ||
| + | |||
| + | |||
| + | <code python> | ||
| + | # Define the key map for the keyboard matrix | ||
| + | KEY_MAP = [ | ||
| + | #col0 | ||
| + | [CUSTOM_KEYS[" | ||
| + | [Keycode.ONE, | ||
| + | [Keycode.Q, | ||
| + | [Keycode.A, | ||
| + | [Keycode.Z, | ||
| + | [Keycode.SHIFT, | ||
| + | [CUSTOM_KEYS[" | ||
| + | ] | ||
| + | |||
| + | </ | ||
| + | |||
| + | あとは、キーを押したり離したりの処理を書けば完成である | ||
| + | |||
| + | カーソルキーはゲームパッドでもあり、キーボード上のカーソルキーでもあるので、押すと両方に対してコードを送信している | ||
| + | |||
| + | もしこれで何か問題が起きるようであれば、キーマップの方を Keycode.UP_ARROW, | ||
| + | |||
| + | <code python> | ||
| + | def gamepad_press_thumb(): | ||
| + | # A button | ||
| + | gp.press_buttons(2) | ||
| + | |||
| + | def gamepad_release_thumb(): | ||
| + | gp.release_buttons(2) | ||
| + | |||
| + | def gamepad_press_thumb2(): | ||
| + | # B button | ||
| + | gp.press_buttons(3) | ||
| + | |||
| + | def gamepad_release_thumb2(): | ||
| + | gp.release_buttons(3) | ||
| + | |||
| + | def gamepad_press_trigger(): | ||
| + | # X button | ||
| + | gp.press_buttons(1) | ||
| + | |||
| + | def gamepad_release_trigger(): | ||
| + | gp.release_buttons(1) | ||
| + | |||
| + | def gamepad_press_top(): | ||
| + | # Y button | ||
| + | gp.press_buttons(4) | ||
| + | |||
| + | def gamepad_release_top(): | ||
| + | gp.release_buttons(4) | ||
| + | |||
| + | def gamepad_press_top2(): | ||
| + | # L button | ||
| + | gp.press_buttons(5) | ||
| + | |||
| + | def gamepad_release_top2(): | ||
| + | gp.release_buttons(5) | ||
| + | |||
| + | def gamepad_press_pinkie(): | ||
| + | # R button | ||
| + | gp.press_buttons(6) | ||
| + | |||
| + | def gamepad_release_pinkie(): | ||
| + | gp.release_buttons(6) | ||
| + | |||
| + | def gamepad_btn_base3(): | ||
| + | # Select button | ||
| + | gp.click_buttons(9) | ||
| + | |||
| + | def gamepad_btn_base4(): | ||
| + | # Start button | ||
| + | gp.click_buttons(10) | ||
| + | |||
| + | def gamepad_press_up(): | ||
| + | # Send both Gamepad / Keyboard | ||
| + | gp.move_joysticks(x=0, | ||
| + | kbd.press(Keycode.UP_ARROW) | ||
| + | |||
| + | def gamepad_release_up(): | ||
| + | gp.move_joysticks(x=0, | ||
| + | kbd.release(Keycode.UP_ARROW) | ||
| + | |||
| + | def gamepad_press_down(): | ||
| + | # Send both Gamepad / Keyboard | ||
| + | gp.move_joysticks(x=0, | ||
| + | kbd.press(Keycode.DOWN_ARROW) | ||
| + | |||
| + | def gamepad_release_down(): | ||
| + | gp.move_joysticks(x=0, | ||
| + | kbd.release(Keycode.DOWN_ARROW) | ||
| + | |||
| + | def gamepad_press_left(): | ||
| + | # Send both Gamepad / Keyboard | ||
| + | gp.move_joysticks(x=-127, | ||
| + | kbd.press(Keycode.LEFT_ARROW) | ||
| + | |||
| + | def gamepad_release_left(): | ||
| + | gp.move_joysticks(x=0, | ||
| + | kbd.release(Keycode.LEFT_ARROW) | ||
| + | |||
| + | def gamepad_press_right(): | ||
| + | # Send both Gamepad / Keyboard | ||
| + | gp.move_joysticks(x=127, | ||
| + | kbd.press(Keycode.RIGHT_ARROW) | ||
| + | |||
| + | def gamepad_release_right(): | ||
| + | gp.move_joysticks(x=0, | ||
| + | kbd.release(Keycode.RIGHT_ARROW) | ||
| + | </ | ||
| + | </ | ||
| + | <del>boot.pyをいじれば、ゲームパッドなどもついかできそうだが、boot.pyを壊してしまったら CIRCUITPYドライブは現れない | ||
| つまり結局ファームウェアを復元できる状態でないととてもじゃないが怖くていじれないというのが正解だ | つまり結局ファームウェアを復元できる状態でないととてもじゃないが怖くていじれないというのが正解だ | ||
| 行 968: | 行 1348: | ||
| 今のところ、自前のcode.pyはうまく動かない | 今のところ、自前のcode.pyはうまく動かない | ||
| - | デバッグなどできればいいのだが、Pythonはインデントなどに強く依存する変態言語なので、見た目大丈夫そうでも実は動かないとかあるので、本当に勘弁してほしい | + | デバッグなどできればいいのだが、Pythonはインデントなどに強く依存する変態言語なので、見た目大丈夫そうでも実は動かないとかあるので、本当に勘弁してほしい</ |
pocketterm35.1782697678.txt.gz · 最終更新: by araki
