QMKファームウェアでステータスLEDを扱う

{{キーボード名}}.cled_set_kb(uint8_t usb_led)で設定する

usb_led(1 << USB_LED_NUM_LOCK), (1 << USB_LED_CAPS_LOCK), (1 << USB_LED_SCROLL_LOCK)論理積を取るとそれぞれのLEDを点灯させるべき状態かどうかを取得できる。あとはそれぞれのキーボードに合わせてLED点灯・消灯のコードを書くだけ。

キーボードを設計しています

またか, という感じですが

わかりやすい説明

  • 左手用と右手用が分かれたキーボードです
    • 左右間は特殊なオーディオケーブルで繋ぎます
  • ノートパソコンのキーボードのうちテンキーとファンクションキーと矢印キー以外のキーが揃っています。
    • 文章の入力に使う部分がほとんど揃っています
  • 普通のキーボードと同じように行ごとにキーがずれています
  • 日本語配列にも英字配列にもできます
  • キーキャップセットのキーがうまくはまります
  • スイッチを簡単に交換できます
  • キーボードからテンキーを生やせます
  • 親指の位置にたくさんキーがあります
  • 穴に端子を差し込むタイプのパーツを使うのではんだづけが簡単です

コンセプト

「そういえば60%を分割した形でスイッチを簡単に交換ができるキーボードってないよね」という気づきがきっかけです。

自分は panda_split が最も良いと感じているので, 新しいキーボードは布教用・一般の環境の練習用に使いたいです。

y-sni.hatenablog.com

仕様

  • MX互換スイッチ専用
  • 組み立てやすいようにスイッチソケット以外はスルーホール部品
  • modulo互換(INT用のTRRSジャックはなし)
  • 一般のキーキャップセットで全キーを埋められる
  • US/JIS両対応
  • くっつけると分割式でないキーボードの形になる

妥協点

  • Kailh Low Profileスイッチ非対応
    • スイッチの選択肢が少ないからスイッチを交換する需要がないと思った
    • 正しいサイズのISOエンターキーのキーキャップがないらしい
  • 高さのあるスルーホール部品を使いつつ薄くするため, 左右に大きなベゼルができる
  • Enterキー周辺の配列がJISとUSで大きく変わるため, 5ピンスイッチ指定になるかもしれない
    • 需要があるならプレートを作り分ける??
  • 電源ランプ以外光らない
  • くっつけるとスペースキーの部分に0.25uの隙間ができる

設計中に気付いたこと

  • トラブルシュートのために電源ランプを付けるべき

現状

左手の配線がひと通り完了した

右手の配線は次のキーボードニュースを見ながらするつもり

進捗 2019/06/04

諸々のコストについての議論を見て完組品として販売するのが良さそうだと思いました。

不良が出ないようにTRRSケーブルもModulo Pendant(互換品)も接続した状態で販売したいと思います。

まとめ

いかがでしたか?

ほしい人は「ホギィ」と言っていただけると開発のモチベーションに繋がります。

GoogleHomeにFelicaカードの残高を教えてもらおう

libpafeをインストール

sudo apt install libavahi-compat-libdnssd-dev 
mkdir tmp
cd tmp

# libpafeをインストール
sudo apt install libusb-dev
wget http://hito.music.coocan.jp/pasori/libpafe-0.0.8.tar.gz
tar zxvf libpafe-0.0.8.tar.gz
cd libpafe-0.0.8/

## 以下3行は不要かもしれない
./configure
make
sudo make install

sudo apt install debhelper autotools-dev
dpkg-buildpackage -us -uc
sudo dpkg -i ../libpafe_0.0.8-1_armhf.deb

ls /lib/udev/rules.d/ | grep libpafe # udevの設定ができているか確認


# libpafe-devをインストール
wget http://hito.music.coocan.jp/pasori/libpafe-dev_0.0.8-1_all.deb
sudo dpkg -i libpafe-dev_0.0.8-1_all.deb

# libpafe-rubyをインストール
wget http://hito.music.coocan.jp/pasori/libpafe-ruby-0.0.8.tar.gz
tar xvzf libpafe-ruby-0.0.8.tar.gz 
cd libpafe-ruby/

sudo apt install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev
sudo apt install ruby ruby2.3-dev

ruby extconf.rb
make
make install

google-home-voicetext のインストール

cd ~/prog
git clone https://github.com/sikkimtemi/google-home-voicetext.git
cd google-home-voicetext
npm install

patch -u -R node_modules/mdns/lib/browser.js < mdns_patch/browser.js.patch

onServiceUpが1度しか動作しないようにする

diff --git a/google-home-voicetext.js b/google-home-voicetext.js
index a856df4..435fe46 100644
--- a/google-home-voicetext.js
+++ b/google-home-voicetext.js
@@ -22,7 +22,13 @@ const ip = function(ip) {
 const notify = function(message, callback) {
   if (!deviceAddress) {
     browser.start();
+    serviceUpCalled = false;
     browser.on("serviceUp", function(service) {
+      if(serviceUpCalled)
+        return;
+      else
+        serviceUpCalled = true;

ポート番号の変更

既にスマートリモコンプログラムが動いているからか8080番, 8888番では動かなかったのでそれぞれ8079番,8887番で動かすことにし, 適切に書き換える

Rubyスクリプトを書く

ICカード読み込み部分はあっきいさんのそのままです。

# coding: utf-8

# IC balance checker  /  2019 @y_sni
#   Licence:
#     MIT License
#   references:
#     http://homepage3.nifty.com/slokar/pasori/libpafe-ruby.html
#     http://iccard.jennychan.at-ninja.jp/format

#require 'wiringpi'
require "pasori"
require "net/http"
require 'uri'

def send_message(message)
    url = "http://localhost:8079/google-home-voicetext"
    puts message
    res = Net::HTTP.post_form( URI.parse(url), {'text'=>message} )
    puts res.body
end

pasori = Pasori.open

begin
  begin
  felica = pasori.felica_polling
    system = felica.request_system()
    card = {}
    system.each {|s|
      puts "system called"
      if (s == Felica::POLLING_SUICA || s == -31065 || s == -32546 || s == -31138 || s == -30261)
        # Suica (Generic traffic IC card) balance
        felica.foreach(Felica::SERVICE_SUICA_HISTORY) {|l|
          data = l.unpack('CCnnCCCCvN')
          card["name"] = "Suica"
          if (s == -32546)
            card["name"] = "Iruca" end
          if (s == -31138)
            card["name"] = "SAPICA" end
          if (s == -30261)
            # PASMO(TOKYU noruleage honorary station master card)
            card["name"] = "PASMO" end
          card["balance"] = data[8]
          break
        }
        # SAPICA point
        felica.foreach(0xBA4B) {|l|
          data = l.unpack('CCCNNNC')
          card["point"] = data[2] | data[1] | data[0]
        }
      elsif (s == -512)
        pasori.felica_polling(Felica::POLLING_EDY) {|felica2|
          # Edy balance
          felica2.foreach(0x1317) {|l|
            data = l.unpack('VVNnv')
            card["name"] = "Edy"
            card["balance"] = data[0]
            break
          }
          # nanaco balance
          felica2.foreach(0x5597) {|l|
            data = l.unpack('VVVV')
            card["name"] = "nanaco"
            card["balance"] = data[0]
            break
          }
          # nanaco point
          cnt = 0
          felica2.foreach(0x560B) {|l|
            if (cnt == 0)
              cnt += 1
              next
            end
            data = l.unpack('CCC')
            card["point"] = data[0] << 16 | data[1] << 8 | data[2]
            break
          }
          # waon balance
          felica2.foreach(0x6817) {|l|
            data = l.unpack('vNNNn')
            card["name"] = "WAON"
            card["balance"] = data[0]
            break
          }
          # waon point
          felica2.foreach(0x684B) {|l|
            data = l.unpack('CCC')
            card["point"] = data[0] << 16 | data[1] << 8 | data[2]
            break
          }
        }
      elsif (s == 0xA604)
        felica2.foreach(0x00CF) {|l|
          data = l.unpack('NNNCCCC')
          card["balance"] = data[4] << 16 | data[5] << 8 | data[6]
          card["name"] = "LuLuCa"
          break
        }
      elsif (s == 0x0F04)
        felica2.foreach(0x030F) {|l|
          data = l.unpack('NNNCSC')
          card["balance"] = data[4]
          card["name"] = "ナイスパス"
          break
        }
      else
        if (s != 1223 && s != -32638 && s != -31445)
          card["name"] = "Unknown"
          card["balance"] = sprintf("%d(%X)", s, s)
        end
      end
    }

    send_message "#{card["name"]}の残高は#{card["balance"]}円です。"
    #sleep 5
  rescue => ee
    puts 'error'
    send_message "ICカードがありません"
    #sleep 1
  end #while true

rescue Interrupt
  felica.close
  pasori.close
  print "\n\n"
  exit 0
end

サーバーが自動で起動するようにする

~/bin/googlehomeserver.sh

#! /bin/bash

cd /home/pi/prog/google-home-voicetext

export VOICETEXT_API_KEY=YOUR_API_KEY
export WIRELESS_IP=RASPBERRY_PI_ADDRESS

node file-server.js &
sleep 5
node api-server.js

/etc/systemd/system/googlehomevoice.service

[Unit]
Description = google home server

[Service]
ExecStart=/home/pi/bin/googlehomeserver.sh
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target
sudo systemctl enable googlehomevoice.service

苦労した話

  • 間にsleepを入れないとエラーになった
  • 最後のコマンドの末尾に&を付けると実行が終了したと判断されて殺されるらしい

Slackから起動するようにする

~/homebot/bin/iccard-check.sh

#! /bin/sh
ruby /home/pi/prog/iccard-check.rb

既になにかしらのコマンドをSlackから実行できているとして,

$ cd ~/homebot
$ bin/hubot
homebot> homebot command readic bin/iccard-check.sh

Slackでhomebotにsend readic()と話しかけると~/homebot/bin/iccard-check.shが実行されるようになる。

苦労した話

  • hubotのコマンド名には英字大文字は使えない

GoogleHomeへの呼びかけで起動するようにする

IFTTTで適切に設定する

参考

格安スマートリモコンの作り方 - Qiita

初心者歓迎詐欺被害者の会: Rubyでrequire 'pasori' したい話

libpafe

libpafe-ruby

Rasberry Piの起動時にhubotも自動的に起動させる - Smart Home

ストロベリー・リナックスのSB1602BWかスイッチサイエンスのI2C LCDを使って、Felica系カードの残高を表示するスクリプト。OSCのデモとかで使用中。 · GitHub

QMKファームウェア書き込み時にリセットボタン2連打が必要なProMicroの挙動

Detecting USB port, reset your controller now......... と表示されたときにリセットボタンを1回押すと

Device /dev/ttyACM0 has appeared; assuming it is the controller.
Waiting for /dev/ttyACM0 to become writable.

と表示される。リセットボタンを2連打すると通常通り書き込みが走る。

一度書き込んだあとはリセットボタンは1回押しで書き込めるようになる。

aptでインストールしたPythonでvirtualenvwrapperを使う

virtualenvwrapper.shがない

$HOME/.local/bin/virtualenvwrapper.shにある

source $HOME/.local/bin/virtualenvwrapper.shするとエラーメッセージが出る

sourceする前に以下を実行

export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Devel

パンダスルーホール ビルドガイド

  1. 抵抗とコンデンサを取り付ける
  2. TRRSジャックを取り付ける
  3. LEDを取り付ける
  4. スライドスイッチを取り付ける
  5. ICソケットを取り付ける
    • MCP23017を取り付けて全てのキーが反応するか動作確認する
  6. スイッチソケットを取り付ける
    • 片側に予備ハンダする
    • ソケットを乗せてはんだを温める
    • 反対側の端子を温めながら内側からハンダを流し込む
      • 十分にはんだを流し込むと基板上のパッドを伝ってこて先にはんだが付く
    • 動作確認する
  7. ゴム足を付ける

写真

f:id:y_sanagi:20190308151054j:plain
TRRSジャックとコンデンサ・抵抗を載せた状態

f:id:y_sanagi:20190308152600j:plain
スライドスイッチを付けた状態

f:id:y_sanagi:20190308152627j:plain

f:id:y_sanagi:20190308152722j:plain

f:id:y_sanagi:20190308164801j:plain
完成してしまったあと

知識ゼロでMCP23017を使う

動機

ライブラリを使うだけの空きメモリがなかった

方法

github.com

結果

  • レジスタIOCON.BANKの値によってレジスタのアドレスが変わるようだが, IOCON.BANKの初期値は0なのでそれで考えればいい
  • bufferにレジスタのアドレスと値を代入してwriteする
  • レジスタの値を読み出すときは, レジスタのアドレス(1バイト)をwriteしてからreadする
  • ピンの読み出しも「ピンの値を保持したレジスタを読み出す」形である。単にreadするだけではない。