キット販売する際の手順

このたび自作キーボードSatsuma JP SMDを遊舎工房で販売させていただきましたが, いくつもの失敗があったので, 今後レンタルボックスを利用される方が同様の失敗をすることがなければいいと思い, ドキュメントにします。

作業手順書を作る

中途半端な組立品として販売したので,

  • ゴム足を貼り忘れた
  • ネジ・スペーサーを付け忘れた
  • スタビライザーを付け忘れた
  • ファームウェアを通電確認用から書き換え忘れた

などの失敗しました。(もしくは作業をしたか不安になりました。)不安を解消するためにあったほうがいいと思います。

通常のキット販売なら内容品のチェックリストですかね。

レンタルボックスを予約したら在庫をつくる

レンタルボックスが利用可能になる頃から急に忙しくなって商品を用意することができなくなることもあるため, レンタルボックス利用期間開始前に一定量の在庫を用意しておきましょう。また, 「実際に組み立て済み品を作ってみると想像以上に動作確認が大変だったのでキット販売に切り替える」なんてこともできます。販売する商品の種類は少ないほうがPOPなどの用意も簡単なのでおすすめです。

利用期間になっても商品を用意できないと場所代だけがかかるし, 舎にも売上比例分の収入が入らなくなるのでできれば避けましょう。

キットに入れるもの

ぼくがキーマップ表を作り忘れたのでこの項目があります。

Satsuma JP SMD ソケット通電未確認版に入れたのは次のものです。

  • 組み立て・ファームウェア書き換え手順書
  • 本体
  • キーマップ表
  • ねじ・スペーサーの予備

設計・利用している当人は使い方がわかりますが, 購入者にはわからないので, 組み立て・ファームウェア書き換え手順書とキーマップ表はWebでもいいので用意する必要があります。特に今回はファームウェア書き込み済み品を販売したので特にキーマップ表が必要でした。ファームウェアを自分で書き込んでもらうタイプでC言語を読める人を購入者として想定するならキーマップ表は無くてもいいと思います。

ねじ・スペーサーは小単位での購入が面倒なので予備を入れておきました。

販売形態が複数あるなどの理由でPOPに「別途買う必要のあるもの」を書いてない場合, 値札と同じ側に「別途買う必要のあるもの」の表を付けておきましょう。

POPを作る

台はダイソーやセリアで売っています。手書きの紙を折って置くのはおすすめしません。

最低限商品名・値段・別途買う必要のあるものを書くといいと思います。

しないほうがいいこと

販売しない商品は置かないほうがいいです。今回テンキーも基板はありましたが販売が確定していないため・SatsumaJPキットに含まれるとの誤解を防ぐために撤去しました。

まとめ

この記事を読んで販売を面倒に感じたかもしれませんが, キットに不良があったとしても交換すればいいだけなので気軽にやっていきましょう!!

日本語配列60%分割キーボード Satsuma JP SMD について

f:id:y_sanagi:20190630084047j:plain
6キーは右手側にある
f:id:y_sanagi:20190628165028j:plain
くっつけたとき

名称について

"Satsuma"プロジェクトの"JP"配列の"SMD"部品を使ったキーボードです。 元々はJP/ANSI両対応でスルーホール部品を使ったキーボードを作るつもりだった

Satsumaとは英語で温州みかんのことです。

動機

  • 最近まで日本語配列分割キーボードが存在しなかった
    • 今はOtaku SplitNin76があります
    • 真ん中にキーを追加していないのはこれが唯一かな
  • メディアに「組み立てにははんだづけが必要」と書かれるのが癪だった

特徴

  • キーキャップを行を違わずに設置できる
  • スタビライザー対応
  • ベゼルレス
  • トップ・ボトムプレートがアルミ製
  • 薄い
    • ボトムプレートをM2ナットで固定していて外すのに珍しい工具が必要になるため, 販売時は2mm厚くなるかもしれません
      • 3mmのネジを使うことで珍しい工具を必要とすることなくそのままの厚さにしました
  • くっつけるとほぼ一体型60%キーボードとして使える
    • スペースキーの位置に0.5Uの間が空きます
  • ブートローダーをQMK-DFUにしているため, ModemManagerのせいでファームウェアの書き換えに失敗することがない
    • LUFA MassStorageBootloader版も作るかもしれない WSLでもDFUの書き込みができるため作りません
  • 販売する際ははんだづけ不要でキースイッチとキーキャップを付けるだけで使えるようにする
    • キーソケットを付けた後の動作確認が大変なのでソケット未取付での販売が主となります
  • テンキーを接続できる
    • テンキーは未発売です……
  • Space+Aを押した状態でパソコンに接続することでファームウェアの書き換えなしに右下のキーを矢印キーに変更できる

f:id:y_sanagi:20190702111554j:plain
キーキャップ天面まで3cm

不良点

改良するかもしれない

改良予定なし

  • QMK Firmwareにプルリクを出していないため, QMK Configurater等に対応しない
    • コードが雑なのでしません
  • PendantのUSB端子が薄くて接触が甘いため, テープで嵩増ししている
    • 嵩増しはやめました
  • 左手の電源LEDがスタビライザーと干渉するため設置できない
    • 右手の電源LEDで十分なので次期作からは削除されます。
  • スペースキーの部分に0.5Uの間が空く

販売について

遊舎工房レンタルボックスでソケット未取付品1万5千円, 完組品2万円で販売中です!! 限定合計6個です!!

次期作について

色に統一感を出すために基板の色が白色になります。

moduloにするかもしれない

添付文書

Satsuma取扱説明書 - Google ドライブ

連絡先

Twitter: ysni_pub

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