# LEDチェーンとレイアウトバリアント ELC P-4 の検証。 ## 結論 チェーンはレイアウトの選択と無関係に固定されている。バリアントごとにチェーンの並びを分ける必要はない。 理由は、per-key LEDがスイッチとは独立に基板へ実装されるため。排他な2つのキー位置の両方にLEDが載る。 QMKの `rgb_matrix.layout` はボードに1つしかなく、レイアウトごとに分かれていない。 | ボード | レイアウト数 | LED数 | バリアント固有のマトリクス位置 | そこへのLED割り当て | | --- | --- | --- | --- | --- | | dp60 | 7 | 72 | 8 | 8個すべてにあり | | cherrybstudio/cb87rgb | 9 | 92 | 6 | 6個すべてにあり | | crkbd/r2g | 2 | 54 | 6 (外側の列) | 6個すべてにあり | | annepro2/c18 | 2 | 61 | 1 (ISOの追加キー) | なし | | boardsource/sessanta | 2 | 72 | 4 (分割スペース) | なし | annepro2/c18 と boardsource/sessanta は、バリアント固有のキーにLEDが無い。 チェーンが切れるのではなく、そのキーにper-key LEDが載っていないだけである。 PHYがper-key LEDの有無をキーごとに持つ形でそのまま表せる。 どちらの型でも、LEDの番号はレイアウトの選択で動かない。 ## 調べ方 QMKのinfo.json 400件から、レイアウトを2つ以上持ち `rgb_matrix.layout` を持つ19件を抽出。 全レイアウトのマトリクス位置の和集合から共通部分を引いた「バリアント固有の位置」に、 LEDが割り当てられているかを見た。11件は割り当てあり、12件は一部に無し(両方に該当する板がある)。 ## 限界 回路図そのものではなく、ファームウェアが持つLED配列を見ている。 LEDが基板に実装されているかどうかまでは確認できない。ただしQMKの `rgb_matrix.layout` は レイアウトごとに分けられない構造なので、「選択によってチェーンの並びが変わる基板」は 少なくともQMKでは成立しない。 ## 検証スクリプト ```py # QMKのinfo.jsonを無作為抽出で取得する。qmk_list.json は # curl -o qmk_list.json https://keyboards.qmk.fm/v1/keyboard_list.json import json, random, urllib.request, concurrent.futures, os kbs=json.load(open('qmk_list.json'))['keyboards'] random.seed(42) os.makedirs('qmk', exist_ok=True) def fetch(kb): p='qmk/'+kb.replace('/','__')+'.json' if os.path.exists(p): return True try: with urllib.request.urlopen(f'https://keyboards.qmk.fm/v1/keyboards/{kb}/info.json', timeout=30) as r: open(p,'wb').write(r.read()) return True except Exception as e: return str(e)[:60] with concurrent.futures.ThreadPoolExecutor(8) as ex: res=list(ex.map(fetch, random.sample(kbs, 400))) print('fetched', sum(1 for r in res if r is True), 'failed', sum(1 for r in res if r is not True)) ``` ```py # レイアウトバリアントとper-key LEDの関係。rgb_matrix.layout はボードに1つしかない import json, glob, collections st=collections.Counter() for f in glob.glob('qmk/*.json'): d=json.load(open(f,encoding='utf-8')) for kb,v in (d.get('keyboards') or {}).items(): lays=v.get('layouts') or {}; rm=(v.get('rgb_matrix') or {}).get('layout') if not rm or len(lays)<2: continue st['対象ボード']+=1 used=[set(tuple(k['matrix']) for k in l['layout']) for l in lays.values()] variant_only=set().union(*used)-set.intersection(*used) # バリアント固有の位置 led=set(tuple(l['matrix']) for l in rm if 'matrix' in l) if not variant_only: continue if variant_only & led: st['固有位置にLEDあり']+=1 if variant_only - led: st['固有位置にLEDなし']+=1 print(kb, 'レイアウト', len(lays), 'LED', len(rm), '固有位置', len(variant_only), 'うちLEDあり', len(variant_only & led)) print(st) ```