Quantcast
Channel: 仙石浩明の日記
Viewing all 47 articles
Browse latest View live

台灣大哥大のプリペイド SIM のデータパッケージ型 (計量型 NT$180 1GB 30日) プランを日本から Web で購入してみた

$
0
0

台湾のプリペイド SIM カードは、 購入する際パスポートの他に、 もう一点、身分証明書 (日本の運転免許証で OK) が必要。 他の国に比べて SIM 管理が厳しい (誰がどの SIM を購入したか当局が把握していると思われる) ので、 台湾を訪れるたびに SIM を新規に購入して使い捨てていると、 一人で何枚も購入した記録が残ってしまい問題があるかも?

台湾のプリペイド SIM カードは半年間チャージ (儲值, 充值, recharge, topup 等、 いろいろ呼び方はあるが要は SIM の残高を増やすこと) をしないと失効してしまうので、 半年以内に再び台湾を訪れるのでなければ、 日本でチャージを行なう必要がある。

幸い台灣大哥大 (Taiwan Mobile) の場合、 台湾滞在中にアカウントを作っておけば (アカウントが無くても可能かも? 後述)、 そのアカウントでログイン (登入) し、 Web 上でクレジットカード払いでチャージできるので、 私は失効前に (日本から) Web でチャージするようにして SIM を維持し続けている。 SIM を維持していると、 台湾を訪れたとき、 (空港等で) SIM を新規に購入する必要がないので便利。

台灣大哥大 SIM のデータ通信は、 1KB あたり NT$0.045 (約 0.16円) もかかる (100MB 通信すれば 16,000円!) ので、 計日型/計時型 (日単位/時間単位で使い放題) か、 計量型 (30日間有効なデータパッケージ) のいずれかの行動上網 (モバイル インターネット) プランを購入することになる。

計時型計日型上網費率 (時間単位・日単位で使い放題, データ定額)

プラン費用有効期限
1時間型NT$35申込み完了から 1時間
1日型NT$100申込み完了から 24時間
3日型NT$250申込み完了から 72時間
5日型NT$350申込み完了から 120時間
7日型NT$450申込み完了から 168時間
10日型NT$600申込み完了から 240時間
30日型NT$800申込み完了から 720時間

計量型上網費率 (データパッケージ)

プラン費用データ量有効期限
250MBNT$50250MB30日
1GBNT$1801GB30日
2GBNT$3002GB30日
3GBNT$4502GB30日

申込みは (台湾で) ケータイから 535 をダイヤルすることにより可能だが、 Web でもクレジットカード払いで購入できる。 台湾を訪れる前に日本でデータプランを購入しておけば、 飛行機を降りた直後からネットが使える。 計日型だと、 申込み完了直後から 「使い放題」 期間が始まってしまうので、 申込み完了から台湾に到着するまでの期間 (少なくとも約半日間) が無駄になってしまうが、 計量型なら有効期限が 30日間あるので、 台湾に行く前日にでも Web で申し込んでおけばよい。

今回の訪台は 3泊4日 (台湾滞在が 78時間くらい) だったので、 台湾到着直後に 3日型プランを申し込むと、 4日目の途中 (72時間後) から飛行機に乗るまでの数時間、 ネットが使えない時間帯ができてしまう (かといって 5日型だと丸一日以上無駄になる) ので、 計量型 1GB を購入してみることにした。 データ転送量が 1GB に収まるのであれば、 1GB NT$180 なので、 3日型 (NT$250) より安い。

計量型上網費率のページ (首頁資費&手機預付卡產品行動上網介紹計量型) において、 「線上購買」 (オンライン購入) のボタンをクリックする。 残念なことに英語版ページはないので、 中国語のページで購入する必要がある。

「立即儲值/查詢餘額」 (すぐにチャージ/残高照会) のボタン ↓ が下の方に表示されるのでクリック。 ログインしていない場合は、 「請選擇餘額查詢/網路儲值登入方式」 (残高照会/チャージするためのログイン方式を選んでください) と表示され、 「我要登入台灣大會員」 (台灣大哥大の会員としてログインする) か、 「我要快速登入」 (電話番号とパスポート番号でログインする) かを選択する画面が表示される。

24HR 預付卡網路儲值 查餘額・使用期限・網路儲值記録 立即儲值/查詢餘額

前者の場合は、 会員ID (登録したE-mailアドレス) とパスワードを入力してログインする。 後者の場合は、 (台灣大哥大の) 携帯電話番号 (手機號碼) と、 (SIM購入時に登録した) パスポート番号 (身分證號) と、 CAPTCHA (キャプチャ, 驗證碼) を入力する。 後者の場合、 アカウントが無くてもチャージできるのかも? あいにく私はすでにアカウントを作ってしまっているので、 アカウントが無い場合の実験ができない。 どなたか、アカウントを作る前にチャージできるか確認して頂けると幸い。

ログインすると表示される 「選擇商品與付款」 ページにおいて、 「儲值產品」 として 「行動上網」 の 「計量型」 の 「180元 1GB/30天」 を選択。 「付款方式」 として 「信用卡刷卡付款」 (クレジットカード払い) を選択し、 クレジットカード番号 (日本のクレジットカードで OK) と有効期限、 カード裏面の 3桁のセキュリティコード (CVV2/CVC2) を入力。 「Email通知」 に注文受領のメールを受け取りたいメールアドレスを入力。 以上を入力したら 「下一歩」 (次へ) をクリック。

すると、 「請再次確認以下資料是否正確」 (以下の内容が正しいか再度確認してください) と表示されるので、 問題なければ 「確認送出」 をクリック。

以下のような 「儲值成功」 が表示されれば成功。 注文受領のメールが届く。

您已完成線上儲值流程!
訂單編號 	U14017XXXX
門號   	0970-XXX-XXX
儲值產品 	180元(180元 1GB/30天)
付款方式 	信用卡刷卡付款
信用卡卡號	4541-58**-****-XXXX
授權時間 	103年 01月 22日 下午22點 07分
Email通知	sengoku@gmail.com
收據資訊 	二聯式電信收據
收據抬頭/收件人	SENGOKU HIROAKI
電信收據地址	台北市松山區敦化北路100號
儲值結果 	儲值成功

同時に以下のような SMS も届くが、 台灣大哥大のプリペイド SIM カードはローミングに対応していないので、 日本にいる間は受け取ることはできない。 台湾に到着後ケータイの電源を入れたら、 以下のような SMS が届いていた。

From: 0935120867
Date: 2014/1/22 22:07 CST

電子儲值通知_門號0970XXXXXX儲值成功。查詢餘額及效期請手機直撥867;儲值上網產品者請於三十分鐘內收到開通簡訊再重開機即可手機上網

(拙訳) 電話番号 0970XXXXXX に対するチャージが成功しました。 残高および有効期限を参照するには、 ケータイで 867 をダイヤルしてください。 30分以内に開通を知らせる SMS が届きますので、 ケータイを再起動すれば、 すぐインターネットが利用できます。

クレジットカード払いでデータパッケージを購入する場合、 チャージと同じ扱いらしく、 SIM の有効期限が 半年後まで延長されていた。

続いて (14分後)、 以下のような SMS が届いていて、 データパッケージが利用可能になったことを知らせていた。

From: 0935120188
Date: 2014/1/22 22:21 CST

歡迎使用手機上網計量包服務, 手機重新開機即可使用, 服務結束是 2/21 23:59, 查詢餘量請撥*867*300#

(拙訳) ケータイ インターネット データ パッケージ の ご利用ありがとうございます。 ケータイを再起動すれば、 すぐご利用できます。 サービスの有効期限は 2/21 23:59 までです。 残りデータ量を参照するには、 *867*300# をダイヤルしてください。
Balance for the Mobile Internet is about 1017.98MB, and will expire on Feb 21 2014 23:59.

臺灣桃園國際機場 第二航廈 (台湾桃園国際空港 第2ターミナル) に到着 (1/23 11:48 CST) して、 facebook に書込んだり等、 ネットを一通り利用した後、 上記 SMS に書いてあるように 「*867*300#」 をダイヤルすると、 残りデータ量が 1017.98MB と表示された →

台湾ではまだ LTE サービスが始まっていないので 3G (HSPA+) での接続となるが、 ほとんどの場所で下り 10Mbps くらいは余裕で出ていて、 とても快適だった。 が、それ以上にホテルの無料 LAN (有線・無線) が快適で、 ホテルにいるときは Wi-Fi を使っていたので、 台灣大哥大 SIM では 300MB 程度しか使用しなかった (台湾滞在 3日と 6時間)。

テザリングして PC からネット接続するのでなければ、 1GB は一週間未満の短期滞在には充分なデータ量だと思う。 3日型 (NT$250) や 5日型 (NT$350) を購入するより、 1GB (NT$180) を購入するほうが安い。 同様に 10日型 (NT$600) より 2GB (NT$300) のほうが安いわけで、 計日型を利用する理由はほとんど無いのではなかろうか? 計量型だと事前に (出発前日に日本で) 余裕を持って購入しておけるので、 台湾到着時の (入国手続き等で) 忙しいさなかに購入しなくて済む、 というメリットも大きい。


ALS Ice Bucket Challenge

$
0
0

(株)モノタロウの安井卓さんから ALS Ice Bucket Challenge のご指名を頂きました。もろチェーンメールなのがアレですが、同じ阿呆なら踊らな損々のノリで、謹んでお受け致します。残暑厳しき折、ちょうど氷水をかぶりたい心境でした。

次は、立命館大学の上原哲太郎先生、おごちゃん、歌手のファンキーコバ様 (五十音順) のお三方を僭越ながら指名させて頂きます。(私の限られた人脈の中で) なるべく異なる分野の方々へ広げたほうが面白かろうということで畏れながら選ばせて頂きました。

もちろん、チェーンメールが招きがちである悪影響を惹起せしめることは本意ではありませんので、ここでは ALS Ice Bucket Challenge について殊更に述べることは致しませんし (私なんぞがいまさら解説しなくても、みなさまとっくにご存知でしょうし、知らない人はググレカス、おっと失礼)、私が指名した方々に対して、そしてこの拙文を読んで下さる全ての読者の皆様に対して、いかなる行動についても強要ないし推奨する意図は 1ミリも御座いません。

いわんや、指名した方々がさらに次の人を指名してこの ALS Ice Bucket Challenge を拡散することをや、です。どんなに大義が大きくても足元が危うくなっては本末転倒ですし、大義が大きければ大きいほど、正義を振りかざせば振りかざすほどに、抑制が効かなくなって暴走しがちというのもまた現実でしょう。 いいか、絶対に拡散すんなよ。約束だぞ。

ALS Ice Bucket Challenge が、これだけ世間の注目を集めてしまいますと、賛否両論かまびすしくなるのは必然なわけでして、まあ他人様のバイラル・マーケティングの大成功が妬ましくなるのは重々分かるんですが、外で見ていてあれこれ言うより、踊る阿呆として楽しんじゃったほうが精神衛生上も世界平和のためにもいいんじゃないでしょうか? 同調圧力を吹き飛ばすには、笑い飛ばすことこそが重要と思う次第です。

で、実際にやってみると、コレすごく大変です (>_<)。 こんな短い動画でも見るのと作るのとでは大違い、という当たり前すぎることを痛感しました。 特に、氷水をかぶるのは不可逆過程ですから練習するわけにもいかず、一発勝負に...

なにごともやってみることは重要ですね。

お金と自由 〜 なぜ貯められないのか?

$
0
0

先日、 立命館大学で 2, 3回生の学生さん達に講義する機会を頂きました。 せっかくの機会なので、 大学ではまず教わることが無いであろう 「お金」 の話をしました。 学生さん達が社会人になる前に、 是非ともお金について考えてみてもらいたいと常々思っていたからです。

お金も時間も足らないはずがない
(中略)
何でお金がなくなるんだろうって、そっちも不思議なんですよ。 日立ってそんな高い給料じゃないと思うんですけど、 入社当時、お金ってそんなに必要じゃなかったから、 あの安月給でもお金が貯まって仕方なかったんです。 お金が貯まらないって人は、 好きじゃないことにお金を使っているんじゃないの? って思いますね。
例えばですね、 すごく不思議だったから今でも覚えているんですけど、 同期入社の人が、入社早々、みんな車を新車で買ったんですよ。 もちろん好きだったら、買ってもいいと思いますよ。 レーサーを目指しているとか、 峠を攻めたいとかっていう人は買ったらいいと思うんです。
でもそんなに好きでもなくて、たまにしか車を使わないような人でも、 みんな車を買ったんで、不思議だなと思いましたね。 私に言わせるとありえないんですけど。 みなさん、好きでもないことにばっかりお金を使っているんじゃないですかね。

自由な時間は学生さんの方が持っていて、 お金は社会人の方が持っているという違いがありますが、 どちらも無駄遣いする習慣を身につけてしまうと、 一生取り返しがつかないように思うので、 ぜひとも人生これからの学生さん達に伝えたいと思っていました。

多くの人が、 学生時代は月に 10万円以下の生活費で暮らしていたにもかかわらず、 社会人になって急に毎月 20万円以上の 「大金」 を手にし、 いままでやらなかった (できなかった) 消費活動に走り、 お金があればあるだけ使ってしまうという悪い習慣に、 あっと言う間に染まってしまいます。 ひとたびこの悪習に取り憑かれると、 給料が上がっても、 上がったぶんだけ消費が増え、 いつになっても金銭面での余裕ができず、 何をするにもお金がネックになって、 お金に振り回される人生を送ってしまいます。 まさに 「お金は悪い主人である」 の典型ですね。

良い召使い or 悪い主人

・ 良い召使い
  - お金は自由を得るための手段
  - お金をどう使うか主体的に考える
  - チャレンジするための強力な道具
・ 悪い主人 (充分なお金が無いと...)
  - お金は生活の手段
  - 使い方を考えるまでもなく出費がかさむ
  - お金のために働く, お金に振り回される
  - お金を失う恐怖で、チャレンジができない
  - お金の不安がストレスに

日頃ストレスを感じると回答した人に、 その理由を尋ねると、 約 40% の人が 「収入や家計に関すること」 と回答しているそうです。 ストレスの原因としては他に、 「人間関係」 「健康状態」 「子育て」 なども挙げられていますが、 例えば、 職場の人間関係にストレスを感じていても辞められないのは、 転職したくても収入が不安定になるのが怖くて踏みきれないことが多かったりするわけで、 様々なストレスの遠因がお金 (が足らないこと) にあると言っても過言ではありません。

当座の生活に困らない程度の充分な蓄えさえあれば、 ストレスの多くは感じずに済み、 お金に振り回されるのではなく、 お金を 「良い召使い」 として使えるようになります。

そんなこと言ったって、 安月給なんだから貯められないのは仕方がない、 って声が聞こえてきそうですが...

充分なお金ってどのくらい?

・ 年収 300万円なら年 100万円以上貯金
  - 独身なら生活費は月 10万円程度で済むはず
・ 30歳までに 500万円
  - 100万円/年 * 5年
・ 40歳までに 4000万円
  - 350万円/年 * 10年
  - 年収 700万円以上あれば誰にでも貯められる
・ もちろん、やりたいことにはお金を使うべき
  - でも、昨今はお金をかけずに何でもできてしまう

独身で扶養家族を持たない人であれば、 学生時代と同程度の生活費で暮らすことが本来は可能なはずです。 しかも学費を払う必要がないのですから、 正社員であれば初年度においても 100万円以上貯めることは簡単なはずです。 その後、昇給もあるでしょうから、 30歳までに 500万円貯めるのは (正社員であれば) 誰にでもできることでしょう。

私は日立の安月給で 30歳までに 1000万円ほど貯めました。 寮費が月 2000円、 朝夕食は寮の食堂で昼食は社内食堂で食べ、 会社にいないときは寮に併設のコートでテニスをしている、 というほとんどお金を使わない生活をしていたからですが (^^)。

なお、 「お金を貯める」 「貯金」 は、 普通は銀行預金のことを意味しますが、 ここでは貯金してまとまった資産をつくり、 それを元手に投資することまで含むものとします。

その後、 給料が増えても生活レベルをあまり上げずに生活費を年間 200万円程度に抑えれば、 増えたぶんは丸々貯金できるはずです。 もし年収が 700万円以上あれば、 40歳までに 4000万円貯めることも決して難しくないはずです (私は 1億円近く貯めました ^^)。 貯金が 4000万円もあれば、 どれだけ生活にゆとりができるでしょうか。 ストレスの多くは感じずに済むのではないでしょうか。

ところが、 この程度の貯金すら、 ほとんどの人にとっては難しいようです。

なぜ貯められない?

・ お金に関する間違った常識
  - 持ち家は一生の財産 (ローンなら負債)
  - 生命保険は社会人の常識
  - お金は使ってなんぼ (うまく投資できればの話)
  - 「お金に働いてもらう」信仰 (貯めるのが先)
・ お金に余裕があると使ってしまう習慣
  - お金を使うことに慣れてしまう
  - 初任給から一ヶ月間が運命の分かれ道
・ 貯めるメリットが分かってない
  - そんなに貯めて何に使うの?

多くの人は貯金するどころか逆にローンという莫大な借金を抱えてしまいます。 家賃を払う人には安い家に引っ越して負担を減らす自由がありますが、 ローンを抱えている人には負担を軽減する自由はありません。 したがって、 「購入 VS 賃貸」 という比較はナンセンスです。 それなのに、 なぜ家賃を払うより 「お得」 と称して高額のローンを組ませるのでしょうか? もちろん、 銀行が儲けるためです。

扶養家族を持たない新卒社員に、 なぜ生命保険が必要なのでしょうか? 高額療養費制度により高額な医療費を支払ったときは払い戻しが受けられるのに、 なぜ医療保険が必要なのでしょうか? もちろん、 保険会社が儲けるためです。

銀行や保険会社以外も、 「お金は使ってこそ価値がある」 「若いときこそお金は自己投資すべき」 などと、 あの手この手でお金を使わせようとします。 でも、 まだ何の経験もない新卒社員に、 お金の有効な使い方が分かれば苦労はしません。 新卒社員にお金を使わせようと躍起になっている人たちが、 真に有効なお金の使い方を新卒社員に教えるでしょうか? もちろん彼らが教えるわけはなく、 教える義理もありません。 彼らが関心があるのは彼ら自身の儲けだけです。

お金を使わせようとする人以外にも、 「お金に働いてもらおう」 「投資に成功する方法」 など、 「儲かる」投資方法を伝授しようとする人もたくさんいます。 彼らがもし真に儲かる投資方法を知っているなら、 わざわざ他人に教えるのではなく自分で大儲けすればいいのにと思いますが、 わざわざ本を書いたり講演したりして、 わずかばかりの印税や講演料を稼いでいるわけです。 変だと思いませんか?

そういう本や講演にいちいちお金を使っていては彼らの思うツボです。 お金を払う前に、 著者あるいは講演者がどれほどの個人資産を持っているというのか、 調べてみるべきでしょう。 決して、 お金が勝手に働いてくれるわけではありません。 自分の頭で考えて汗をかいて有効な投資先を見つけ出し、 あるいは自分で創り出し、 リスクを取って投資し、 その報酬としてリターンを得るのです。

お金はいったん使ってしまうと歯止めが効きません。 使えば使うほど、 財布の紐は緩くなります。 近頃流行りのソーシャルゲームは、 いかにユーザに最初の 100円を払ってもらうかが腕の見せどころです。 いったん 100円を払ってしまったユーザは、 次の 1000円を払うのに躊躇しません。

世界中の大人達が寄ってたかってウブな若者にお金を使わせようと知恵を絞る中、 お金を使うのを我慢するのは至難の業ですが、 さらに周囲の同僚からも、 「ケチ」 だの 「そんなに貯めてどうするんだ?」 などと、 冷たい視線を浴びることになります。 ほとんどの人が、 お金を貯めるメリットを理解していないのだから仕方ありません。

お金を貯めるメリット?

・ お金を「良い召使い」として使える
  - お金 (生活) のためでなく自己実現のために働ける
  - お金の不安という最悪のストレスから解放される
・ お金 (定期収入) を失う恐怖を克服できる
  - 転職に踏み切れる, 何度でも勝負できる
・ 資本家と対等の立場に立てる
  - 自身の労働を売らない自由 (給料交渉では超重要)
階層の壁を越えるチャンス
  - (詐欺ではない本物の) うまい投資話に乗れる
  - まずは最低でも 1000万円。それ以下では相手にされない

労働者は自身の労働力を売らない自由が無い (辞めると生活できなくなる) ばっかりに、 資本家 (会社) との交渉において立場が弱くなってしまい、 (たとえ労働三法の庇護の下でも) 不利な条件を渋々飲まされるわけで、 「辞める自由」ってのは非常に重要だと思います。

また、 お金 (定期収入) を失う恐怖を克服して、 勝負する自由や、 お金を (生活のためでなく) 自己実現のために使う自由も重要でしょう。 ところが、私が 「お金を貯める最大のメリットは、 自由が得られること」 と話しても、 学生さん達にはイマイチ響かなかったように見えました。

お金を貯めるメリットが分からないというよりは、 自由の価値が分からない、 ということなのかも? と講義を終えてから思い当たりました。 (もし次の機会があれば) 今度はお金だけでなく自由の重要性についてもっと話してみようと思います。

(資本主義における) 給料が 「労働の再生産コスト」 に収斂するのは、 労働者に自由がないからであって、 もし労働者が自由を獲得すれば、 労働者が実際に産み出した 「(使用)価値」 に近いところまで給料が上がることが期待できます。 「労働の再生産コスト」 と同程度の給料でできるのは現状維持だけですが、 それより給料が高ければ 「剰余価値」 が労働者の側に蓄積し、 労働者階級から脱出する道が見えてきます。

階級格差は固定されたものではなく、 一労働者からスタートしても、 階層の壁を越えて資本家側に移ることは充分現実的であり、 お金を貯めれば貯めるほど、 その難度は下がります。 お金を貯めて 「壁越え」 に挑戦する人が増えることを願って止みません。 それこそが閉塞感を打ち破り、 活気に満ちた社会を取り戻す唯一の方法だと思うからです。

成功者を妬んで引きずり降ろそうと足の引っ張り合いをするのではなく、 成功者を正当に評価して後に続く人を増やすことこそが、 真の 「トリクルダウン」 (trickle-down) であり、 社会の成長エンジンになりうるのだと思います。

- o -

10/15 追記: 30代で年収 700万円以上もらえる人がどれだけいるのか? というご意見をいただきました。 仕事を任せられる 30代なら、 ほとんどの企業で 700万円くらいは出すと思います (少なくとも私が経営者なら喜んで出します)。

したがって、 700万円未満の給料しかないのは、 (1) 使用価値が 700万円を下回っているか、 (2) 給料交渉をしていないため給料が 「労働の再生産コスト」 程度に抑えられてしまっているか、 のどちらかでしょう。

(2) の場合は、 ある程度の蓄えがあるなら 「辞める自由」 をちらつかせて給料交渉すべきですし、 あくまで昇給を拒否するなら転職するまでです。 主体的に動ける人ならば引く手あまたなので。

30代になったときに (1) の場合となってしまうのを避けたいのであれば、 20代のうちに自分の能力以上の仕事に果敢にチャレンジして失敗を数多く経験し、 主体的に動けるようになるべきかと。

シンガポール SingTel 3G プリペイド SIM を 4G へアップグレードしてみた

$
0
0
SingTel 3G Prepaid SIM

2012年2月にシンガポールで買ったプリペイド SIM カード
当時シンガポールでは LTE (4G) サービスが始まっていなかったが、 その後 2013年に SingTel と M1 は、 プリペイドでも LTE が使えるようになった。 ところが、 この SingTel の古い 3G プリペイド SIM カードでは LTE を利用することができず、 4G プリペイド SIM へアップグレードする必要がある。

もちろん、 わざわざアップグレードしなくても、 コンビニ等で新規に 4G プリペイド SIM カードを買えばよいのだが、 私の場合この古い 3G プリペイド SIM カードの残高が S$140 (約 12,000円) ほどあったのと、 この SIM の番号へ日本から電話連絡してもらう予定があったのとで、 古い SIM を使い続ける必要があった。

元々 S$28 のプリペイド SIM なのに、 なぜ S$140 も残高があるかというと、 この SIM をはじめほとんどのプリペイド SIM は、 一定期間以上チャージしないと無効になってしまうから。 この SIM の場合 S$20 チャージすると、 その時点から 180日間有効なので、 半年ごとに S$20 チャージし続けると、 5回チャージする (計 S$100) ことで、 購入から 2年半以上経った現在でも (同じ電話番号のままで) 使い続けることができている。

街中 (というかショッピングモール内) を歩いていて見かけた SingTel shop で、 4G SIM へのアップグレードの方法を店員に尋ねたら、 アップグレードは、 SingTel shop ComCentre (31 Exeter Road) でのみ可能とのこと。 Google Maps で調べると、 MRT (地下鉄) の Somerset 駅 (NS23) の南側に聳え立つ ComCentre (SingTel の本社ビル) の隣らしい。 近くなので行ってみた。

店内に入ると、 順番を待つ客でごった返していた。 (順番待ちの) 発券機があったので画面にタッチすると、 「Enter your NRIC/FIN」 と表示され、 番号を入力しないと先に進めない。 たかが順番待ちに、 NRIC (National Registration Identity Card) や FIN (Foreign Identification Number) を入力させるとは、 さすが管理国家 (>_<)。 もちろん一観光客 (ビザ無し) である私はどちらも持っていない。

通りがかったスタッフに 「4G SIM へアップグレードしたいんだけど?」 と (もちろん英語で) 聞くと、 「今日は特別な日 (たまたまこの日は Deepavali というヒンドゥー教のお正月) だから激混みで 2時間待ちだけどそれでもいいのか?」 と脅される。 待つなら発券機で番号札を取れと言うので、 「私は resident じゃないので NRIC を持ってない」 と言うと、 ポストペイ SIM かプリペイド SIM か聞いてきて、 「プリペイド SIM なら、向こうの Cashier へ行け」 と言われた。

SingTel ComCentre Cashier

へ? Cashier ? 順番待ちせずにアップグレードできるということなのかなぁ? Cashier って、 ケータイケースとかの、 お金を出すだけですぐ買えるような単純なものを買うところのように見えるんだけど... (後ろにケータイケースとか並べてるし)

Cashier 前に並んでいる人たちは、 ケータイ用のアクセサリ等を買うために並んでいるか、 あるいは既に相談カウンターで手続きが済んでいて、 お金を払う最終段階で Cashier に並んでいるように見える。 Cashier でアップグレードの手続きができるとは、 ちょっと信じがたい... それに、 Cashier には 10人ぐらい並んでいて、 Cashier といえどそこそこ待つ必要があるように見える (実際 10分かかった)。 待った挙げ句、 やっぱり番号札を取って順番を待てという結末になったら嫌だなぁと思いつつ、 ダメ元で並んでみた。

で、 順番が回ってきて、 Cashier のレジ係に 「4G へアップグレードしたいんだけど...」 と伝えると、 レジ係がメモ紙を出して、 「ケータイの電話番号を書け」 と言う。 お、 本当にここでアップグレードできるのか? いま使ってる 3G SIM の 8桁の電話番号を渡された紙に書くと、 レジ係が 「Mr. Hiroaki Sengoku, パスポートはいま持ってるか?」 と聞く。

う、 プリペイド SIM の電話番号だけで名前が分かるのか! と驚いたけど、 よく考えたら SIM を買ったとき (2012年) にパスポートを提示したのだから当たり前。 でも、 レジ係が簡単に SIM 使用者の身元を照会できてしまうのはいかがなものか? もし私が (間違えたフリして) 他人の番号を書いたら、 使用者の名前を聞けてしまう? 管理国家とはこういうことなのか?

パスポートを見せると、 「S$38 をチャージする形になるが構わないか?」 と聞かれた。 つまり、 いま S$38 (約 3200円) を払う必要があるが、 それはプリペイド SIM にチャージされ、 通話やデータ通信する料金として使うことができて、 アップグレード自体は無料ということ。 「もちろん」 と答えて、 S$38 を (クレジットカードで) 支払う。 すると、 間髪をいれず手元の Nexus5 (3G SIM が入ってる) に、 チャージ完了を知らせる SMS が届く:

From: 74445
Date: 2014-10-22 13:32

You have successfully topped up $38.00. New MAIN bal: $152.32, Expiry: 20/04/2015.Visit www.singtel.com/hi (no data charges!) or dial *363 to buy data plans or transfer airtime locally/overseas via hi!Share using your MAIN account bal.

新しい 4G SIM の代金として払った S$38 なのに、 アップグレードする前の古い 3G SIM に S$38 がチャージされるのは、 ちょっと変な感じ。

SingTel ComCentre Cashier

そして遂に、 新しい 4G プリペイド SIM カードをゲット。 Cashier の順番が回ってきてからここまで 3分もたっていない。 街中で新規に S$38 のプリペイド SIM を買うのとほとんど同じ。 古い 3G SIM の残高と電話番号を引き継げる。 新しい SIM はケータイ網に登録されるまで 15分ほどかかるとのこと。

店内の椅子に腰掛けて、 Nexus5 から古い 3G SIM を取り出す。 そして、 いま買ったばかりの 4G SIM を入れると、 まだ登録が済んでいないらしく圏外表示。 15分待ってもアンテナ表示が立たないので、 Nexus5 を再起動したらアンテナと 「LTE」 の文字が表示された。 ちなみに古い 3G SIM の返却は求められなかったし、 Cashier では Nexus5 に入れたままだったので提示すらしていない。

シンガポールは、 一人 3枚までしかプリペイド SIM を持てず、 一つのパスポートで 3枚のプリペイド SIM を買ってしまうと、 たとえ紛失したとしても 3枚の SIM が有効である限り、 それ以上 SIM を買うことはできないと聞く。 しかしながら、 4G へのアップグレードの際に古い SIM を持参する必要が無かったことを考えると、 例えば SIM のサイズを nano に変更するなどの理由があれば (無くても?)、 新しい SIM を 3枚制限の枠内で発行してもらうことが可能なのではないか? 紛失して有効期限満了まで待てない時など、 試してみる価値はありそう。

なお、 新しい 4G プリペイド SIM カード表面に、 「FREE 1GB DATA」 と書いてある通り、 7日間 1GB までのデータ通信が無料になるが、 私の場合は古い 3G SIM において既に 「7-Day $25 Ultimate 14GB Plan」 を購入済みだったので、 この 「FREE 1GB DATA」 は使われることなく無駄になってしまった (1週間以内の短期滞在なので)。 「7-Day $25 Ultimate 14GB Plan」 の 7日間が過ぎるか、 あるいは 14GB を使いきれば、 「FREE 1GB DATA」 も使えるのだろう。

米国 BYOD プリペイド SIM の GoSmart を使ってみた 〜 $35 で 30日間 かけ放題、4G データ 2.5GB 〜

$
0
0

私は米国 AT&T のプリペイド SIM を (毎年 refill して) 維持している。 日本に居るときは料金プランを月額料金が無料で従量制の 「10¢ / minute」プランにして残高が減るのを防ぎ、 訪米する直前に 「$60 Monthly」プランに切り替えて、 $60 で 30日間 4GB までのデータ通信ができるようにしている (通話・SMS は無制限)。

ところが今回初めて 30日間を超えて 35日間米国に滞在したので、 最後の 5日間ほどデータ通信ができなくなってしまった (通話は 「10¢ / minute」プランでもできる)。 もちろん、 二ヶ月続けて 「$60 Monthly」 を契約すればデータ通信できるのだけど、 たった 5日間のために $60 も払うのはモッタイナイ。 そこで、 最近米国で流行っている BYOD (Bring Your Own Device) SIM を買ってみることにした。

BYOD SIM を買ってから気付いたが、 訪米の 20日ほど前に AT&T の 「$45 Monthly」プラン (1.5GB までのデータ通信が可能) を契約し米国滞在中に (一度だけ) 自動更新すれば、 未使用データ通信量は一度だけ Rollover されるので、 二ヶ月分の計 3GB が利用できて、 二ヶ月分の料金 $90 で済む。
BYOD SIM で 500MB 以上のデータ通信を行なおうとすると月単位の料金プランを契約する必要があって、 最低でも (SIM 代金と月額料金の合計で) $40 くらいはかかるので、 AT&T 「$60 Monthly」 + BYOD で合計 $100 になってしまう。 AT&T 「$45 Monthly」 + BYOD なら合計 $85 だが、 1.5GB + BYOD だとちょっと心許無く、 $5 の差なら AT&T の 「$45 Monthly」* 2ヶ月 3GB のほうが好ましい。
とはいえ、 一度 BYOD SIM を使ってみたかったということで... (^^;)

(注) 後述するように BYOD SIM は今まさに価格競争が起きている。 近いうちに (現時点ですでに?) 結論は変わってくるかもしれない。 すなわち、プリペイド SIM は refill して維持するよりも、 訪米ごとに使い捨てたほうがいい、ということになってしまうかも? (もちろん、訪米頻度に依存する)

日本では BYOD は、 「企業における私物端末の持ち込み」 という意味に限定されて使われているが、 米国だと持ち込み先は 「企業」 に限らず、 「年間契約が不要なプリペイド・プランへ自分の端末を持ち込む」 という意味にも使われているらしい。

もちろん米国では何年も前から、 プリペイド SIM を単体で購入し、 (日本から持ち込んだ) SIM ロック・フリー スマホに入れて使うことはできた。 でも、 AT&T や T-Mobile などのショップで (英語を喋って) 購入する必要があり、 英語がニガテだとハードルが高い。 しかも ID (身分証明) が必要 (私の場合、 米国発行のクレジットカードを提示したらそれ以上は求められなかったが、 適当なものが無ければパスポートが必要かも?)。

それが昨今の BYOD SIM だと、 スーパーマーケットで日用品を買うのと同じような感覚で買えてしまう。 英語を喋る必要もなく、 単に商品棚から SIM を手にとって買物カゴに放り込み、 レジでお金を払うだけ。 まさにバナナを買うのと同じ感覚でプリペイド SIM が買えてしまう。 従来のプリペイド SIM のように refill して維持してもいいが、 これだけ手軽に買えるなら訪米ごとに使い捨てでもいい。

ただし、 Walmart などの (万引き防止策に熱心な?) スーパーマーケットだと、 (万引きされやすい) 一部の商品は什器 (商品棚) に固定されていて、 店員に (英語で) 頼まないと手に取ることすらできない。 気軽に商品を手に取れてこそのスーパーマーケットだと思うのだけど、 商品を引っくり返して裏面の説明を読むためだけに、 わざわざ店員を呼ぶのはいかにも億劫。 お客の買う気を減退させてどうする?と思うのだけど、 それだけ万引き被害が甚大ということなのかなぁ? そもそも SIM などの商品はギフトカードとかと同様、 レジで有効化しないと使用できないはずなので、 万引きの対象にならないんじゃなかったのか? とか思ったり (SIM にも 「Not active until scanned at register」 と書いてある)。

BYOD SIMs at Target

幸い、私が GoSmart BYOD SIM を買った TARGET (パッケージには $39.95 と書いてあるのに、 なぜか 4セント高い値段で売っていた) では、 どの SIM も自由に手にとって見ることができたので、 裏面の説明をじっくり読んで、 比較検討してから買うことができた (もちろん、TARGET で扱ってる SIM だけでなく、 Longs Drugs など他の店で扱ってる SIM も比較検討した)。

とはいえ、 裏面の説明には肝心なことが書いてない。 買って本当に誰でも (非居住者でも) SIM の activate 手続きを WWW だけで完結できるのか? activate 手続きの途中で身分証明 (SSN の入力とか) や (米国発行の) クレジットカードの提示を求められたりしないのか? 追加費用が発生したりしないのか? activate するには使用するケータイの IMEI を登録する必要があると聞くけど、 その登録したケータイ以外のケータイでも使えるのか? など不安の種は尽きない (→ 全部杞憂だった, 後述)。 まあ SIM 自体は $5 もしないので ($39.95 のパッケージに $35 の refill カードが含まれているので、 SIM 単体だと差し引き $4.95)、 ダメでもいいやくらいの気持ちで買ってみた。

GoSmart というのは T-Mobile の低価格プリペイド用ブランド。 速度を 3G に抑えることで料金を安くしているらしい。 この SIM は、 activate してから 30日間、 2.5GB まで 3G 速度でデータ通信できる。 私のケータイは日本で買った北米以外の地域向けの Nexus5 (LG-D821) で、 どのみち T-Mobile では 4G 通信できないので 3G で充分。

パッケージ表面に 「UNLIMITED 4G LTE FACEBOOK ACCESS, AT NO EXTRA CHARGE」 と書いてある。 LTE で接続しているときも 3G 速度に抑えるが、 facebook をアクセスするときは速度を制限しない、ということ? 3G で接続したときはどうなるんだろう? (→ 速くなった, 後述)

GoSmart SIM は、 TARGET の他、 ドンキホーテや街中のケータイ・ショップなどでも売っていて、 最近販売に力を入れ始めているように見える。 なお、 $35/月プランで 2.5GB のデータ通信が可能になったのは今年の一月末からで、 以前は 500MB だったらしい。

開封して説明書を読むと、 SIM の activate に必要なのは以下の 6点:

  • SIM Card Serial #:
  • Phone Serial # / IMEI #:
  • Area Code for Your Phone Number:
  • ZIP Code:
  • Pick a Security PIN (4 digits):
  • Refill Code:

「SIM Card Serial #」 は ICCID (IC Card IDentifier) のこと。 同梱されている SIM の表面に印字してある 19桁の数字。 「ICCD は最長 19桁」 だとする解説が日本語で書かれた WWW ページに多いが AT&T の SIM は 20桁だし、 正しくは最長 22桁らしい。

「Phone Serial # / IMEI #」 はケータイで 「*#06#」 をダイヤルすれば表示される。 どのケータイの IMEI (International Mobile Equipment Identity) を入力すべきか悩ましいところ。 私の常用スマホ Nexus5 には (この時点では) AT&T SIM が入っていたので、 それをいったん抜いて GoSmart SIM と入れ替えるのはメンドクサイ。 とりあえず遊んでる予備機 Galaxy Mega 5.8 Duos に GoSmart SIM を入れて Galaxy Mega の IMEI を登録したいところなのだけど、 もしここで登録した IMEI のケータイと GoSmart SIM が紐づいてしまって、 そのケータイ以外では使えなくなってしまうのだとしたら、 AT&T SIM の 「$60 Monthly」 プランの期限が切れた後の 5日間、 Nexus5 で通信できなくて Galaxy Mega の使用を強いられるのは困る。

まあ、 注意書きも無いのに勝手に紐づくなんてことは無いだろうと思いつつも、 万一に備えて (一時的に) Nexus5 の SIM を GoSmart に入れ替えることにした (AT&T SIM は Galaxy Mega へ)。

「Area Code for Your Phone Number」 で好きな地域の電話番号の局番を入力すれば、 その地域の電話番号を割当ててもらえる、 ということなのだろう。 ハワイ州なら 「808」。 日本だと携帯電話の電話番号は 「090」や「080」等で、 固定電話の局番とは別になっているが、 米国だと固定電話と同じ局番になる。

なぜここで 「ZIP Code」(郵便番号) を登録する必要があるのか分からないのだけど、 「Please enter your ZIP code so we can determine what area codes are available」 と言ってくるので、 入力した ZIP の地域の局番しか選べないのかもしれない。 素直に泊っているホテルの ZIP を入力したので、 別の州の ZIP を入力するとどうなってたかは不明。

「PIN」 は適当な 4桁の暗証番号を登録すれば良い。 カスタマーサポートに電話するとき、 本人認証するために用いるらしい。

GoSmart Refill Card

「Refill Code」 は同梱されている Refill カードの下方のマスク部分を削ると出てくる 10桁の数字。 ちなみに、 この 「マスク部分」 を 「スクラッチ」 と呼んでる WWW ページをやたら目にするのだけど、 どこからそんな誤用が広まったのだろう? 「スクラッチを削る」 って文を見ると頭痛が痛い (>_<)。

以上 6点の準備ができたので、 www.gosmartmobile.com をアクセスして、 「ACTIVATE ACCOUNT」 をクリック。 あとは準備した 6点を入力していくだけ。 名前や住所も聞かれるが入力が必須な項目ではない。 ところが、 以下 2点が必須入力項目:

  • Email
  • Alternate phone number

必須なら、 説明書に書いておくべきだと思うのだけど... 「Email」 は、(誰でも持っているだろうから) まあ良いとして、 「Alternate phone number」 は日本のケータイ番号でも構わないのだろうか? 私は Google Voice の番号を持っているので、 それを入力したのだけど、 GoSmart が米国での最初の電話番号になる人はどうすればよいのだろう? ここで入力した番号に電話がかかってくる様子もないので、 ホテルの電話番号でも入力しておけば良い?

説明書に、 「When asked to select a plan YOU MUST CHOOSE THE $35 UNLIMITED TALK, TEXT & HIGH-SPEED WEB PLAN without any additional features.」 と書いてあるので、 「Unlimited Talk, Text & Web + up to 2.5GB of 3G Web」プランを選ぶ。 WWW ページの文字列を説明書と一致させておいたほうが良いのではないかと思ったり...

最後に 「Refill Code」 を入力して無事 activate 完了。 さっそく SIM を Nexus5 へ入れてみる。 APN は 「fast.t-mobile.com」 のままでデータ通信できるみたい。 でも、びっくりするくらい遅い。 測ってみると 1Mbps も出ていない。 こんなんで (AT&T SIM の 「$60 Monthly」 プランの期限が切れた後の) 5日間を耐えられるのかなぁ? 同じ 3G でも AT&T SIM なら (同じ Nexus5 で) 5Mbps 以上出るのに。 ただ、 看板通り facebook へのアクセスだけは速い。 「UNLIMITED 4G LTE FACEBOOK ACCESS」 というのは LTE じゃない 3G 接続時も適用されるらしい。

ところが実際に使い始めてみると、 意外なほど遅さは気にならなかった。 画像など大量のデータを送受信するのは、 実は facebook がほとんどだった、ということなのか...orz ただし、 AT&T と比べて 3G が圏外になる頻度が高いのは、 (GoSmart に限らず) T-Mobile の不便なところ。 ちょっと郊外へ出かけるとすぐ 2G になるのはもちろん、 市街地でも建物の奥に入ると電波が弱くなり、 2G に落ちることもしばしば。

BYOD SIMs at Longs Drugs

データ通信量が 250MB 程度で充分であれば、 LycaMobile や ultra mobile の BYOD SIM が $34 で売っていた。 いずれも T-Mobile の MVNO で、 $5 相当の SIM (Standard/micro 両対応 SIM) と、 $29 の月額料金がセットになっている。

500MB くらい使えるのであれば検討したが、 250MB というのは (5日間だけとはいえ) いかにも少ない。 しかも ultra mobile の $29/月プランのデータ通信は無制限ではない。 つまり他の SIM はデータ通信量の上限に達しても、 2G 速度で (遅すぎて実用的ではないかもしれないが) 通信できるのに対し、 ultra mobile の $29/月プランは上限に達したらデータ通信できなくなる。

T-Mobile が $35/月で 2.5GB まで使えるのだから、 T-Mobile の MVNO にはもうちょっと頑張ってもらいたいところ... と思って検討対象から外した (9/4 時点) のだが、 いま (10/5) WWW で確認したら、 ultra mobile の $29/月プランは 1GB まで LTE 速度で、 LycaMobile の $29/月プランは 500MB まで LTE 速度で、 それぞれデータ通信できるようになったらしい (^O^)。 SIM パッケージの印刷が間に合ってないということか? 1GB まで使えるなら、 ultra mobile で良かったのかも。(^^;)

前述したように T-Mobile (とその MVNO) は、 AT&T と比べてサービス・エリアが見劣りするので、 AT&T の MVNO の BYOD SIM を探していたのだが、 T-Mobile 系は SIM 本体の値段が $5 程度であるのに対し、 AT&T 系は SIM 本体だけで $10 以上するものがほとんどだった。 AT&T 系は AT&T の SIM だけでなく、 T-Mobile や CDMA (Verizon とSprint) の SIM も同梱されている (つまり 2枚ないし 3枚の SIM が入っている) ので値段が高いのだと思われる。 ちなみに NET10 の BYOD SIM は AT&T, T-Mobile, CDMA それぞれについて Nano SIM も入っているので、 全部で 6枚もの SIM が入っている。 しかもどれか一つしか使えないのではなく AT&T と T-Mobile と CDMA それぞれ独立に使える (3人で使っても良い)。

写真右下に映ってる Red Pocket $34.99 は AT&T の SIM と T-Mobile の 2枚の SIM が入っていると書いてあるが、 $29.99/月の Unlimited TALK & TEXT プランが付いているので、 SIM 部分は $5 のようだ。 無制限データ通信は含まれていないが、 「BONUS! 100MB WEB」 と書いてあるので 100MB までならデータ通信できるらしい。 ただし ultra mobile の $29/月プランと同様、 無制限ではないので上限に達したらデータ通信できなくなる。

100MB ではいくらなんでも少なすぎると思って検討対象から外したのだが、 これも今 (10/5) WWW で確認したら、 500MB に増量されている! (@_@) なんてことだ。 BYOD SIM はパッケージの印刷が間に合わないくらい急激に価格競争が起きているのかも。 間に合わないなら間に合わないなりに、 シール貼り等で対応すればいいのにと思うが。

自分戦略 〜 なぜ成功できないのか?

$
0
0

昨年に続き、 今年も立命館大学で 2, 3回生の学生さん達に講義する機会を頂きました。 就職を目前にした学生さんが自分の進路を考えるにあたって、 社会人の話を聞いて参考にしようという趣旨のカリキュラムのようです。 私は 8人目、 ちょうど真ん中あたりだそうです。

「社会人の話」 というと実際にどのような仕事をしているかとかの、 会社視点の話が多くなりがちだと思いますし、 学生さん達もそういう話を期待していると思うのですが、 あいにく私は現在は働いていません (^^;)。 会社を辞めたのは 2011年ですが、 それまでの 11年間も取締役だったので、 いわゆるサラリーマン的な働きかたをしていたのは 15年以上も昔 (前世紀!) の話で、 すでに忘却の彼方です。

こんな状況で仕事の話はできるわけはなく、 ならばいっそと、 会社視点の話はヤメにして会社と対峙する一個人の視点からお話ししました。 会社で研究者・技術者としてバリバリ専門的な仕事をしている人たちの話とは毛色が違いすぎて、 戸惑っている学生さん達も多かったようですが (40代で働いていない、 というのは学生さんにとってそれなりのインパクトがあるようです)、 学生さん達がこれからの人生を考える上で多少なりとも参考になれば幸いです。

これから社会人になろうとする学生さん達がまずやるのは 「自分探し」 「自己分析」 で、 さらに自分のこれからの人生を切り拓くための戦略を考えたりするそうです。 「自分戦略」 をググると、 「自分のやりたいことを明確にし、それを実現するための手段を練ること」 などと出てきます。

私が学生の時、 自己分析したり戦略を練ったりしてたかなぁ? ぜんぜん思い出せません。 コンピュータにのめり込んでコンピュータを自作してしまったり、 授業そっちのけでソフトウェア開発のアルバイトに没頭したりした記憶はあるのですが、 将来のことなど考えたことは無かったように思います。

私が新卒で就職したのはバブル崩壊真っ只中の 1992年です。 就職氷河期 (1993年〜) に入る直前にギリギリ滑り込みました。 その後の世代と比べれば恵まれた時代だったのでしょう。 とはいえ、 就職でラクをしたぶん昨今ではバブル世代と揶揄されて、 厳しい社会人生活を余儀なくされている人も多いようですが。

そんな中で私が今でもラクできているのは何故なのか? 振り返ってみると、 将来のことを考えなかった割に、 実はとても 「戦略的」 な人生を歩んできたように思います。 「結果論」 とか 「後講釈」 のように見えるかもしれませんが (^^;)、 私の実例を 「単に運が良かっただけ」 と切り捨てるか、 「自分戦略」 として参考にすべき点があるか、 判断は読者のみなさんにお任せします。

私と違って、 いまの学生さん達は 「自己分析」 とかに余念がなく、 それはとても結構なことだと思うのですが、 「自分のやりたいこと」 ってそんなに明確ですか? 「なりたい自分」 と 「やりたいこと」 を混同してませんか? 私にとって 「研究者」 は 「なりたい」 職業でしたが、 研究は 「やりたいこと」 ではありませんでした。 (アルバイトの経験を除けば) まだ働いたこともないのに、 これからどんな仕事をしていきたいのか明確なはずはないと思うのです。 自己分析をすればするほど分からなくなる、 あたりが関の山じゃないでしょうか?

で、 実際の会社選びとなると、 (学生さん達の間で) 人気がある企業とか、 初任給 (or 平均給与) が高くて福利厚生がよい企業とか、 職場環境がよい企業とか、 ブラック企業は何としても避けなければとか、 「自分戦略」 というお題目とは裏腹に、 えらく近視眼的で、 自分の将来どころか数年先すら見通せていないのが現状ではないでしょうか?

自分戦略?

・ 「自分のやりたいことを明確にし、それを実現するための手段を練ること」
・ やりたいこと?
  - 将来も、やりたいことが変わらないのか?
  - 何が向いているかも定かでないのに
・ やりたいことをやって、その後は?
  - 体力が衰えて同じようには働けなくなる
・ とりあえず給料の高い会社へ就職
  - 福利厚生・職場環境がいいところ?
  - 社会貢献できるところ?
・ 将来どころか数年先すら見通せていない
3

初任給が高くてもその後の昇給ペースが遅ければ意味がないし、 平均が高くても自分の給料が平均以下なら意味がないし、 平均が低くても自分の給料が高ければ問題無いわけです。 職場環境だって、 会社の中での立場が変われば変わってきます。 新卒入社一年目の社員から見れば快適な職場環境が中堅社員から見ると最悪だったり、 極端な話、 誰が自分の上司になるかで大きく変わることもあります。

そもそも、 現在の 「やりたいこと」 を一生続けたいですか? みなさんが今から 10年前、小学生だった時に 「やりたい」 と思っていたことが、 今でも 「やりたいこと」 なのか考えてみれば明らかでしょう。 今から 10年後には、 今とは全然違うことが 「やりたい」 と思っているかもしれません。

「やりたいこと」 を一生続ける?

・ やってみたら実際は違った
  - プログラミングじゃなくてニーズの汲み上げ
  - 経験を積むと嗜好は変わる / 体力は落ちる
・ そもそも幸せな人生とは?
  - ネガティブにならないこと
  - ネガティブの根本にはいつもお金の問題
  - お金の召使いになってはいけない
・ 年功序列の崩壊
  - 「やりたいこと」 だけでは老後破綻一直線
  - みんなが年金を頼ると国家破綻一直線
5

それなら、 今 「やりたいこと」 よりも、 「やりたい」 ことを見つけた時に、 それを 「やれる」 自由度こそが重要ではないでしょうか?

社会人を何年もやっていれば、 予想できない偶発的なことがいろいろ起きます。 その中には自分のキャリアを大きく左右するような事象もあることでしょう。 その偶発的な事象を 「計画的に導くこと」 で、 自分のキャリアを良いものにしていこうという考え方が、 ジョン・D・クランボルツの 「計画的偶発性理論」 です。

成功者を見ると、 「あの人は運が良かった (だけ)」 と思ってしまい、 運に恵まれない自分は決して同じようにはなれないと、 諦めてしまう人が多いのだと思いますが、 運が良くてもその運を活かせなければ成功できません。 「運に恵まれない」 のではなく、 せっかく訪れたチャンスに気付いてないだけかもしれませんよ? 「チャンスは備えあるところに訪れる」 のです。

人生には分岐点が沢山あります。 たとえ八方塞がりに思えるドツボな状況に陥ってしまっていても、 その後の分岐点で最適な選択さえできれば、 ピンチが逆にチャンスになったりします。 ただしそれは、 分岐点に選択肢の幅が十分にあればの話です。

ところが大多数の人はこの 「選択肢」 を進んで捨ててしまいます。 「やりたいこと」 があっても、 いろいろ言い訳 (「時間がない」 「お金がない」 「自分の今の実力ではムリ」 等々...) して挑戦を躊躇います。 ぐずぐずしているうちに歳をとり、 選択肢がどんどん狭まっていくわけです。 なにごともやってみなきゃわからないと思うのですが、 やるまえから 「どうせダメに決まってる」 と諦めてしまっていては選択肢は広がらず、 成功を遠ざけてしまいます

また、 実際やってみてダメだったとしても、 果敢に挑戦することによって新たな選択肢が現れてきます。 まさに失敗は成功の元ですね。 「運が良い人」 というのはそうやって 「運を引き寄せる」 のです。

(戦略1)
選択肢を減らさない

・ 「自分のやりたいこと」 って明確じゃない
  - 「自分探し」 は時間の無駄
  - いろいろ学ぶと興味が持てることが変わってくる
  - やりたいことは手当たり次第やってみるべき
・ リスクを取らないリスク
  - 「みんなと同じ」 はリスク最大
  - 歳をとるごと選択肢は容赦なく減っていく
・ 別の道へ変更する選択肢は残しておく
  - 最悪の事態を常に想定する
・ 計画的偶発性理論
  - チャンスは備えあるところに訪れる
6

選択肢を減らさないようにして (戦略1) 運を引き寄せることができたら、 次に必要になるのが運を活かす能力です。 多くの業界において一番重要な能力は 「論理的に考える力」 でしょう。 ところが現代の学校教育は、 この 「考える力」 を伸ばす点においてあまり有効ではないようです。

より正確に言うと、 考える材料をたくさん提供している点においては有効なのですが、 それはあくまで元々考える習慣を持っていた学生さんに対してのみ言えることであって、 義務教育の課程で考える習慣を身につけることができなかった学生さんは、 高等教育において、 ますます考えなくなってしまうという悪循環に陥っています。

極めつけは、 会社選びで 「自分の気持ちを大切に」 などと言う人ですね。 自分の将来という一番合理的に考えなければならない時に、 それも高等教育をちゃんと受けた人が、 「自分の気持ち」 すなわち感情を優先するなんてアリエナイと思うのですが、 いったん考えなくなってしまった人たちは、 自分の気持ちに従うことをオカシイとは思えなくなってしまうようです。

誰でも (どんなに地頭のいい人でも) 一人では考える力を伸ばすことはできません。 なぜなら 「考えたつもり」 に陥って、 そこで思考停止してしまうからです。 自分の考えを他人に話したり書いたりして、 他人から何らかの反応 (批判とか) を受け取って初めて考えるきっかけが生まれるのです。 自分の考えを文章にしてみるだけでも、 自分がどのくらい考えているか (あるいは考えていないか) 分かる場合もあるでしょう。

たとえば、 「答がある問題を解いているだけでは考える力を伸ばせない」 と言ったりしますが、 なぜ 「答がある問題」 じゃダメなのか考えたことはありますか? 「現実の社会は答が無い問題ばかりだから」 という説明で満足してしまっていたとしたら要注意です。 現実社会の問題に答が無いからと言って、 答がある問題を解く練習が役に立たないことにはならないですよね? 中途半端な説明で満足してしまっているのは、 「分かったつもり」 そのものです。

だから、 授業を聞いているだけではダメなんです。 分かっているつもりで聞いていても、 いざそれを自分の言葉で表現しようとしたら、 ぜんぜん言葉が出てこないなんてことがよくあります。 学校教育で考える力が伸びない理由がここにあります (もともと考える力があった人は教えなくても伸びるのでここでは除外して考えます)。 言いたいことがあれば先生の話を遮ってでも発言し、 自分が 「考えたつもり」 に陥っていないか常に確認する必要があります。

幸い、何を言っても許されるのが学生の特権です。 社会人だとキャリアに終止符を打ってしまいかねない発言すら、 学生なら 「若いから」 ということで許されてしまいます。 どうかこの特権をフルに活用して、 「自分の考えを発言する」 習慣を、 学生のうちに身につけてください

はじめに

・ ぜひ、この場でしかできないことを
  - 聞くだけなら、私のブログを読むだけで充分
・ 分からないことはすぐ質問
  - あとでググればいいやなどと思わないように
・ 言いたいことがあればすぐ発言
  - うまく言おうとしないこと
  - 最初は誰だって下手くそ。場数を踏んで上手くなる
・ 空気を読んではいけない
  - こんな発言したら浮く?とか考えていてはダメ
1

ハワード・ガードナーの多重知能理論によれば、 人間は進化の過程で

  1. 言語的知能
  2. 論理数学的知能
  3. 音楽的知能
  4. 身体運動的知能
  5. 空間的知能
  6. 対人的知能
  7. 内省的知能
  8. 博物的知能

の8つの知能を発達させてきたとされます。

このうち、科学技術とりわけ IT 技術の進歩により 2, 4, 8 の知能はどんどん重要ではなくなりつつあります。 あと 10年ほどで大半の職業が消えるとか言われていますね。 また、現代社会においては (特定の職種を除けば) 3, 5 は、 さほど重要とは言えないでしょう。

残るは 1, 6, 7 ですが、 なかでも他の知能を伸ばすメタ知能ともいえる 「7. 内省的知能」 が一番重要でしょう。 すなわち、自分自身の 「心」 を理解してコントロールする能力です。 7 の能力を他者に対して発揮すれば 6 になりますし、 1 が重要なのは主に 6 のためですから、結局 7 が一番重要ということになります。

現代社会では何かというと 「自分の気持ち」 を重視する傾向 (自分の気持ち至上主義) がありますが、 自分の気持ちのままに生きれば、 内省する機会は失われてしまいます。 知能は繰り返し発揮することにより伸びるものですから、 内省する機会があまりなければ、 内省的知能が未発達のまま成長してしまうことになります。

(戦略2)
合理的に考える

・ 「お気持ち」 至上主義
  - 「いまのお気持ちは?」
  - 自分の気持ちを大切にしていてはダメ
・ もっと頭を使おう!
  - 使えば使うほど頭はよくなる
  - 授業を聞いているだけではダメ
・ 金融リテラシー = 「恐怖と欲望」 のコントロール
  - 内省的知能
失敗は成功の元
  - 成功できないのは 「失敗」 が少なすぎるから
  - リスクを負わなければ失敗も無い
7

運を引き寄せ (戦略1)、 運を活かす能力を磨いたら (戦略2)、 最後に必要になるのが 「敵」 を知ることです。 これから社会に出ていく学生さんが、 当の社会のことを知らなければ出だしから躓くのは必至です。

残念なことに、ここでも学校教育はあまり有効ではありません。 先生がたは社会の矛盾点・問題点を追求することがお好きなようです。 もちろん批判的精神は重要ですし、 社会のあるべき姿を論じ、 学問の力で社会をよりよい形へ変革していくことは大学の使命とも言えるでしょう。

しかし学生さんはその 「社会」 の中でこれから生き抜かなければならないのです。 すでに 「エスタブリッシュメント」 な先生がたなら、 社会を批判しているだけでも周囲から一目おかれますが、 体制の後ろ楯もなく、 何の権力も持たない学生さんがいきなり社会を批判したって、 返り討ちに遭うだけです。 学生さんに今必要なのは、 理想社会の戯言ではなく、 現実の矛盾だらけ欠陥だらけの社会が実際にどう動いているか、 そして丸腰でそういう社会に飛込んで生き抜く方法です。

言うまでもなく現代社会は資本主義社会です。 お金の仕組みを知らずに社会に出ていくことは、 カモがネギをしょって出ていくようなものだと思うのですが、 どうして先生がたはそれを止めようとしないのでしょう? 特にウブな理系の学生さんは、 「優れた技術は高い給料で報われるべき」 などと考え、 社会に出ても自身の技術を磨くことばかりに夢中になってしまいます。 こういう勘違いは実に悲劇的だと思うのですが...

仮にも最高学府で学んでいる学生さん達が、 「努力は報われる」 などといったような素朴な労働観をいまだに持っていることに、 あらためて驚かされます。 製造業 (あるいは農林漁業) が産業の中心だった時代ならば、 労働者一人一人の 「努力」 が生産量の多寡に反映したこともあったでしょう。 労働者にとっても自らの努力の結果が目に見えるので、 努力の方向を間違えることはありません。 生産量が多い労働者の稼ぎが増えるのは合理的ですし、 周囲の納得感を得ることも難しくありません。

しかし今や企業の利益は数多くの労働者の連係プレーによって生み出されています。 技術者だけではお金にはなりません。 努力の結果がすぐに利益の増大という形で見えることは稀で、 あさっての方向の努力をしてしまうことも珍しくありません。 誰がどのくらい利益に貢献しているかなんて (合理的に) 測定することは不可能でしょう。 誰もが納得する利益配分なんて、 それこそ人月で測るくらいしか無いのが実状です。

努力の方向がズレていて残業ばかりしている (傍目には一生懸命努力しているように見える) 人と、 的確な判断のもと効率的に仕事を進めて短時間で仕事を終える (傍目にはあまり努力していないように見える) 人と、 どちらを評価すべきでしょうか? もちろん後者の方が会社に貢献しているのですが、 前者を冷遇すると納得感が失われて社内に不満が溜ります。

つまり、 資本主義が言うように、 給料は労働の対価ではなく、 単に労働力の再生産 (つまり労働者が次の日も納得して働いてくれること) に必要な 「コスト」 に過ぎません。 労働者の技術の優劣と、給料の高低との間に、直接の関係はありません。 もちろんモチベーション向上のために関連性を 「演出」 することはありますが、 結局のところ給料は多くの人が納得する 「相場感」 で決まってくるのです。 努力が報われないなんて、 そんな社会は間違っている!と思いたい気持ちは重々分かりますが、 実際の社会はそういう仕組みなのですから否定したところで始まりません。

努力を認めてもらいたければ、 「努力は報われるべきだ」 なんて受け身なことを言ってないで、 戦略的にアピールして昇給を勝ち取るべきです。 その際、 「労働力を売らない自由」 (後述) があると昇給交渉をより有利に進めることができるでしょう。

(戦略3)
(社会に出る前に) 社会を理解する

・ 学校では社会の問題点ばかりを学ぶ
  - 社会を変えようとする人ばかり量産している
  - 学問の力で社会を変革する、それは大事だけど…
・ 現実の社会=お金の仕組み
  - お金の仕組みを理解しないと搾取される人生に
  - 社会を知らない学生さんは 「カモネギ」
・ みんなの勘違い
  - 優れた技術には高い給料で報いるべき?
  - 給料は労働の対価ではない!
  - 搾取 ≠ ブラック企業
・ 労働分配率が上がっても消費へ回してしまう
  - 搾取され続け、一生 (嫌々) 働き続ける羽目に
8

学生さんにとって、 とりわけ喫緊の課題は資本主義社会における 「搾取」 の構造です。 現代における 「搾取」 を正しく理解しない限り、 搾取の餌食になってしまいます。 社会を変革するどころか、 その遥か手前で力尽きてしまうことでしょう。

搾取というとすぐブラック企業を思い浮かべる人が多いと思いますが、 ブラックのレッテルを貼られた企業の業績があっと言う間に悪化したように、 給料を安く抑えようとする手法 (サービス残業とか名ばかり管理職とか) は現代社会においてはあまり賢いやり方とは言えません。 本当の、そして最も警戒すべきは、 搾取と気付かれないだけでなく、 むしろ進んで搾取されることを多くの人が望むような搾取です。

資本主義社会において労働者の立場が弱いのは、 自身の労働力を売らない自由が無い (辞めると生活できなくなる) からです。 通常の売買契約でも売り手が (安い価格でも) 売らざるを得ない (閉店時間間際の生鮮食料品売場とかが該当しますね) のであれば (足元を見られて) 損をするように、 労働力を売らないという選択肢が無ければ、 労働条件に不満があっても受け入れざるを得ません。

したがって、 半年くらいは働かなくても生活できる程度の貯金 (数百万円?) さえあれば、 労働者の立場は劇的に改善するはずです。 しかも、 昨今は給料を安く抑えようとすると、 すぐ 「ブラック」 のレッテルを貼られてしまいますから、 給料が生活費ギリギリということはあまり無く、 本来ならば貯金にまわす余裕があるはずです。

ところが、 給料が上がっても同じくらい支出も増えてしまってほとんど貯金できない、 という人がほとんどのようです。 給料半年分どころか、100万円の貯金すら無い人が多いとか。 いったい何にそんなにお金を使っているのでしょうか? 初任給が 300万円/年だった人が 400万円/年に昇給して、 何年たっても 100万円すら貯金できないというのは意味不明です。

お金をついつい使ってしまう人に是非考えてほしいのは、 資本主義の世の中では、 いかに消費者にお金を使ってもらうか必死に考えている人が大勢いるってことです。 ほとんど全ての場合において、 お金を使いたいと自発的に消費者が思っているのではなくて、 お金を使わせたいとあの手この手で誘惑している人がいて、 消費者はまんまとその策略に乗せられてしまっているだけなのです。

お金を使う決断をする前に、 はたしてその金額が自分の身の丈に合っているか考えてみるべきでしょう。 給料が少なかったときはお金を使わずに我慢できていたことであれば、 それは決して必需品ではありませんよね? 知能が先天的なものか後天的なものか本当のところはよく分かりませんが、 たとえ先天的なものが支配的であったとしても、 マシュマロを我慢する自制心は鍛えるべきだと思います。 「子どもでも大人でも自制心を育むことは可能」なのですから。

支出を増やしてしまうこと以上に問題なのが借金することです。 年収の 5倍もの借金をかかえて、 借金を返すためだけに働いているような人もたくさんいます。 まさに銀行の 「奴隷」 ですね。 なぜ身の丈を大きく超えるような金額の住宅を買わせようとするのでしょうか? 言うまでもなく巨額のローンを組ませて銀行が儲けるためです。

労働者が生み出した剰余価値を奪うことによって、 労働者を労働者階級に固定化することを搾取と定義するなら、 こうした貯金させない社会の仕組みこそが現代の搾取と呼ぶにふさわしいと思います。

(戦略3)
お金の仕組み

・ 給料とは労働力の再生産のためのコスト
  - 「努力は報われる」 はプロパガンダ
  - 報われるのはリスクを負った人のみ
・ 生産者になる前に一人前の消費者に
  - 小学生のころから消費の仕方を学ぶ
  - 時給アルバイトは時間の切り売り。生産ではない
・ 消費の誘惑に満ち満ちている = 搾取の構造
  - なぜ銀行は住宅ローンを勧めるのか?
  - なぜ保険会社は保険を勧めるのか?
・ 資本主義=労働者から生産手段を分離
  - 生産手段 (知能) を取り戻せ!
9

以上 3点が、 私が意識せずに実践していた 「自分戦略」 です。 意識していなかったくらいですから大したこととは思っていなかったのですが、 ネット時代になって貯蓄額の世帯分布などを手軽に参照できるようになった昨今、 私と同じくらいの給料をもらっていたはずの人たちにおいても、 大した貯金があるわけではないことを知り、 驚いている次第です。

最初の 100万円を貯めるだけでも労働者としての立場は格段に向上するわけで、 強くなった立場を背景に給料交渉を有利に進めて自身の労働力の価格をつり上げ、 加速度的に貯蓄額を増やしていけば、 労働者階級を脱出する (= 働かなくても当面の生活には困らないし、 やりたい仕事を選べる) ことも現実的な目標となるはずです。 それなのに、 なぜ大多数の人が同様の戦略をとることができないのでしょうか?

「戦略1」 (選択肢を減らさない) は、 自分の適性を知るための戦略です。 世の中は 「自己分析」 が流行りですが、 ちゃんとしたコンサルタントとかに相談するならともかく、 素人の学生さんが一人でやってマトモな分析ができるはずはありませんよね?

「自分のことは自分が一番分かっている」 という思い込みが、 「戦略1」 を実践する障害になっているのではないでしょうか? 実際は、 自己評価より他人からの評価のほうが正しかったりするわけです。 こと自分のことになると、 心理的な防衛本能が働いて真実の姿が見えにくくなります。 自分のことを知るには、 あれこれ考えるより、 やりたいと思うことを実地にやってみるべきです。 とことんやってみれば自分の能力の限界がどのあたりにあるかも、 見えてくることでしょう。

「戦略2」 (合理的に考える) は、 自分の知能を向上させるための戦略です。 小学校から大学まで 16年 (大学院までいけば 18年も!) 学ぶのに、 考える機会がほとんど無いことに驚かされます。 学んでばかりいるから考える機会が失われているのかもしれません。 まさに 「学びて思わざれば則ち罔し」 ですね。

「考える」 という言葉は誰もが気軽に使いますが、 実際のところどこまで考えているでしょうか? ほとんどの場合、 考えているのではなくて悩んでいるだけだったりします。 自分の考えを主張している時ですら、 他の人の意見の受け売りに過ぎなかったりしますし、 そもそも 「自分の考え」 を表明する機会ってほとんど無いように思います。 facebook だって、 「今どんな気持ち?」 ですし。 ふつーに生活している限り、 気持ちを聞かれることはあっても、 考えを聞かれることは皆無ではないでしょうか?

映画 「マーガレット・サッチャー 鉄の女の涙」 で、 「あなたの本当のお気持ちは?」 と聞かれたときのサッチャーの答がふるってます:

なに
私の気持ちってどういうことかしら
最近は考えより気持ちね
どんな気持ち
なんだか気持ち悪いわ
すみません我々の気持ちとしては
これは今の時代の大きな問題ですよ
人々は感情にばかり左右されて
考えやアイディアなんかはどうでもよくなって
本当に面白いのは
考えとかアイディアなのに
私が何を考えているか聞いて

このあと、 例の有名な 「自分の考えが言葉になる、言葉が行動になる、行動が...」 が続くのですが、 そこでは 「考え」 と 「気持ち」 が混同されていて、 私は何だか気持ち悪く感じます。 前段 (上記引用部分) と 後段 (自分の考えが言葉になる...) は、 同じ人の言葉とは思えません。 まあ、後段は 「父の口癖だった」 のかもしれませんが。

斯くして考える習慣がある人はどんどん知能を向上させていき、 習慣が無い人はどんどん差を付けられていく、 という二極化が進んでしまっているのでしょう。 経済的な格差ばかり注目される昨今ですが、 本当の格差は 「知能」 の格差であって、 経済的な格差はその派生物に過ぎません。

そして 「戦略3」 (社会を理解する) は、 金融リテラシーを身につけるための戦略です。 社会に関する誤った常識が、 社会の本質を理解する妨げになります。 お金を使わせたいと誘惑する側が、 誤った知識を広めようとしているのも、 妨げになっていますね。

誤った知識とは、 例えば 「賃貸より購入の方が良い」 といったようなものです。 その 「知識」 を広めようとしている人 (マンション開発業者とか不動産屋とか銀行とか) が、 その知識が広まることで利益を得ているならば疑ってかかるべきでしょう。 「毎月家賃を払い続けても永遠に家は自分のものにならないが、 ローンなら完済すれば家が自分のものになる」 などと言われて騙されてしまう人が沢山います。

家賃を払う人には安い家に引っ越して負担を減らす自由がありますが、 ローンを抱えている人には負担を軽減する自由はありません。 つまり選択肢を減らしてしまっているわけです。 したがって、 「購入 VS 賃貸」 という比較はナンセンスです。

「戦略3」 を実践する最大の障害が、 お金にまつわる感情です。 ほとんどの人が、 「お金がなくなる恐怖」 と 「お金がもっと欲しいという欲望」 の二つの感情に支配されていて、 お金について合理的に考える余裕が無くなっています。 これでは金融リテラシーを身につけることはできません。

「お金になんか興味はない」 「仕事が好きだから働いているんだ」 と言う人もいますが、 そういう人は今の仕事が好きでなくなったら、 働くのを辞めるのでしょうか? 辞めても生活に不自由しないのであれば問題ありませんが、 多くの人はそうではないはずです。 つまり 「興味がない」のではなくて、 お金について本当は考えなければならないのに、 それを直視できていないだけです。 心理学で言うところの 「否認」 ですね。 「仕事が好きだから働いているんだ」 は 「合理化」 (防衛機制の一つ) でしょう。

なぜ戦略的に動けないか?

・ 戦略1 ⇒ 自分の適性を知る
  - 自分のことは (他人のことより) 知るのが困難
  - 心理的な防衛機制が働く
・ 戦略2 ⇒ 論理的に考える力を鍛える
  - 直観や気持ちを優先してしまう
  - 自分の考えを発表する機会が皆無
・ 戦略3 ⇒ 社会の本質を見抜く
  - 「常識」 に囚われてしまう
  - 大人たちが 「カモネギ」 を狙ってる
10

以上のように、 この 3点の戦略には、 それぞれ実践を難しくしている要因がありそうです。 ではなぜ私は実践できたのか?

やりたいことは先送りにせず、 とことんやってみる性格だったのが大きいように思います。 当時は計画的偶発性理論など知らなかったのですが、 何かやりたいと思うより先に行動していました。 やりたいと意識しないうちに、 気が付くと既にやっているみたいな感じで。 また、 知らないことを見つけるとすぐ首を突っ込んでいました。 コンピュータ分野に限らず、 数学、物理学、哲学、社会学、果てはテニスに至るまで、 自分に向いてるかどうかなんて考えずに、 やれるところまでやってみる、 ということを繰り返していました。 戦略1 (選択肢を減らさない) ですね。

中学校 (1979年) の部活動でマイコン部 (当時 PC は 「マイコン」 と呼ばれていました) に入ったのですが、 そのオリエンテーションで BASICっていう言語を使ってプログラミングをするという話を聞いて、 その日のうちに都心の一番大きい本屋へ出かけていって、 当時一冊しかなかった BASIC の入門書 「BASICで広がる世界」 (CQ出版社, 1979年発行) を買って一気に読み、 次の日にはもうプログラムを書き始めていたほどです。

子供のころから好奇心旺盛だった私ですが、 実を言うと考えることは不得手でした。 小学生の時はよく 「分かったつもり」 に陥っていました。 学校のテストで答案によくデタラメを書いたものです。 しかもデタラメを書いているという意識はなく、 自分は分かっていると思い込んで、 正しい答を書いているつもりだったのです。 中学一年生のとき数学のテストで赤点 (30点未満) をとったこともあります。

ところが中学生になって、 とても優秀な同級生が現れたのです。 彼は、一を聞けば十を理解する。 あまりに早く 「分かった」 というので、 信じられずに残りの九を説明しようとすると、 先回りして答えられてしまったほど。 そんな彼が 「分からない」 を連発する。 つまり 「分かった」 と言うのも早いが、 「分からない」 と言うのもとても早かったのです。

そんな彼を見て、 私は 「分からない」 と言えるのはカッコイイことなんだと思うようになりました。 なんとかして自分も、 「分からない」 と言えるようになりたい、 と思ったのでした。 いま思えば、 これが私の人生の転機だったのかもしれません。 戦略2 (合理的に考える) ですね。

以来、 考えるのが好きになり、 大学受験に失敗したのをいいことに、 浪人中の一年間に弁証法とか資本論とかの難しそうな本 (といっても初学者向けの教科書ですが) を手当たり次第に読みまくりました。 難しそうなテーマであればあるほどチャレンジしたくなったのです。 これが資本主義についてもっと知りたいと思うきっかけになりました。 戦略3 (社会を理解する) ですね。 実はこの時、 人文系の本を読みすぎたせいで、 文系へ転向しようかと思ったほどです。 プログラミングの方が好きだったので思い止まり、 結局一年目と同じ情報工学科を再び受験したのですが。

今から思うと、 お金を稼ぐ前に社会の勉強を始めたのは幸いでした。 浪人中はお金が無かったので、 消費したいという欲求が全く起きなかったし、 資本主義について学び始めた後は、 「搾取の構造」 を理解したので消費の誘惑に負けなくなったのです。 収入が増えても消費は増えなかったので、 収入の増分を丸々貯金することができました。 もし現役で大学に合格していたら、 順序が逆になって、 勉強を始める前にアルバイトでお金を手にして、 搾取の罠にはまっていたかもしれません。

いっぽう、 大学生のときパソコン通信や JUNET を知って (1989年)、 これを駆使すれば個人でも有名になれると思ってからは、 どうやって自分を売り込めばいいか考えるようになりました。 企業にとってブランドが最重要であるように、 個人にとってもブランドが資産になると考えたのです。 合理的に考えれば当たり前のことですよね。

もちろん最初は何をすれば自分を売り込めるのか見当もつかなかったのですが、 いろいろ試行錯誤しているうちに、 それなりに注目を集めることもできるようになり、 初めて書いたオープンソース・ソフトウェアである stone は、 そこそこ有名になりました。 私は 「stone の開発者」 として多くのかたに覚えていただけるようになったのです。 stone は私にとって最大の 「資産」 (収入をもたらす源) と言っても過言ではないでしょう。 無料で配布しているソフトウェアが最大の資産ってのが面白いですね。

浪人生の時に資本論をかじったこともあって、 大学生になってアルバイトで数十万円の貯金ができると、 すぐ株の売買を始めました (当時はオンライン取引というとファミコン・トレードだけで、 電話をかけて口頭で売買注文を出したり、 証券会社の窓口で注文したりしていました)。 当時 NTT が株式を公開 (1987年2月9日) したり、 ブラック・マンデー (1987年10月19日) が起ったりと、 世間的に株が注目を集めていたのも、 株の売買を始めようと思ったきっかけの一つだったのでしょう。

もちろん単位株での売買で大した金額ではなかったのですが、 学生にとっては大金です。 損を出すまいと必死になって経済の勉強をしました。 「お金がなくなる恐怖」 が勉強の原動力になったのです。 経済に対する理解が深まれば会社の経営に興味が湧いてくるのは必然で、 2000年に KLab(株) (設立当時の社名は (株)ケイ・ラボラトリー) 会社設立の計画を聞いたとき、 迷わず飛び付きました。

仮に起業が頓挫しても数年くらいは生活に困らない程度の貯金がありましたし、 当時も技術者は不足していましたから、 ここがダメでも再チャレンジの余地は充分あるだろうと思っていたので、 大企業の正社員という安定した 「地位」 を捨ててベンチャーに飛込むことに、 何の躊躇いもありませんでした。 まさに、 備えがあったからこそチャンスが訪れたのです。

私の自分戦略

・ やりたいことは先送りにせず、とことんやってみる
  - 中学1年の時以来、パソコンにのめり込む
  - 大学に入ってパソコンを 「自作」
  - 大学院生の時、個人事業主としてソフトウェア開発
  - 日立に就職後、本業そっちのけでネットインフラ整備
・ 自分ブランド
  - 大学で JUNET を知って (1989年) 以来、試行錯誤
  - stone の開発・発表 (1995年~)
・ 金融リテラシー
  - 小学生の時から株に興味を持ち、大学生の時から売買
  - 「金持ち父さん 貧乏父さん」 (2000年)
11

それはベンチャーと技術者との出会いでした。 それまでベンチャーを興そうという人と技術者とでは興味の対象が異なり、 両者の出会いはかなり稀な事象でした。 2000年当時は、 創業メンバに アイディアマン, 技術者, マーケッタ という多様な顔触れが揃うベンチャーは珍しかったのです。 この会社が現在に至るまで発展し続けているのは、 創業メンバの多様性にこそあったのだと思います。 この偶然の出会いを 「単に運が良かっただけ」 と思うか、 それとも 「戦略的に計画された偶発的事象」 ととらえるか、 判断は読者のみなさんにお任せします。

カナダで fido と WIND と Virgin のプリペイド SIM カードを買ってみた

$
0
0

8年ぶりのバンクーバー。 前回来た (2008年5月) 後にバンクーバーオリンピック (2010年) があって、 街がいろいろ変わってた。 オリンピックが終わっても不動産バブルはドンドン膨らみ続け、 巨大なビルがドンドン建てられている (が、テナントは入らず既にバブル崩壊状態?)。

でも、 それに負けず劣らず大きく変わったのがネット環境。 2008年5月当時はスマホがまだ一般的でなく (iPhone 3G の発表は 2008年6月)、 通信手段と言えばモデムだけ。 ホテルの部屋に LAN は無く、 公衆無線LAN も部屋には届かず、 かろうじていくつかのカフェで Wi-Fi が利用できたので、 わざわざカフェまで行ってネットにアクセスしていた (2008年なので Advanced W-ZERO3[es] で)。 滞在中ほとんどネットにアクセスできなかった (しかもホテルでさえネット無し!) なんて、 今から思うと考えられない。

ちなみに 2008年当時でも、 ワイキキなら多くのホテルは部屋まで LAN が来ていたし、 ホテルの部屋から公衆無線LAN でネットにアクセスすることもできた。 2008年くらいにもなるとモデムを使う羽目に陥ったことは皆無だったはず。 米国のホテルでモデムを使ったのは 2005年くらいが最後だったと思う。

8年前に泊まったホテルも、 今回のホテルも、 ロブソン通 (Robson St) に面していてすぐ近く。 8年前と違うのは周囲にケータイ屋が溢れている点。 ホテルを出てちょっと歩き回ってみるだけで、 fido, WIND, Virgin, Telus, Bell, Rogers など、 ほとんど全てのキャリアのお店を見つけることができた。 ホテルから歩いて最初に見つけた fido に入ってみる。

Fido Solutions

Fido Solutions は Rogers の子会社。 fido という名前の有名な犬がイタリアにいたらしい。 空襲の犠牲となった主人を14年間待ち続けたイタリアの忠犬フィド (イタリア語だと 「フィド」 だけど、 英語では 「ファイド」 と読むようだ)。 この犬にちなんで fido のロゴには犬小屋が描かれている。

fido のプリペイドSIM には、 月額 (By the Month) プラン ($15.75, $25.75, $30, $35, $45, $55, $60, $70) と従量 (By the Minute) プランと一日 (By the Day) プランがある。 もちろん 「$」 はカナダドル、以下同様。 月額プランのうち $15.75 〜 $35 のプランと、 一日プランは通話&SMS のためのプラン (例えば $35 の月額プランは 500分の通話が含まれていて SMS は無制限に使える) で、 データ通信に関しては従量プランと同じ。 したがってデータ通信がメインで SMS は使わない人 (= 私) にとっては、 $15.75 〜 $35 の月額プランや一日プランは (従量プランと比べて) メリットが無く、 従量プランあるいは $45 以上の月額プランが選択肢となる。

従量プラン$15.75$45$55$60$70
ローカル通話40¢/分30¢/分含300分含500分無制限無制限
長距離通話80¢/分70¢/分45¢/分45¢/分無制限無制限
夜間通話放題無し無し5pm-7am5pm-7am全日全日
国内SMS30¢/通無制限無制限無制限無制限無制限
国際SMS35¢/通無制限無制限無制限無制限無制限
データ通信要アドオン要アドオン400MB
5¢/MB
750MB
5¢/MB
1GB
2¢/MB
2GB
2¢/MB

もちろん「¢」はカナダセント、以下同様。 従量プランにはデータ通信が含まれていないので、 アドオンとして以下のデータパッケージを追加することが必須。 $45 以上の月額プランではこのアドオンを追加することはできない。

データ量月額料金超過料金
100MB$1015¢/MB
500MB$205¢/MB
1GB$305¢/MB

2週間の滞在で 1GB くらいは通信するだろうから、 選択肢としては $60 の月額プランか、 従量プランに 1GB のアドオンを追加するか、 の二択。 前者だと 1カナダドル=約80円なので 4800円くらい。 米国とかと比べると 1G で 5000円近くというのは割高感がある。 後者はそもそも 40¢〜80¢/分という通話料が高すぎ。 迷った末に $60 の月額プランを選んだ。

ところが、 支払う段階になってさらに 「Minimum Refill Amount Required $10.00」 が必要と言われる。 しかも消費税 12% が加わって、 しめて $78.40 (約 6300円)。 う〜ん、 これで 1GB しか使えないというのは高いなぁ。 「Refill Amount」 って言ってるけど、 残高 (balance) には加算されない。 預り金 (デポジット) みたいなもの? SIM 代と言ってくれたほうがスッキリするなぁ。

従量プランだと通話 1分 40¢ も課金される。 滞在中に残高を気にしながら電話するのはちょっとなぁ (残高が足らなくなってきたら refill しなきゃならない)、 と思ったので月額プランを選んでしまったのだけど、 多くても一日 5回くらいしか電話しなかったので、 2週間の滞在で合計 50分もいくかどうか。 最初に $20 くらい refill しておけば充分だった。 私の場合、 長距離通話の必要はないし、 SMS もほとんど使わない (ハングアウト等のチャットアプリを使えば済む)。

従量プランでデータ通信する場合は、 アドオンとしてデータパッケージを購入する。 1GB が $30 なので、 通話料が $20 とすれば合計で $50、 つまり従量プランのほうが $10 ほど安くなる。 滞在期間が短い場合や、 通話をあまりしない場合は、 従量プランを選択すべき。

SIM を Nexus 5X に入れてアクティベートしてもらうと、 無事 LTE が使えた。 fido は Rogers の通信設備を利用しているので LTE は Rogers と同じ Band 4 (1700/2100 MHz) と Band 7 (2600 MHz) そして現在展開中の Band 12 (700 MHz, Band 17 を内包する) が利用できる。 3G は Band II (1900 MHz) と Band V (850 MHz)。 いずれも Nexus 5X はサポートしている。

2週間の滞在で通信量を 1GB に収めようとするとかなり節約しなければならない。 Wi-Fi が使えるところでは Wi-Fi を使い、 ホテルでは Nexus 5 を Wi-Fi ルータとして使い (後述)、 できるだけデータ通信量を節約したのだけど、 ほとんど初めての土地 (8年前に来たけどほとんど忘れてた) だとどうしても Google Maps を見ることが多くなりがちで、 1日 100MB くらいは使ってしまう。 10日目くらいで通信量が 1GB を超えてしまった。

月額 $60 のプランだと、 1GB を超えたぶんは 1MB につき 2¢課金される。 仮に 1日に 100MB 使えば $2 になる。 たかが $2 と思うかもしれないが、 月額プランは残高ゼロのままで使えるのがメリットなのに、 課金が発生すると refill しなければならず面倒。

というわけで、 せっかく月額プランにしたのに、 結局 refill することになってしまった。 残高を気にして refill することになるなら、 最初から 40¢/分の通話料を払う従量プランにすればよかった。 なお、 従量プランに 1GB のデータパッケージを購入した場合は、 1GB を超えたぶんは 1MB につき 5¢課金されるので、 (1MB につき 2¢の) 月額プランより 2.5倍高く、 月額プランの意味がないとまでは言えない。

クレジットカード払いで (オンライン) refill する場合は、 カナダの住所でないとダメ。 というのも、 クレジットカード会社に登録している住所を入力する必要があるのだけど、 国名が 「Canada」 固定で変更できない (州もカナダの州から選ぶ選択式だし、 郵便番号はカナダ式でないとエラーになる)。 ところが、 クレジットカード会社に (その) 住所で認証を求めるらしい。 クレジットカード会社の判断次第というところもあるけど、 多くのクレジットカード会社は住所が異なっていると承認しない。

INTERAC Online を使う方法もあるらしいが、 Scotia Bank, RBC (Royal Bank), TD (Bank of Canada), BMO (Bank of Montreal) のいずれかに口座がないとダメ。

結局のところカナダ在住者でないかぎり refill カードを買うしかない、 ということのようだ。 これは fido に限らず他のキャリアでも同じ。 日本からの旅行者が refill カードを買うしかない、 というのは分からなくもないが、 お隣の米国から来た人でさえそうだというのだから、 いかがなものかと思う。 SIM の有効期限が切れてしまうから一刻も早く refill したいが、 それには何百キロも走ってカナダへ行ってくるしかない、 なんて笑えない話も米国にはあるようだ。

refill カードは買った日から 30日間しか有効でない (未確認) ので、 買いだめしておくわけにもいかない。 国によっては、 買ってから一年くらいは (何年も?) 有効な refill カードもあるのに、 カナダのキャリアはどうしてこう融通が効かないのだろう? ちなみに米国のキャリアも、 クレジットカード払いで refill する場合は SSN が必要だったりするので非居住者にはハードルが高いが、 幸いキャリア以外から refill カードを買うことができる (支払は PayPal が利用できる)。

というわけで、 まず refill カード ↓ (写真はカードの表と裏) を近所のドラッグストア買った。 支払は日本のクレジットカードでもちろん大丈夫。

fido refill card

米国、台湾などでは refill カードの裏面にマスクしてある部分があって、 そこをコイン等で削ると Voucher Number (儲值碼, 中華電信の場合 12桁の数字) が出てくる。 香港などでは refill カードが袋綴じになっていて、 袋を開封すると中に voucher activation number (啓用密碼, 3HK の場合 16桁の数字) がある。 いずれの場合も refill カード自体に最初から (商品棚に並べられる以前から) 数字が印刷されている。

fido refill card

ところがカナダの場合は、 refill カード自体に意味はなく、 レジでお金を払うと、 数字が印字されたレシート (みたいな形状の紙, 写真 →) をもらえる。 refill カードにあらかじめ数字を印刷しておく必要がないので合理的だが、 見た感じレシートにしか見えないので、 間違って捨ててしまわないように注意。

赤丸で囲った数字 (写真 →) が PIN number (14桁の数字)。 この数字を 「*114」 に続けてダイヤルするか、 オンラインで入力する。 この数字は買った日から 30日以内に refill しないと失効するらしい。 逆に言うと 30日間で数字の使いまわしができるはずで、 それならなぜ 14桁も必要なのだろうか?

ちなみに英国の場合はもっと極端で、 refill カード自体が存在せず、 レジで top up voucher を買う旨伝えて代金を支払うと、 普通の買物と同様レシートをくれるが、 そのレシートに voucher number (vodafone の場合 12桁の数字) が印字されている。 初めて買ったとき、 「あれ? refill カードはどこ?」 って聞いてしまった (^^;

カナダも、 買ったのがドラッグストアだったから 店内に陳列されていた refill カードを買物カゴに入れてレジに持っていったが、 コンビニとかだと英国と同様に、 レジで直接注文してレシートをもらうだけなのかもしれない (未確認)。

レシート (状の紙) に印字された PIN number を (ダイヤルではなく) オンラインで入力するには、 まず MY ACCOUNT ページ にアクセスし (要ユーザ登録)、 「REFILL NOW」 をクリックし、 「Redeem Voucher」 を選んで 「Voucher Number」 として PIN number を入力すればよい。 細かいことだけど、 同じ数字を 「PIN number」 と呼んだり、 「Voucher Number」 と呼んだりするのはいかがなものかと思う。 初めて利用する人のことを考えて、 用語は統一すべきだろう。

国際ローミングができるので、 日本でも通話と SMS は発着信可能。 従量プランに変更して日本で SIM を維持しようかと思ったが、 なぜか MY ACCOUNT ページでプランを変更しようとするとエラーになってしまう。 この WWW ページはエラーが頻発して使いにくい。 しばしば 「Sorry, it looks like your My Account info isn't available right now, but it shouldn’t be too long. Please try logging in a little bit later.」 などと言われてしまう。

月額プランのままでは (毎月 $60 取られるから) SIM を維持するのは不可能だなぁと思っていたら、 7/30 に次のような SMS が (日本にある SIM に) 届いた。

Hi, it's Fido: You don't have enough in your account to cover your $60 /month plan. You're now on pay-per-use. Call *611 or visit fido.ca/myaccount

「You're now on pay-per-use.」 とあるのが、 従量プランに切り替わった、という意味なのか、 それとも課金が従量ベース (通話 40¢/分, 通信 $1/10MB) に一時的に変わっただけで、 残高が $60 を上回ると勝手に月額プランに戻ってしまう、 という意味なのか、 どちらなのだろう?

もっとも、 refill カードが無い限り日本から refill する方法はないので、 今ある残高 ($6.95) の期限 ($10 refill の場合は 30日間) が切れれば SIM ごと失効する。 $100 refill すれば有効期限が一年間に延びるらしいが、 $100 のカードを売っているのを見たことがない。 どこで買えるのだろうか?

WIND Mobile

fido SIM を買った後、 WIND Mobile のお店にも入ってみた。 WIND は 2008年創業の新興キャリア。 都市部のみ (と言うにはもっと限定的で、大都市が含まれていなかったりする。後述) にサービスエリアを展開し、 2016年5月に加入者数が約 100万を超えた。 サービスエリア外では Rogers, Telus, Bell のサービスエリアならローミング可能。

サービスエリアは狭いけど (データ通信が) 安いと評判のキャリアなのでお店に入ってみたのだけど、 3G (HSPA+) のみと聞いてびっくり。 しかも Band IV (1700/2100 MHz) のみと聞いて二度びっくり。 Band IV は北米以外では (あまり) 使われていないので、 サポートしていない端末も多いのではないか? 香港で買った中国版 Galaxy Note 4 (SM-N9100) で使おうと思っていたのだけど、 この Note 4 は Band IV をサポートしていない。

しかも、 端末に入れて通信できるか確認してからでないと SIM を売れない、 と店員さんに言われる。 売った後で 「通信できない!」 等のクレームを避けるためか。 Nexus 5 なら Band IV をサポートしているが、 あいにくホテルに置いてきてしまった。 いったんホテルに戻って出直す羽目に。 20:00 閉店にギリギリ間に合った。

fido と異なり WIND は月額プラン ($25, $35, $40, $50) のみ:

$25$35$40$50
国内通話無制限無制限  無制限  無制限  
米国への通話15¢/分15¢/分5¢/分無制限
国内&米国へのSMS無制限無制限無制限無制限
その他の国へのSMS5¢/通5¢/通無制限無制限
データ通信$5/100MB2GB3GB8GB

月額 $40 のプランが、 SPECIAL OFFER として 2GB 増量して 5GB になるというので、 この $40 のプラン 「Smartphone 40」 を選んだ。

ところが、 さらに SIM カード代 $25 が加算されるらしい。 消費税 12% が加わって、 $72.80 (約 5800円)。 安いと思って契約したのに支払額は fido とあまり変わらない。 まあ 5GB も使えるからよしとする。 5GB を超えると従量課金されるのではなく、 下り 256kbps 上り 128kbps に帯域が制限されるらしい。 5GB を超えても full-speed で使いたい場合は、 アドオンとして 「3GB Extra Data」 $10 を買えばよい。

ホテルの LAN が有料だったので、 この WIND SIM を入れた Nexus 5 を Wi-Fi ルータとして利用した。 3G といっても HSPA+ (3.5G と 3.9G の間, 公称 42Mbps) なので、 PC で Web ブラウズする程度なら (さほど) 問題は無い。 二週間で 3.2GB 使った。

WIND はサービスエリア (WIND Home) を限定することで低価格を実現しているが、 カナダで 2番目に人口が多い大都市モントリオール (ちなみに 1位はトロント) すら WIND Home ではなく、 ローミング (WIND Away) 料金が適用される。 すなわち、 国内&米国への通話が 15¢/分、 国内&米国への SMS が 5¢/分、 データ通信が 5¢/MB。

あれ? 意外に安い。 ローミングとは思えない安さ。 5¢/MB といえば、 fido の従量プランに 1GB のアドオンを追加した場合の、 1GB を超えたときの料金と同じ。 「ローミング」 というイメージからはかけ離れた安さだなぁと思っていたら、 2年前はもっとずっと高かったらしい。 2014年8月21日付の WIND のブログには、 価格改訂前は WIND Away のデータ通信が $1/MB だったと書いてある (しかも通信速度は 2G レベルの 120kbps)。 まさに 「ローミング」 のイメージ通りの高さで、 この料金だったら使おうとは決して思わなかっただろう。

WIND Home な都市があるのは、 オンタリオ州 (トロント, オタワ, ロンドン, ウィンザー, キングストン), アルバータ州 (カルガリー, エドモントン), ブリティッシュコロンビア州 (バンクーバー, アボッツフォード) の 3州だけで、 それ以外の州は (ケベック州も!)、 たとえ人口が多くても WIND Away となっている。 ただし、 オタワ (オンタリオ州, カナダの首都) は州境にあるので、 オタワ市街は川向こうのケベック州にも広がっている。 なのでケベック州もオタワ周辺だけは WIND Home になっている。

WIND でも (fido と同様) 国際ローミングを利用できる。 ただし初期設定では無効化されている。 「*225#」をダイヤルするか、 Manage Features ページ (要ユーザ登録) で 「International Roaming」 を有効化することで、 国際ローミングを利用できるようになる。 日本でも通話と SMS は発着信可能 (通話発信 $4/分, 通話着信 $3/分, SMS 発信 $1.50/通, SMS 着信は無料)。 データ通信は 1MB あたり $20 もするので実用的ではない。 アドオンとして 「World Traveller」 $8 を買うと 1MB あたり $1 になるが、 実用的ではないことに変わりはない (2年前の WIND Away と同じ)。

日本に居ながら SIM を維持したい場合、 日本で SIM が失効していないか (着呼するか & SMSが届くか) 確認できるので国際ローミングは有用だが、 月額プランは毎月一定額が課金されるので維持には不向き。

Virgin Mobile

20:00 を過ぎると、 もうどのキャリアのお店も開いていない (20:00 だとまだ明るいのに! 6月末の日没は 21:22 頃)。 Galaxy Note 4 用にも SIM を買いたかったので、 翌日、 巨大ショッピングモールへ行ったついでに、 モールの中にあった Virgin Mobile のブースに寄ってみた。 Virgin は Bell Mobility の子会社。 2005年3月に Bell の MVNO としてスタートしたが、 2009年7月に Bell に買収された。

fido もそうだが、MVNO のように見えて実は子会社というのはいかがなものか。 子会社では親会社に遠慮して価格競争を仕掛けられない。 これでは競争とは言えないし、 実際、料金体系が各社横並びで高止まりしているように感じられる。

Virgin のプリペイドSIM には、 月額プラン ($15, $25, $30, $35, $50, $60, $70) と従量プラン (Pay per Use) がある。 月額プランのうち、 $15 〜 $35 のプランは通話&SMS のためのプラン (例えば $35 のプランは 500分の通話が含まれていて SMS は無制限に使える) で、 データ通信に関しては従量プランと同じ。 したがってデータ通信がメインの場合は、 従量プランあるいは $50 以上の月額プランが選択肢となる。

従量プラン$25$50$60$70
ローカル通話40¢/分40¢/分含500分無制限無制限
長距離通話80¢/分80¢/分ローカル扱い無制限無制限
夜間通話放題無し7pm-7am5pm-7am全日全日
国内SMS30¢/通無制限無制限無制限無制限
国際SMS35¢/通無制限無制限無制限無制限
データ通信$1/10MB$1/10MB500MB
5¢/MB
1GB
2¢/MB
2GB
2¢/MB

従量プランだとデータ通信は 10MB あたり $1 もする (1GB 使ったら $100 !) ので、 アドオンとして以下のデータパッケージを追加することが必須。

データ量月額料金超過料金
100MB$1015¢/MB
500MB$255¢/MB
1GB$355¢/MB

1GB のデータパッケージが $35 (超過した場合は 5¢/MB) と、 fido より $5 高いので従量プランの優位性が減っている。 fido の場合は 75分以上通話すると $60 の月額プランより割高になるが、 Virgin の場合は 63分以上で割高になる (とはいえ、実際はそんなに通話しなかったが)。

しかも今は WIND SIM を入れた Nexus 5 を Wi-Fi ルータとして使えるので、 この Nexus 5 をテザリング状態にしたまま持ち歩けば (ディスプレイを点灯させなければ、 テザリングしっぱなしでも丸一日くらいは電池がもつ)、 データ通信量を 1GB 以下に抑えることも容易だろう。 月額プランにすれば refill せずに済みそう。 というわけで $60 の月額プランを選んだ (が、通話は 50分にも満たなかったので実は従量プランのほうが安かった)。

月額料金以外に何を請求されるのか?と身構えていたら、 SIM 代 $5 のみだった。 支払額は WIND と同じ。 アドオンは fido より高いが、 単月なら従量プランでも fido より安くなりそう。 消費税と合わせて $72.79 (約 5800円)。 WIND とほぼ同じ。 契約した 3つのキャリアがほとんど同じ額というのはいかがなものか。 もっとメリハリの効いたプランを出して競争すればいいのにと思う。

SIM を Galaxy Note 4 (SM-N9100) に入れる。 開通するまで 1時間くらいかかるらしい。 ショッピングモールで食事していると、 それまで圏外だった Note 4 に突然 SMS が届いて LTE が使えるようになっていた。 Virgin は、 LTE が Band 2 (1900 MHz) と Band 4 (1700/2100 MHz)、 一部のエリアで Band 7 (2600 MHz)。 3G が Band II (1900 MHz) と Band V (850 MHz)。

この Note 4 は中国版なので Band 4 はサポートしていないが、 Band 2 と Band 7 をサポートしているので特に問題はない。 とはいえ、 ケータイを北米で使いたい場合は Band 4 / IV は押えておくべきで、 今後は Band 4 / IV をサポートしていない端末は避けるようにしたい。

fido の My Account と違って、 Virgin の My Account ページはきちんと機能し、 プラン変更も可能だった。 My Account ページは、 Virgin の 10桁の電話番号 (10-digit Virgin Mobile phone number) と 4桁の PIN (4-digit PIN) を使ってログインできる。 初期 PIN (temporary PIN) は契約時に SMS で送られてくる。 ただし、 初回ログインの時のみ 「Order Confirmation or Agreement Number」 の入力を求められる。 え? 「注文確認番号あるいは契約番号」? なにそれ?

実は私は 「Order Confirmation or Agreement Number」 が何のことか分からず、 カナダ滞在中は My Account ページにアクセスすることができなかった。 日本に帰国した後 (このブログを書くために :-) Virgin Mobile のお店でもらった書類を整理していて、 「IMPORTANT MEMBER INFO.」 と題した紙に、 「Order Number:」 に続けて 「英字 1文字+数字9桁」 の記載を発見した。 写真 ↓ 赤丸で囲った文字列 (「C114843333」)。

Virgin Important Member Info.

すでにカナダを離れているので、 いまさらログインできたところで意味はないのだが (^^;)、 試しにこの 「C114843333」 を 「Order Confirmation or Agreement Number」 の欄に入力してみると、 驚いたことに無事ログインできて、 PIN の変更を促された。 で、 従量プランに変更してみた次第。

「Order Confirmation Number」 と 「Order Number」 とでは見た目の印象が異なるので、 用語を統一しておいてくれたほうが親切だろうとは思う。 そもそも 「Order Number」 といいながら、先頭は 「Number」 ではなくてアルファベットだったりするし (>_<)。 あるいは同一 Web ページ上に、 どこを (どの書類を) 見れば 「Order Number」 が載っているか補足説明があれば、 (私のように) 「Order Confirmation or Agreement Number」 が何のことか分からず往生してしまう人を減らせるだろうにと思う。

自分の失態を棚に上げて難癖をつけているようで心苦しいが (^^;、 契約時にもらった書類と、 契約時に送られてきた SMS の両方が無いとログインできないというのは、 難易度が高すぎるように思う。 My Account ページを使ってみようなどと思うのは、 契約後しばらく経ってからのはずで、 そんな書類をもらったことなんか忘れてる (← 私 orz) 人も多そうだ。 それでも SMS ならば履歴を見ればすぐ発掘できるが、 リアルな紙はどこかへいってしまう。

ちなみに fido も WIND も、 書類記載のデータは一切必要なく、 SMS で送られてきた PIN だけでログインできる。 100万歩譲って、 書類記載のデータが認証の際に求められることを許容するとしても、 それは 「ユーザID」 とか 「認証ID」 とか、 あるいは 「パスワード」 とかの名称であるべきだ。 「Order Number」 という名称のものが認証に必要だなんて誰が想定できるだろうか?

My Account ページにログインできれば、 カナダ滞在中は月額プランで利用し、 日本に帰って来てから従量プランに変更して月額の出費を抑え、 次にカナダへ行く直前に月額プランへ変更する、 といった使い方もできる。 ただし、 カナダで発行されたクレジットカードでないと refill できないので、 refill カードが必要。 カナダを発つ前に refill カードを買っておく必要がある (失効しないよう注意!)。 $100 を refill すれば 1年間 SIM を維持できる。

ZenFone3 Deluxe を Nougat 7.0 にしたらデータ通信できなくなった 〜 アンロックして Android 6 に戻す 〜

$
0
0

Nexus 5X が突然壊れて以来、 私はメインのスマホとして ZenFone 3 Deluxe ZS570KL を使っている。 購入時の OS は Android 6.0 Marshmallow だったが、 今年 3月にシステム更新のお知らせが表示されたので、 更新を行なったら Android 7.0 Nougat WW-5.14.44.1898 になった (3月26日)。

ところが!

この更新によって通話もデータ通信もできなくなってしまった。 LTE ネットワークへ接続できなくなっていて、 アンテナ・ピクトも表示されない (バツ印が付く)。 最初 SIM の接触不良を疑ったのだが、 SIM を入れ直しても接続できないまま。

更新作業は Wi-Fi のあるところ (自宅) で行なうわけで、 (モバイル) データ通信できなくても Wi-Fi でデータ通信できるから気付きにくい。 まさか更新で通話やデータ通信ができなくなってしまっているとは思わないので、 翌日 (3月27日) も気付かずに出かけてしまい、 出先でデータ通信できないことに初めて気付いて往生した。 出かける前に気付いていれば、 昔のスマホ (Nexus 5X とか) に SIM を入れ替えて使うこともできただろうに。

LTE で接続できないなら、 W-CDMA で接続してみてはどうだろう?と思ったので、 設定メニューで 「モバイルネットワーク」 を選び、 「有線ネットワークタイプ」 を 「2G/3G/4G」 から 「2G/3G」 へ変更 (つまり LTE 接続を抑制) してみた。 すると無事 3G (W-CDMA) ネットワークへ接続して (アンテナ・ピクトが表示されて) 通話ができるようになった。 しかし 3G でもデータ通信はできない。

もちろん、 SIM を別のスマホに入れると何の問題もなく通話もデータ通信もできるので、 SIM や設定の問題ではなく、 Nougat な ZS570KL の問題。

この時点では ZS570KL の root 権限を取得していなかった (まだブートローダをアンロックしていなかった) ので、 できることは限られている。 仕方ないので、 設定メニューの 「バックアップとリセット」 で 「データの初期化」 を行なってみると、 LTE へ接続できるようになった (3月28日)。

Android のバージョンを上げるたびに初期化を強要されてはかなわないと思いつつも、 いちおうこれで一件落着なのだが、 せっかく初期化したのだから、 この機会に (自分流にカスタマイズする前に) ブートローダをアンロックすることにした。

昨年 9月11日に発売開始した ZS570KL だが、 アンロックの方法を ASUS が公開したのは半年近くが経過した 2月16日になってから。 アンロックツール ZS570KL_UnlockTool_V1.1.zip が ASUS の Driver & Tools ページからダウンロードできる。

ASUS UnlockTool

アンロックを行なうとデータの初期化が行なわれてしまうので、 アンロックするならカスタマイズする前の方がいい。 本当は購入直後にアンロックするのが一番なのであるが、 当時はアンロックツールが (少なくとも公式には) 公開されていなかったので、 いままでアンロックせずに使い続けてきた次第。

ASUS のページからダウンロードした zip ファイルを展開して得られた UnlockTool-9.0.0.29_ZS570KL.apk を ZS570KL へインストールして実行すると、 警告画面 (→ 右図) が表示される。

アンロック (ロック解除) すると一切の保証が無くなるだけでなく、 ソフトウェアのアップデートも受けることができなくなると書いてある。 もちろん、 同ページから最新のソフトウェアをダウンロードすること自体は (ロック解除とは関係なく) 可能なので、 ここで言う 「ソフトウェアのアップデート」 とは、 あくまで自動で行なわれる更新のことだろう。

「押してデバイスのロック解除を行う」 を押すと、 再起動と初期化が行なわれる。 これでブートローダがアンロックされ、 ボリューム大ボタンを押しながら電源を入れると、 ブートローダ・モードへ入ることができる。

Nexus 等のブートローダ・モードは、 起動する OS を選べたりするが、 ZS570KL のブートローダ・モードは、 通常の起動画面 (ASUS ロゴ) と変わらず、 ブートローダ・モードに入ってるか外見上は区別できないので注意が必要。 ZS570KL と USB ケーブルでつないだ PC 上で fastboot コマンドを実行することで、 任意のブート・イメージ (TWRP など) を起動したり、 フラッシュメモリへ書込んだりすることが可能。

きちんと動作する ZS570KL 用の TWRP がちょうど公開されていた (3月19日, これ以前のバージョンは画面が逆になるなど、いろいろ不具合があった) ので、 ZS570KL をブートローダ・モードにした上で、 fastboot コマンドを使って起動してみる:

$ fastboot boot twrp-3.1.0-0-Z016D-20170319-N.img 
downloading 'boot.img'...
OKAY [  0.777s]
booting...
OKAY [  0.394s]
finished. total time: 1.171s
$ 

ちょっと使ってみた限りでは、 問題無く使えるようだ。 早く TWRP 公式ページからダウンロードできるようになることを望む。 TWRP を ZS570KL の recovery パーティションへ書込んでおくと、 ボリューム小ボタンを押しながら電源を入れることで、 (PC とつながなくても) TWRP を起動できる。

$ fastboot flash recovery twrp-3.1.0-0-Z016D-20170319-N.img 
target reported max download size of 536870912 bytes
sending 'recovery' (19980 KB)...
OKAY [  0.777s]
writing 'recovery'...
OKAY [  0.021s]
finished. total time: 0.797s
$ 

アンロックしたらついでに root 権限も取得したくなるのが人情なので、 SuperSU v2.79 SuperSU-v2.79-201612051815.zip を TWRP からインストールした。

と、 ここまでは順調だったが、 ある日 (4月4日) 出先で唐突にデータ通信できなくなった。 外出中にデータ通信できなくなると往生する (前述したように 3月27日に体験済み) ので、 こんなこともあろうかと ZS570KL には普段使っている IIJmio の nano SIM の他に、 非常用として Aeon Mobile の micro SIM も入れていた。 電話の着信は両方の SIM で可能 (DSDS, Dual SIM Dual Standby) で、 電話の発信とデータ通信は、 どちらの SIM で行なうか選ぶ方式。 データ通信を Aeon Mobile へ切り替えると、 無事データ通信できた。

なぜ Aeon Mobile ではデータ通信できるのに、 IIJmio ではできないのだろう? と思いつつも、 IIJmio でもアンテナ・ピクトは表示されるし、 IIJmio の電話番号に着信できるので、 そのまま使い続けた。

もちろん、 Aeon Mobile なら他の SIM でもデータ通信できるのか?とか、 micro/nano どちらの SIM トレイに入れるかでデータ通信の可否が変わるのか?とか、 いろいろ確認したいことはあり、 その都度いろんな実験をしている。 が、結論から言えばいずれも直接の関係は無さそうだった。 わざわざ他キャリアから Aeon Mobile へ MNP して新たに nano SIM を作ってみたがデータ通信できなかったし、 IIJmio や mineo の nano SIM にゲタを履かせて micro SIM トレイに入れてもデータ通信できるようにはならなかったし、 nano SIM トレイでも例えば香港3 の nano SIM なら (ローミングで) データ通信できた。

そんなある日 (4月13日)、 IIJmio の電話番号へ電話がかかってきたので出たら、 音声が互いに聞こえなかった。 驚いて Aeon Mobile で通話実験したら、 Aeon Mobile でも着信/発信ともに音声が聞こえなくなっていた。

少なくとも 4月11日までは普通に通話できていたので、 2日間の間に何があったか思い出そうとしたが全く心当たりがなかった。 ある日突然、通話できなくなるなんてことが起きると大変困る。

# 4月21日追記: TWRP で DSP をリストアすると発症するらしい。
# 音声が聞こえない症状については Nougat への更新とは無関係だった。

データ通信ができないのはモバイル・ルータを持ち歩くことで対処できるが、 通話ができないのでは話にならない。 直ちに ZS570KL の使用を中止し、 IIJmio SIM を Pixel に入れて Pixel を持ち歩くことにした。 Pixel は Band 19 をサポートしていないので docomo (の MVNO) では使いにくく、 (日本では) あまり使っていなかった。

というわけで ZS570KL の使用を中止したので、 さらに思いきった実験ができるようになった。 手始めに再度初期化してみることにした。 今回は root 権限を取得済なので、 バックアップ/リストアが手軽にできる。 初期化してデータ通信が復活すれば、 徐々に設定を変えていくことで原因の切り分けも可能だろう。

ところが!

初期化したのに IIJmio のデータ通信は復活しなかった (4月14日)。 通話も着信/発信はできるけど音声が伝わらない。 ZS570KL 単体の問題ではなくて、 LTE ネットワークとの相性も関係あるのか? ますます何が原因なのか分からなくなってしまった。

初期化ではなく Nougat をクリーン・インストールすれば、 通話もデータ通信も元通りできるようになるのかもしれない。 しかしいつ同じ症状が再発しないとも限らない。 突然データ通信できなくなったり、 音声が聞こえなくなったりするようでは安心して使えない。 しかもこの Nougat にはメニューボタンが使えないという (私にとっては) 重大なバグもある (本来は 「ASUS カスタマイズ設定」 でマルチタスクボタンの長押しでメニューを表示するように設定できるはずだが、 この Nougat では機能しない)。

通話やデータ通信ができなくなるのはおそらくバグだろうし、 メニューボタンのほうは明らかにバグ。 ついでに言うと、 カメラが EXIF に記録する位置情報が壊れていて、 他のソフト (Googleフォトを含む) で位置情報が認識できないというバグもある。 そのうち修正版が公開されるのだろうと思っていたが、 Android 7 Nougat WW-5.14.44.1898 が公開されてから一ヶ月がたつのに何も発表されない。 ASUS はバグを認識していないのか?

# 4月19日追記: WW-5.14.44.2096 が公開された。が、症状は変わらず (>_<)

事ここに至っては Nougat の使用を諦め、 Android 6 Marshmallow に戻すしかないと決断した。 といっても、 バージョンを戻す方法は (ASUS 公式には) 提供されていない。 が、 アンロックした ZS570KL であれば、 フラッシュメモリに直接書込めるので、 Marshmallow のイメージを書込めばよい。 つまりクリーン・インストール。

このブログを書いていて (いまごろ orz) 気付いたが、 上記 TWRP のダウンロードページ に、 「Downgrade_to_4.12.40.1698」 というディレクトリがあった。 Android 6 に戻したいという (私と同様な) 人が多いのだろう。

まず ASUS の Driver & Tools ページから Marshmallow の最後のバージョン WW-4.12.40.1698 の zip アーカイブをダウンロード。これを展開する:

senri:~/ZS570KL $ unzip UL-Z016-WW-4.12.40.1698-user.zip
Archive:  UL-Z016-WW-4.12.40.1698-user.zip
signed by SignApk
 extracting: system.patch.dat        
  inflating: META-INF/com/android/metadata  
  inflating: META-INF/com/google/android/update-binary  
  inflating: META-INF/com/google/android/updater-script  
  inflating: boot.img                
  inflating: file_contexts           
  inflating: firmware-update/NON-HLOS.bin  
  inflating: firmware-update/adspso.bin  
  inflating: firmware-update/cmnlib.mbn  
  inflating: firmware-update/cmnlib64.mbn  
  inflating: firmware-update/devcfg.mbn  
  inflating: firmware-update/emmc_appsboot.mbn  
  inflating: firmware-update/hyp.mbn  
  inflating: firmware-update/keymaster.mbn  
  inflating: firmware-update/mdtp.img  
  inflating: firmware-update/pmic.elf  
  inflating: firmware-update/rpm.mbn  
  inflating: firmware-update/tz.mbn  
  inflating: firmware-update/xbl.elf  
  inflating: logs/version            
  inflating: system.new.dat          
  inflating: system.transfer.list    
  inflating: META-INF/com/android/otacert  
  inflating: META-INF/MANIFEST.MF    
  inflating: META-INF/CERT.SF        
  inflating: META-INF/CERT.RSA       
senri:~/ZS570KL $ 

zip アーカイブの中の boot.img が android のブート・イメージ。 このファイルの中に、 Linux カーネルと initramfs が含まれている。 これを boot パーティションへ書込む:

senri:~/ZS570KL $ fastboot flash boot boot.img
target reported max download size of 536870912 bytes
sending 'boot' (13305 KB)...
OKAY [  0.524s]
writing 'boot'...
OKAY [  0.021s]
finished. total time: 0.545s
senri:~/ZS570KL $ 

zip アーカイブの中の firmware-update ディレクトリ下の 13個のファイルが、 ブートローダ等のファームウェア。

拡張子が *.bin のファイル (2個) はファイルシステムのイメージ。 NON-HLOS.bin は /firmware ディレクトリに、 adspso.bin は /dsp ディレクトリに、 それぞれ読み取り専用でマウントされる。 どちらも AMSS モデム関連のディレクトリ。 NON-HLOS.bin は modem パーティションへ、 adspso.bin は dsp パーティションへ、 それぞれ書込む:

senri:~/ZS570KL $ fastboot flash modem firmware-update/NON-HLOS.bin
target reported max download size of 536870912 bytes
sending 'modem' (81916 KB)...
OKAY [  3.166s]
writing 'modem'...
OKAY [  1.895s]
finished. total time: 5.060s
senri:~/ZS570KL $ fastboot flash dsp firmware-update/adspso.bin
target reported max download size of 536870912 bytes
sending 'dsp' (16384 KB)...
OKAY [  0.652s]
writing 'dsp'...
OKAY [  0.401s]
finished. total time: 1.053s
senri:~/ZS570KL $ 

拡張子 *.mbn は multi-boot binary ファイル (8個) で、 ARM の ELF (Executable and Linkable Format) オブジェクト。 keymaster.mbn と cmnlib.mbn は 32bit の共通ライブラリで、 keymaster は証明書の暗号鍵を管理する。 cmnlib64.mbn は 64bit の共通ライブラリ。

rpm.mbn と emmc_appsboot.mbn は 32bit ARM の ELF 実行ファイル。 rpm は 「resources & power manager」 を意味し、 プライマリ・ブートローダの役割を果たす。 emmc_appsboot はセカンダリ・ブートローダで、 このブートローダが android のカーネルを起動する。

残りの devcfg.mbn, hyp.mbn, tz.mbn は 64bit ARM の ELF 実行ファイル。 機能の詳細は分からないが、 devcfg は device config、 hyp は hypervisor、 tz は trust zone のことだと思われる。

詳細不明なファイルが多いが (^^; 以上 8個の *.mbn ファイルを、 それぞれ対応するパーティションへ (emmc_appsboot.mbn は aboot パーティションへ、 それ以外はファイル名と同じ名称のパーティションへ) 書込む:

senri:~/ZS570KL $ fastboot flash cmnlib firmware-update/cmnlib.mbn
target reported max download size of 536870912 bytes
sending 'cmnlib' (200 KB)...
OKAY [  0.029s]
writing 'cmnlib'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.060s
senri:~/ZS570KL $ fastboot flash cmnlib64 firmware-update/cmnlib64.mbn
target reported max download size of 536870912 bytes
sending 'cmnlib64' (254 KB)...
OKAY [  0.030s]
writing 'cmnlib64'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.061s
senri:~/ZS570KL $ fastboot flash devcfg firmware-update/devcfg.mbn
target reported max download size of 536870912 bytes
sending 'devcfg' (39 KB)...
OKAY [  0.040s]
writing 'devcfg'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.070s
senri:~/ZS570KL $ fastboot flash aboot firmware-update/emmc_appsboot.mbn
target reported max download size of 536870912 bytes
sending 'aboot' (749 KB)...
OKAY [  0.047s]
writing 'aboot'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.077s
senri:~/ZS570KL $ fastboot flash hyp firmware-update/hyp.mbn
target reported max download size of 536870912 bytes
sending 'hyp' (257 KB)...
OKAY [  0.038s]
writing 'hyp'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.069s
senri:~/ZS570KL $ fastboot flash keymaster firmware-update/keymaster.mbn
target reported max download size of 536870912 bytes
sending 'keymaster' (220 KB)...
OKAY [  0.032s]
writing 'keymaster'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.062s
senri:~/ZS570KL $ fastboot flash rpm firmware-update/rpm.mbn
target reported max download size of 536870912 bytes
sending 'rpm' (224 KB)...
OKAY [  0.039s]
writing 'rpm'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.069s
senri:~/ZS570KL $ fastboot flash tz firmware-update/tz.mbn
target reported max download size of 536870912 bytes
sending 'tz' (1600 KB)...
OKAY [  0.084s]
writing 'tz'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.114s
senri:~/ZS570KL $ 

拡張子 *.elf は 64bit ARM の ELF 実行ファイル (2個)。 *.mbn との違いは不明 (誰か教えて!)。 pmic.elf は power management IC のこと? xbl.elf は eXtended bootloader のことだろう。 それぞれ対応する同名のパーティションへ書込む:

senri:~/ZS570KL $ fastboot flash pmic firmware-update/pmic.elf
target reported max download size of 536870912 bytes
sending 'pmic' (41 KB)...
OKAY [  0.040s]
writing 'pmic'...
(bootloader) partition_need_check.
OKAY [  0.030s]
finished. total time: 0.070s
senri:~/ZS570KL $ fastboot flash xbl firmware-update/xbl.elf
target reported max download size of 536870912 bytes
sending 'xbl' (1780 KB)...
OKAY [  0.090s]
writing 'xbl'...
(bootloader) partition_need_check.
OKAY [  0.034s]
finished. total time: 0.124s
senri:~/ZS570KL $ 

mdtp.img は Mobile Device Theft Prevention のこと? ファイルの形式すら不明だが、 mdtp パーティションへ書込む:

senri:~/ZS570KL $ fastboot flash mdtp firmware-update/mdtp.img
target reported max download size of 536870912 bytes
sending 'mdtp' (8086 KB)...
OKAY [  0.324s]
writing 'mdtp'...
OKAY [  0.218s]
finished. total time: 0.542s
senri:~/ZS570KL $ 

以上で zip アーカイブの中の firmware-update ディレクトリ下の 13個のファイルの書き込みが完了した。

そして、 zip アーカイブの中の system.new.dat と system.transfer.list が、 android を起動したときに /system にマウントされる ext4 ファイルシステムのイメージ。 つまり Android OS の本体。 3.6GB ほどあり、 zip アーカイブの中の他のファイルと比べてケタ違いに大きい。

一般にファイルシステムのイメージは、 データが無い部分が散在する 「疎なファイル」(sparse file) になる。 フラッシュメモリに書込むときに、 データが無い 「0」 ばかりのブロックを書込むのは効率が悪いので、 「0」 なブロックを飛ばしたデータのファイル system.new.dat と、 そのデータをフラッシュメモリのどこへ書込むか指定するファイル system.transfer.list に分かれている。

残念なことに fastboot コマンドは system.transfer.list を扱えないので、 あらかじめ system.new.dat と system.transfer.list から ext4 ファイルシステムのイメージを作成しておいて fastboot コマンドに与える必要がある。 sdat2img コマンドを使うと、 system.new.dat と system.transfer.list から、 (疎な) イメージ system.img を作成できる:

senri:~/ZS570KL $ sdat2img system.transfer.list system.new.dat system.img
sdat2img binary - version: 1.0

Android Marshmallow 6.0 detected!

Copying 32770 blocks into position 0...
Copying 2 blocks into position 33009...
Copying 32016 blocks into position 33519...
Copying 2 blocks into position 65536...
Copying 32257 blocks into position 66046...
Copying 2 blocks into position 98304...
Copying 2 blocks into position 98545...
Copying 32016 blocks into position 99055...
Copying 2 blocks into position 131072...
Copying 32257 blocks into position 131582...
Copying 2 blocks into position 163840...
Copying 2 blocks into position 164081...
Copying 32016 blocks into position 164591...
Copying 2 blocks into position 196608...
Copying 32257 blocks into position 197118...
Copying 2 blocks into position 229376...
Copying 2 blocks into position 229617...
Copying 32016 blocks into position 230127...
Copying 2 blocks into position 262144...
Copying 32257 blocks into position 262654...
Copying 2 blocks into position 294912...
Copying 2 blocks into position 295153...
Copying 32016 blocks into position 295663...
Copying 2 blocks into position 327680...
Copying 32257 blocks into position 328190...
Copying 2 blocks into position 360448...
Copying 32257 blocks into position 360958...
Copying 2 blocks into position 393216...
Copying 32257 blocks into position 393726...
Copying 2 blocks into position 425984...
Copying 32257 blocks into position 426494...
Copying 2 blocks into position 458752...
Copying 32257 blocks into position 459262...
Copying 2 blocks into position 491520...
Copying 32257 blocks into position 492030...
Copying 2 blocks into position 524288...
Copying 32257 blocks into position 524798...
Copying 2 blocks into position 557056...
Copying 32257 blocks into position 557566...
Copying 2 blocks into position 589824...
Copying 32257 blocks into position 590334...
Copying 2 blocks into position 622592...
Copying 32257 blocks into position 623102...
Copying 2 blocks into position 655360...
Copying 32257 blocks into position 655870...
Copying 2 blocks into position 688128...
Copying 32257 blocks into position 688638...
Copying 2 blocks into position 720896...
Copying 32257 blocks into position 721406...
Copying 2 blocks into position 753664...
Copying 32257 blocks into position 754174...
Copying 2 blocks into position 786432...
Copying 32257 blocks into position 786942...
Copying 2 blocks into position 819200...
Copying 2 blocks into position 819441...
Copying 32016 blocks into position 819951...
Copying 2 blocks into position 851968...
Copying 32250 blocks into position 852478...
Copying 2 blocks into position 884736...
Copying 2 blocks into position 884977...
Copying 32016 blocks into position 885487...
Copying 2 blocks into position 917504...
Copying 2820 blocks into position 918014...
Copying 2 blocks into position 950272...
Copying 24508 blocks into position 950782...
Copying 7689 blocks into position 975291...
Skipping command zero...
Skipping command erase...
Done! Output image: ~/ZS570KL/system.img
senri:~/ZS570KL $ 

そして、作成した (疎な) system.img を system パーティションへ書込む。 fastboot コマンドは、 与えられた system.img が疎なファイルであることを認識し、 まず system パーティション全体を erase した後、 必要なブロックのみを ZS570KL へ転送して書込んでいる:

senri:~/ZS570KL $ fastboot flash system system.img
target reported max download size of 536870912 bytes
Invalid sparse file format at header magi
erasing 'system'...
OKAY [  0.002s]
sending sparse 'system' (513215 KB)...
OKAY [ 20.270s]
writing 'system'...
OKAY [  4.815s]
sending sparse 'system' (524224 KB)...
OKAY [ 20.871s]
writing 'system'...
OKAY [  5.201s]
sending sparse 'system' (523318 KB)...
OKAY [ 20.565s]
writing 'system'...
OKAY [  4.793s]
sending sparse 'system' (486869 KB)...
OKAY [ 19.371s]
writing 'system'...
OKAY [  4.688s]
sending sparse 'system' (513436 KB)...
OKAY [ 20.188s]
writing 'system'...
OKAY [  4.710s]
sending sparse 'system' (512989 KB)...
OKAY [ 20.225s]
writing 'system'...
OKAY [  4.705s]
sending sparse 'system' (506575 KB)...
OKAY [ 20.021s]
writing 'system'...
OKAY [ 22.541s]
sending sparse 'system' (67808 KB)...
OKAY [  2.684s]
writing 'system'...
OKAY [  0.475s]
finished. total time: 196.124s
senri:~/ZS570KL $ 

「header magi」 とは何ぞ? 東方より来たりし三賢者? と思ったら、 「header magic」 と表示しようとしてるのだけど、 fastboot コマンドの内部のバッファの大きさが 1 バイト足らなくて、 1 文字欠けてしまったらしい (まあ magic の語源は magi なので当たらずとも遠からじだが)。 system/core/libsparse/sparse_read.c を見ると、

static void verbose_error(bool verbose, int err, const char *fmt, ...)
{
	char *s = "";
	char *at = "";

…… (中略) ……

		size = vsnprintf(NULL, 0, fmt, argp);

…… (中略) ……

		at = malloc(size + 1);

…… (中略) ……

		vsnprintf(at, size, fmt, argp);

…… (中略) ……

		if (err == -EINVAL) {
			sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);

…… (中略) ……

struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
{

…… (中略) ……

	if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
		verbose_error(verbose, -EINVAL, "header magic");
		return NULL;
	}

などと書いてある。 せっかくメッセージの長さを調べて、 ちゃんと 1 バイト大きい (size + 1) バッファを確保しているのに、 vsnprintf の第2引数に (size + 1 ではなく) size を与えているために、 最後の 1 文字が欠けてしまうというバグ (>_<) どうしてこんな単純なバグが放置されているのか?

system/core/libsparse/sparse_format.h によると、 「sparse file format」 というファイル形式があって、 その magic (フォーマット識別子) は 0xed26ff3a であるらしい。 それなら何故 system.new.dat と system.transfer.list というファイル形式を使っているのだろう? sparse file format にすれば 1つのファイルで済むように思われるのだが...

max download size (前述の例だと 536870912 bytes) を超えるファイルを書込もうとすると、 fastboot はそのファイルが 「sparse file format」 であることを期待して、 magic が違うと 「Invalid sparse file format at header magic」 と表示する。

この場合、 通常のファイルとして読み込み直して、 内部で 「sparse file format」 へ変換するコード (sparse_read.c) になっている。 この場合、 そのファイルが疎か否かは関係なく、 単に 1ブロックが全て同じ値かどうか調べているので、 (疎でない) 通常のファイルを与えた場合も、 必要なブロックのみを転送して書込む仕様になっていた。

以上で、 Android 6.0 Marshmallow WW-4.12.40.1698 のクリーン・インストールが完了した (4月14日)。 数日間使っているが、全く何の問題も起きていない。 3月27日に問題が発覚してから 19日間、 さんざん苦しめられていたのが嘘のようだ。 もっと早く Marshmallow へ戻せばよかった。 OS のメジャーバージョンを上げるときは、 元に戻す方法を確認してからにすべきと改めて痛感した。

4月19日 11:00 追記:

このブログを書き上げてから再度 ASUS の Driver & Tools ページを見に行ったら、 Nougat の新バージョン WW-5.14.44.2096 が公開されていた。 タイムスタンプは 2017/04/17 になっている。

さて、どうしたものか? (^^;

現状、まったく困っていないからなぁ...

4月19日 21:00 追記:

TWRP でバックアップしておけば速攻で元に戻せるので、さくっと新しい Nougat WW-5.14.44.2096 を試してみた。結果は、... ぜんぜんダメ orz

IIJmio nano SIM ではデータ通信できず、Aeon Mobile micro SIM だとできる。メニューボタンのバグは直っていない。かろうじて、カメラの位置情報のバグだけは直っているみたいだけど、それだけじゃ話にならない。

速攻で Marshmallow WW-4.12.40.1698 に戻した。

IIJmio でデータ通信できない件は LTE ネットワークとの相性と言えるかもしれないけれど、メニューボタンのバグが一ヶ月たっても直せないというのはどういうことなんだろう?

4月21日 追記:

通話で互いに音声が聞こえなくなるのは、 TWRP で DSP をリストアしたのが原因だった。 つまり、 音声が聞こえない症状については Nougat への更新とは無関係だった。

DSP は Nougat や Marshmallow の更新ファイル中に含まれる adspso.bin で、 dsp パーティションに書き込まれ、 /dsp ディレクトリに (読み込み専用で) マウントされる。

dsp パーティションは、 TWRP でバックアップすることができるが、 リストアすると SELinux のコンテキストが変わってしまうため android から参照できなくなってしまう。

dsp パーティションには音声コーデックのライブラリ等が含まれ、 これが android から読めなくて通話の音声が聞こえなくなる。

dsp パーティションは読み込み専用でマウントされるため、 android の動作によって内容が変化することは無い。 したがって TWRP でバックアップする必要は無い。


カナダで Project Fi を使ってみた 〜 現地でプリペイドSIMを買うより安いし、同時に複数のスマホで使える

$
0
0

昨年に続いて今年も、 日本の梅雨 (と夏の暑さ) から逃れるべく バンクーバーに滞在した昨年の滞在では fido, WIND, Virgin のプリペイド SIM を買ったが、 今年 1月に米国を訪れたとき Project Fi を契約したので、 Project Fi の SIM をバンクーバーでも使ってみた。

Project Fi は米国外で使っても (米国内で使う場合と同じく) データ通信が $10/GB (1MB あたり 1セント) という、 リーズナブルかつシンプルな料金体系。 カナダだと、 何故かキャリアのプリペイド SIM を買うより安くつく。 カナダでは Telus や Bell の通信設備を使ってローミングサービスしているようだが、 Telus や Bell のプリペイド SIM よりも料金が安いのは何故なのか?

バンクーバーのいたるところにキャリアの店舗があるので、 適当に入ってみた店で、 「いま Google の Project Fi を使っているのですが、 Project Fi より安くなるプランありますか?」と (もちろん英語で) 聞いてみたら、 「Project Fi みたいに安いプランはありません!」 と即答されてしまった。(^^;

しかも、 Project Fi の大きな特徴として、 追加料金無しで Data-only SIM を 9枚まで追加できる。 妻のスマホに Data-only SIM を入れておけば、 Project Fi 一契約だけで (私と妻、二人分の) 用が足りてしまう。 Data-only SIM を 9枚入手すれば、 10人で共有できる?

なお、 Project Fi の (通話可能な) SIM は、 Pixel や Nexus に入れて使うことが推奨されているが、 ふつうのスマホに入れても通話やデータ通信が可能。 私はバンクーバー滞在中ずっと、 ZenFone 3 Deluxe (ZS570KL) に入れていた。 もちろん Pixel も持ってはいるのだが、 ZenFone のほうがメモリが多く、 また root 権限を取得済なのでいろいろ使い勝手が良い。

もちろん、 いくら安くても通信速度が遅ければ意味がないので、 その時は現地でプリペイド SIM を買うつもりでバンクーバーを訪れたのだが、 Project Fi でもしっかり LTE の電波をつかんでくれた。 昨年使った fido や Virgin のプリペイド SIM と比べてなんら遜色がない (WIND は 3G のみ)。 結局 27日間のバンクーバー滞在で、 一度も現地 SIM を買おうという気にはならなかった。

Project Fi のページで確認すると、 7月の 24日間のデータ通信量が 7.36GB (私が 2.62GB、妻が 4.75GB) だった。 ホテルの Wi-Fi が使い物にならないほど遅かったので、 ホテルに居るときまで Project Fi を使うことになり、 ふだん日本で 1ヶ月に使うデータ通信量より多くなってしまった。

最近のアプリ (特に facebook, twitter, Instagram など) は、 画像や動画をストレスなくどんどん表示するために、 勝手にどんどん通信するのでいかがなものかと思う (私はこの手の通信しまくりアプリは一切インストールしない)。 滞在したホテルは 「無料 Wi-Fi 完備」 をうたっていたが、 宿泊客がこの手の通信しまくりアプリを使うためか、 夕方以降は全くといっていいほど通信できなくなってしまっていた。

日ごとのデータ通信量は次のような感じ:

Project Fi Current cycle

日によってばらつきが大きいが、 平均 300MB くらい。 二人分なのでこんなもの? 7.364GB で $73.64 (約 8250円)。 国際電話の通話料が 21分間で $4.20 (約 470円)。 さらに Project Fi の基本料金 $20 と税金等 ($6 くらい?) がかかる。 まだ請求が来ていない (〆日は 8月2日) が、 合計 $104 くらい?

Project Fi で、 カナダから米国の toll free 番号に電話できるのか? と思いつつ 1-888 から始まる電話番号に電話したら繋がったので、 てっきり無料通話かと思って安心して長電話してしまった。 有料なら繋がらないで欲しい (>_<)。 繋がらなければ、 米国外からかけるときに使えるコレクトコール番号を使ったのに。
1分あたり 20セントという国際電話料金は、 ケータイの通話料の感覚からすると安いが、 Google Voice の感覚からすると高い。 ZenFone 3 に Project Fi の SIM を入れて通話したのだが、 Pixel (あるいは Nexus 5X) に入れて通話すれば無料だったのかも?
少なくともハングアウトで発信していれば無料だった。

昨年のバンクーバー滞在は 14日間と今年の半分の日数だったが、 fido の SIM に CA$89.60 (約 7900円)、 Virgin の SIM に CA$72.79 (約 6400円)、 WIND (現 Freedom Mobile) の SIM に CA$72.80 (約 6400円) 支払った。 もし今回 Project Fi がなくて昨年と同様にプリペイド SIM を買っていたら、 昨年の倍、4万円近くかかってしまったかもしれない。

カナダでは、 どのキャリアも 1GB 程度使おうとすると、CA$70 以上かかってしまう。 各キャリア横並びの料金で、 格安SIM 登場前の日本みたいな感じ。 キャリア間の競争がないのかも? どの月額プランを選ぶかでデータ通信の課金レートが変わってくるので、 最適な月額プランを選ぶのが難しい。

これに対し Project Fi は、 どんなに使っても 1MB あたり 1セントの単一課金レートなので分かりやすいし、 課金レートだけ比較すると、 カナダのどのキャリアよりも安い (例えば比較的安価な Virgin でも 2¢/MB)。

ただし Project Fi はプリペイド SIM ではなくポストペイ SIM なので、 米国に住所がないと契約が難しいかも? 私の場合はホテルの住所で契約することができた。 私は米国の銀行が発行したクレジットカードで払っているが、 日本のクレジットカードで契約できるかどうかは不明。

また、ポストペイなので当然だが、 使わなくても毎月基本料金 ($20 と税金等が $3 ほど) がかかる。 日本でもローミングサービスが利用できるが、 日本だと 1¢/MB という課金レートは (格安 SIM と比べると) 安いとは言えず、 常用には向かない。

日本に居るときは基本料金が無駄になるなぁ、 $23 というのは無駄に払う額としては安くないなぁ、 と思いつつ (今年 1月に) 契約したのだが、 実は Project Fi の契約は一時停止できる。

一時停止中は、 データ通信ができないのはもちろん、 通話の着信もできなくなるが、 いまどき通話なんてできなくても困らない。 Google Voice の番号は他にも持ってるし。 :-)

というわけで今年 3月1日に一時停止した。 停止/再開は Project Fi の Web ページからいつでも可能。 3ヶ月後に自動で再開してしまうが、 その直後に停止すれば基本料金はほとんどかからない。 3ヶ月後の 5月30日に自動再開したが、 6月4日に停止し、 バンクーバーを訪れる直前 6月27日に再開し、 日本に帰国してから停止した。 自動再開した直後に停止することさえ忘れなければ (自動再開を知らせるメールが届くので見落とさなければ大丈夫)、 使っていない期間の基本料金をほぼゼロにできる。

8/7 追記:

8/2 の〆日を過ぎたので請求額が確定した。 Project Fi を今年 2/2 に契約して以来、 丸 6ヶ月が経過した。 現在までの請求額 (前述した国際通話料金を除く) をまとめてみる:

〆日米国内米国外データ料金基本料金税など合計
2/2$10.00$20.00$3.02$33.02
3/2968 MB2 MB-$0.30
$0.00
-$0.56
$0.00
-$0.25-$1.11
7/22618 MB$26.18
$10.00
$3.21
$20.00
$5.35$64.74
8/27363 MB$63.63
$0.00
-$4.41
$0.00
$2.29$61.51
合計968 MB9983 MB$109.51$38.24$10.41$158.16

この 6ヶ月間のうち、 1ヶ月をハワイで過ごし (日本の寒い冬を避けるため)、 1ヶ月をカナダで過ごした (日本の蒸し暑い梅雨を避けるため)。 残り 4ヶ月は日本に居たので Project Fi を一時停止した。 したがって基本料金は約 2ヶ月分 ($20 * 2) しか払っていない。

データ料金 (データ通信料金) と基本料金の欄が上段/下段に分かれているが、 上段が後払い分 (〆日までの 1ヶ月間の利用料, ポストペイ) の額で、 下段が前払い分 (〆日から 1ヶ月間の利用料, プリペイド) の額。 つまり前月の前払い分と〆日の後払い分の合計が、 〆日までの 1ヶ月間の利用に対応する料金になる。 2/2 は契約開始日なので (契約日までの) 後払い分は無い。 マイナスの金額は払い戻し。

払い戻しによって合計額がマイナスになる場合 (3/2 請求分) は、 次に請求額がプラスになった月 (7/2 請求分) から減額される ($64.74 から $1.11 を減額した $63.63 が実際の請求額になる)。

「税など」 の内訳 (ハワイ州の場合) は次の通り:

2/23/27/28/2合計
State 911 Tax
E911税 $0.66
$0.66-$0.66$1.32-$0.66$0.66
State Sales Tax
州税 (売上税) 4%
$0.81$0.41
-$0.02
$2.04$3.09
-$0.18
$6.15
County Sales Tax
郡税 (売上税) 0.5%
$0.10$0.05$0.26$0.39
-$0.02
$0.78
PUC Fee
公益事業委員会費 0.5%
$0.10$0.00$0.12-$0.02$0.20
Telecomm Relay Systems Surcharge
電話リレーサービス負担金
$0.02$0.00$0.03-$0.01$0.04
Federal Universal Service Fund
ユニバーサル サービス負担金
$1.20-$0.03$1.42$0.01
-$0.27
$2.33
Federal Regulatory Assessment Fee
規制アセスメント費
$0.13$0.00$0.16$0.00
-$0.04
$0.25

E911税は、 911緊急通報 (日本でいう 119番通報) の際、 携帯電話の位置を追跡するシステムを整備するための税金。 ユニバーサルサービス制度は日本でも導入されている (日本より桁違いに高額) が、 電話リレーサービス負担金 (Telecomm Relay Systems Surcharge) は、 聴覚障碍者のために音声を文字 (TTY, テレタイプ) あるいは手話に変換する福祉サービスを支えるための負担金。 マイナスの金額は払い戻し。

基本料金は前払いなので 2/2 (Project Fi 契約開始日) に $20 を払っているが、 3/1 に Project Fi の利用を停止したので 3/2 の〆日までの 1日分 (正確に言うと 19時間分) 56セントの払い戻しがある。 同様に 7/2 に $20 払っているが、 7/26 に停止したので 8/2 までの 6日間と 21時間分 $4.41 の払い戻しがある。 基本料金の払い戻しにともない、 税なども払い戻されている。 〆日において利用を停止中の場合 (3/2 と 8/2) は、 〆日以降の基本料金の前払いは請求されない。

逆に月の途中 (〆日から次の〆日までの間) で Project Fi の利用を再開した場合は、 その時点から〆日までの基本料金の日割額 (日単位じゃなくて時間単位なので時間割額?) を〆日に後払いする。 6/27 に再開したので、 6/27 〜 7/2 までの 4日間と 20時間分 $3.21 を払った。

データ料金は 0.001GB あたり 1セント。 したがって正確には 1.024MB あたり 1セントだが、 ここでは簡単のため 0.001GB を 1MB と表記している。

データ通信は 1GB 分が前払いなので 2/2 に $10 払っているが、 2/2 〜 2/23 の期間において米国で 968MB (正確には 0.968GB なので実際は 991MB, 以下同様) 使い、 そのあと日本に帰国してから 2MB 使った。 計 970MB で、 1GB の残り 30MB を使わなかったので、 未利用分 30 セントの払い戻しがある。

また、 7/2 に $10 を前払いしているが、 7/2 〜 8/24 の期間において 7363MB も使ってしまったので、 1GB を 6363MB も超過してしまった。 したがって 8/2 に超過分 $63.63 を後払いしている。 基本料金と同様、 〆日において利用を停止中の場合は、 〆日以降のデータ通信料金の前払いは請求されない。

月の途中で再開した場合は、 その時点から〆日までのデータ通信量を〆日に後払いする。 6/27 に再開したら、 〆日の 7/2 までわずか 5日足らずなのに 2617MB も使ってしまった。 これは、 カナダ到着後にホテルの Wi-Fi のあまりの遅さに業を煮やし、 Project Fi の SIM を入れたスマホのテザリングを有効にして PC を接続したら (6/29)、 勝手に別のスマホが Wi-Fi 接続してきて大量のデータ通信 (アプリの更新と同期) を行なったため。

Wi-Fi 接続先が (テザリングな) スマホだとキケン。 Wi-Fi 接続時に自動で更新/同期する設定は一般的だと思うので、 テザリングを行なうときは他のスマホがどんな状態にあるか注意が必要。 さらに、 Windows PC が Wi-Fi 接続して Windows の更新 (のためのダウンロード) を始めたりしたら目もあてられない。

7/2 は前払い額も加わるので請求額が高額になった。 カナダ到着早々 $63.63 もの請求額を見て驚いてしまい (たった 4日で 60ドル?! @_@)、 以後テザリングの使用には慎重になった。 前払い額を除けば $30 程度なので、実は大したことはないのだが、 当時は Project Fi の料金体系を充分理解できていなかった。

Project Fi Billing

Project Fi はデータ通信の前払い額を設定できるが、 余れば払い戻しされるし、 超過すれば後払いになるので、 はたして前払い額を設定する必要性があるのか疑問。 前払い額をいくらにするかは、 契約時に悩むポイントでもあるので (悩んでるうちに契約をためらってしまうかも?)、 前払い額を設定させず全て後払いのほうがいいのではないか? 私は、 前払い額を設定させておいて後払いもできるのはヘンだ、 何か理解できてない仕組み (落とし穴) があるのではないか? と疑心暗鬼になってしまっていた。

もちろん前払いした $10 は米国内でのデータ通信だけでなく、 米国外でのデータ通信にもそのまま同じレート 1¢/MB で使える。 だから極端な話、 米国で全くデータ通信しない場合 (米国の他のキャリアの SIM を使う場合) でも Project Fi を利用する意味がある。

Project Fi は米国内では T-Mobile の MVNO なので、 (at&t などと比べると) 使い勝手がいいとは言えない。 なので、 あまり米国内向けサービスであることを強調しないほうが (ローミング専用と言うのは言い過ぎであるにしても) いいのではないか?

Project Fi というと、 米国内向け、 基本料金が必要、 Pixel/Nexus 向け、 というイメージが強すぎるのではないか? 少なくとも私にとっては、 米国内ではさほど魅力的なサービスとは言えず、 米国外 (特にデータ通信料金が高い国) でこそ真価を発揮するサービスだと思う。
しかも、 一時停止できるので Project Fi が必要ない国 (データ通信料金が充分安い国) に滞在している期間は、 基本料金を払う必要がない。
また、 Project Fi の SIM は Pixel/Nexus 以外のスマホでも全く問題なく使えるわけで、 Pixel/Nexus を使っていない人にとっても有用なサービスだと思う。 実際カナダ滞在中は、 ずっと ZenFone 3 と Galaxy Note 5 で利用していた。
なお、 Project Fi の SIM を普通のスマホ (or タブレット等) で使うには、 APN として 「h2g2」 を設定する

私も米国内では主に at&t のプリペイド SIM を使ったので、 Project Fi は 968MB しか使わなかった。 いっぽう、 カナダでは 9981MB (約 10GB !) も使った (日本で 2MB)。 6ヶ月で計 10951MB (約 11GB) 使ったのでデータ通信料金は合計 $109.51 だった。 1MB が 1セントなので、データ通信料金の計算が容易。

これに約 2ヶ月分の基本料金 $38.24 と税金など $10.41 が加わって、 6ヶ月の支払合計が $158.16 (国際通話料金を除く) になった。 約 $160 というと高いようにも見えるが、 これは 2人で使った合計額なので 1人あたりだと $80 となり、 半年間 (利用したのは 2ヶ月間) の合計と考えるとリーズナブル。

ZenFone3 で撮影した写真ファイル内の、位置情報が壊れている Exif データを修復するコードを書いてみた

$
0
0

昨年発表された ASUS のハイエンド スマホ ZenFone 3 Deluxe ZS570KL の購入時の OS は Android 6.0 Marshmallow だが、 この OS (UL-Z016-WW-4.12.40.1698) の標準のカメラ アプリには、 撮影した写真の位置情報が他のアプリで参照できないというバグがあった。

つまり、 カメラの設定で 「場所サービス」 を 「オン」 にすると、 撮影した場所 (GPS などの位置情報) を写真ファイルに付加する (Exif データ) が、 この位置情報に問題があって、 他のアプリで写真を見ても位置情報が表示されない。 例えば撮影した写真を Google フォトへアップロードしても、 撮影場所が表示されない

browsed with Gallery Application

この写真を Google フォトからダウンロードしてみれば、 Exif データ内に位置情報が保持されていることが分かるし、 アプリによってはこの位置情報を参照できるものもある。

例えば OS 標準の 「ギャラリー」 アプリで見ると、 位置情報が正しく保持されているように見える。 例えば、 御成町の交差点で撮影したこの写真を 「ギャラリー」 で見ると (右図 ⇒ ZenFone のスクリーンショット)、 場所が 「鎌倉市, 御成町」 と正しく表示される。

位置情報を正しく表示できるアプリがある以上、 バグではなく仕様 (Exif のバージョンの違い?) の可能性もある。 このとき私は、 この写真の位置情報を参照できないアプリは、 アプリ側にも問題があるのだろうと思ってしまった。 おそらく開発元でも同様の理由で、 このバグが見逃されてしまったのだろう。

そしてもし仕様なら、 Google フォトで撮影場所が表示されない問題も、 そのうち (Google 側で) 解決されるのだろうと思って放置してしまった。 ところが先日バンクーバーに行ったことで、 これは仕様などではなく、 紛れもないバグだったことを確信した。

browsed with Gallery Application

つまり、 日本や ZenFone 製造元である台湾など、 「東経」 な地域では正しい場所が表示されるが、 バンクーバーなど 「西経」 な地域だと全くかけ離れた場所が表示されてしまうということ。

例えば、 バンクーバーで撮った写真を 「ギャラリー」 で見ると (右図 ⇒ ZenFone のスクリーンショット)、 場所が 「内モンゴル, フルンボイル, オロチョン自治旗, 中華人民共和国」 などと広大な草原の真っ只中な場所が表示されてしまう (データが無いためか地図が真っ白, そもそも 「自治旗」 なんて行政単位は初めて見た)。 「49.288132,123.130722」 という位置座標を、 「49.288132,-123.130722」 に直してみる (経度をマイナスにしてみる) と、 正しい座標になることから、 「西経」 で撮影しても 「東経」 で記録してしまうバグであることが分かった。

もちろん、 このバグは現行の Android 7.0 Nougat では修正されているが、 Android 7.0 へ更新する前に撮影した写真ファイルの位置情報は (当然のことながら) 壊れたままなので不便このうえない。

そこで Android 6.0 の時に撮影した写真ファイルの位置情報を、 修正してみることにした。 まずは Exif データに保持された位置情報の何が問題なのか調べてみる。

Exif データの構造」 という検索語でググってみると、 いくつか解説ページが見つかる。 ありがたいことに 「Exif データにアクセスするコード」 まで見つかった (libexif project のような本格的なライブラリだと読むのが大変)。 長い解説文よりも動く簡潔なコードの方が話が早い。

コードを読みつつ、 まずは動かしてみて、 ZenFone 3 で撮影した写真ファイルの何がマズイのか調べてみる。

senri:~ $ gcc -o exif sample_main.c exif.c
senri:~ $ ./exif P_20170701_100640_vHDR_Auto.jpg
[P_20170701_100640_vHDR_Auto.jpg] createIfdTableArray: result=5

{0TH IFD}
 - DateTime: [2017:07:01 10:06:40]
 - GPSInfoIFDPointer: 22232 
 - Model: [ASUS_Z016D]

	…… 中略 ……

{GPS IFD}
 - GPSLongitude: 123/1 7/1 505923/10000 
 - GPSLatitudeRef: [N]
 - GPSProcessingMethod: [ASCII]
 - GPSAltitudeRef: 200/100 
 - GPSLatitude: 49/1 17/1 172751/10000 
 - GPSLongitudeRef: [se]

	…… 後略 ……

Longitude は経度、 Latitude は緯度。 GPSLongitude や GPSLatitudeRef のことをタグと呼び、 タグの値を保持するのが Exif データの役割。

Exif では緯度・経度の値を、 3つの分数で表現する。 例えば 「GPSLongitude: 123/1 7/1 505923/10000」 というのは、 タグ GPSLongitude の値が 「123/1 度 7/1 分 505923/10000 秒」、 すなわち撮影場所が 「経度 123 度 7 分 50.5923 秒」 であることを意味する。

そして GPSLatitudeRef: [N] は、 タグ GPSLatitudeRef の値が 「N」 つまり北緯 (North) であることを表わす。 では、 GPSLongitudeRef: [se] はどういう意味なんだろうか? 普通に考えればここは [se] ではなく 西経 (West) の [W] ではなかろうか?

[se] に何か特別な意味でもあるのかと思ってググってみたが、 何も見つからず。 単なるバグ? どうやったらこんなバグを作り込めるのだろう?

ちなみに GPSLongitudeRef: [se] は Exif データ内では次のような 12バイトのデータ (「タグフィールド」 と呼ぶ) になる:

00 03    00 02    00 00 00 03    73 65 00 00

最初の 2バイト 「00 03」 が、タグ GPSLongitudeRef を表わし (タグそれぞれに ID が割当てられている。GPSLongitudeRef は 3, GPSLongitude は 4 といった具合)、

次の 2バイト 「00 02」 が、タグの値のタイプが ASCII 文字列であることを表わす。

次の 4バイト 「00 00 00 03」 が、タグの値の長さ、 ここでは ASCII 文字列の長さが 3文字 (終端文字 00 を含む) であることを表わし、

最後の 4バイト 「73 65 00 00」 が、 タグの値である ASCII 文字列 「se」と終端文字 00。 最後の 00 は単なる穴埋め。

タグの値が 4バイトに収まらないときは、 実際のデータ格納場所を指し示すポインタ (ファイル内のオフセット) が、 この最後の 4バイトに格納される。

御成町の交差点で撮影した写真の位置情報も同様に調べてみた。

{GPS IFD}
 - GPSLongitude: 139/1 32/1 576664/10000 
 - GPSLatitudeRef: [N]
 - GPSProcessingMethod: [ASCII]
 - GPSAltitudeRef: 200/100 
 - GPSLatitude: 35/1 19/1 27825/10000 
 - GPSLongitudeRef: [se]

やっぱりタグ GPSLongitudeRef の値が [se] になっている。 どうりで 「西経」 でも 「東経」 と同じになってしまうわけだ。

ちなみにタグ GPSAltitudeRef は海抜 (altitude) が海水面より上 (0) か下 (1) かを表わす。 0 か 1 以外の値はダメ。 この Exif データでは RATIONAL タイプ (32ビット符号無し整数 2個で分数を表わす) になっている (正しくは BYTE タイプ)。 タグ GPSAltitude と混同しているのか? 高度 2m というのも意味不明だけど。 まあ、 タグ GPSAltitudeRef を参照するアプリが手元にないので、 とりあえず放置。

というわけで原因が分かった。 位置情報の [se] を [W] (西経の場合。東経ならば [E]) へ書き換えればうまくいきそう。 つまり、前述の 12バイトのタグフィールドの場合なら、 次のように書き換えればよい。

00 03    00 02    00 00 00 03    73 65 00 00
00 03    00 02    00 00 00 02    57 00 00 00

つまり、 データの長さ (終端文字を含む ASCII 文字列の長さ) を 3 から 2 へ書き換え、 ASCII 文字列を 「se」 から 「W」 (西経の場合) へ書き換える。 要は、 書き換えるべきタグフィールドが写真ファイル内のどこにあるかさえ分かれば、 それを読み込んで書き換えてファイルへ書き戻すだけ。 というわけで、 サクっと書いてみた。 わずか 150行足らず。

書き換えるべきタグフィールドがどこにあるか調べるのは、 「Exif データにアクセスするコード」 を流用させて頂いたので、 あっと言う間に書けた (このブログを書く方が遥かに時間がかかっている)。

ありがとうございます > DSAS 開発者の部屋のみなさま, とくに tanabe さま

東経か西経かという情報は写真ファイルには無いので、 西経の場合は -W オプションを指定する (デフォルトは東経)。 例えばバンクーバーで撮った写真を一つのディレクトリにまとめておいて、

senri:~/Vancouver $ fix_GPSInfo -W *

といった感じで一括修復できる。 修復の必要がないファイル (他のカメラで撮影した写真ファイルや、 位置情報を含んでいない写真ファイル、 および動画など写真以外のファイル等) は影響を受けない。 手元の写真ファイルはこれで全て修復できるのだけど、 Google フォトにアップロード済みの写真はどうしよう? いちど全部消してアップロードし直せばいいのだろうけど、 あまりに枚数が多すぎるなぁ...

配当金領収証の上限は 100万円、複数枚でも簡易書留で送付できるらしい 〜 祝 KLab(株) 初配当 (特別配当 9円) 〜

$
0
0

株式の配当金って、 (特に手続きしなければ) 郵便為替 (正確に言うと、 株式会社ゆうちょ銀行が発行する配当金領収証) を利用することがほとんどだと思いますが、 金額が大きくなっても郵便為替を使うのかなぁ? と日頃からギモンに思っていました。

「領収証」って名称ですが、為替証書の一種です。 株主届出印を押印して郵便局へ持っていくと換金できます (身分証明書の提示が必要)。

そんなある日、 KLab株式会社から 「定時株主総会招集ご通知」 と題する簡易書留郵便が届きました。

「ご通知」 を簡易書留で?
不審に思いつつ受け取って開封すると...
3枚の配当金領収証が出てきました。(@_@)

「配当金領収証」の上限って 100万円なんですね、初めて知りました。 配当金の額面は 255万円余り (税引き前は 320万円余り) なのに、 簡易書留 (5万円が上限) で送れるとは、 ちょっとビックリ。

送金できる金額は、 配当金領収証1通につき100万円までです。 100万円を超える送金は、 配当金領収証を発行することで行えますが、 追加する配当金領収証の枚数に応じた料金がかかります。

もっと金額が大きくなると、どうなるんだろう?

その企業の役職員の場合や、法人株主宛の場合は、 さすがに郵便為替ということはないと思うのですが、 個人株主宛の場合は、 どんなに高額でも (事前に手続きしない限り) 郵便為替を使うんですかねぇ?

- o -

18年前の 2000年7月末、 (株)ケイ・ラボラトリー (当時の社名) への出資金として 300万円を振り込みました。

シティバンク銀行 (現 SMBC信託銀行PRESTIA) 虎ノ門支店で、 虎の子の 300万円をおろしたものの、 目と鼻の先にある東京三菱銀行 (現 三菱東京UFJ銀行) 虎ノ門支店まで現金を持っていくのが怖くて (引ったくりに遭ったらどうしよう?)、 840円もの振込手数料を払って振り込んだのが思い出されます。

1年後にどうなってるか分からない会社 (実際、かなり危うい時期が何度かありました) にポンと 300万円もの大金を投じた当時の私 (← 株式会社日立製作所から転職したばかりの 33歳でお金持ってません) もビックリですが、 それから 18年たって、 出資した額以上の配当金 (税引き前 320万円余り) を出せる会社に成長したのもビックリです。

今回の配当金の総額は 3億3500万円だそうですが、 創業当時の資本金が 3億円でした。

正確に言うと創業時の私の出資分に対する今回の配当金は 162万円 (税引き後は 129万円ほど) です。 創業時 1株 5万円だったので、 私が出資した 300万円は 60株になったのですが、 2004年10月30日に 1株が 2株に分割、 2011年4月21日に 1株が 300株に分割、 2012年1月31日に 1株が 5株に分割しました。 つまり、 創業時の 1株が 3000株になったので、 私の 60株は現在の 18万株になったわけです。 今回の特別配当は 1株あたり 9円ですから、 9円 * 18万株=162万円ですね。

もっとも、 創業当時とは全く異なる事業の会社になってしまいましたが、 全く異なる会社に脱皮できたからこそ今の成長があるのでしょう。

初の配当おめでとうございます。 そして、 ありがとうございました。 株主の一人として、また創業者の一人として御礼申し上げます。

NETGEAR のスマートスイッチ GS108E/GS116E のポートごとの通信量を取得して Cacti でグラフ化してみた 〜 通信量を見ることができない Wi-Fi中継機への対処法 〜

$
0
0

たまたま買った Wi-Fiアクセスポイント (以下 Wi-Fi AP と略記) が、 SNMP 非対応なのはもちろん、 Web管理画面でさえ通信量を見ることができなかったので、 この Wi-Fi AP を接続しているスイッチ (ネットワークハブ) NETGEAR GS108E (と GS116E) のポートの通信量を取得して Cacti (最近の流行りは Grafana ?) で可視化してみた。 Wi-Fi そのものの通信量ではないが、 Wi-Fi AP とスイッチとの間の通信量を測ることができる。

買ったのは NETGEAR WiFi中継機/802.11ac wave2 Nighthawk X4 EX7300-100JPS (以下 EX7300 と略記)。 たまたま amazon で税込5,980円 (送料無料) で売っていて、 中継機 (ワイヤレスエクステンダー) としてだけでなくアクセスポイントとしても使えるとのことなので (NETGEAR製ということもあって) 衝動買い。 もともとルータの機能は必要ないし、 コンセント直挿しで場所を取らないので、 リビング用としてちょうどいいかなと思った次第。

さいきん Wi-Fi中継機が流行りらしく、 各社からお手頃価格で多数の製品が発売されている。 コンセント直挿しで場所を取らないので家庭向きと言えるが、 残念なことにアクセスポイントとして使える製品は (NETGEAR や TP-Link などのごく一部の例外を除くと) ほとんど無い。
中継機も (広義の) アクセスポイントと言えるが、 親機 (Wi-Fiルータ) と無線でつながるのが中継機で、 有線でつながるのが (狭義の) アクセスポイント。 あたりまえだが、 有線でつなぐことができる (家庭内でケーブルを配線できる) なら、 有線のほうがいいに決まってる。
ちなみに私が初めて Wi-Fi中継機を使ったのは 10年前なので、 いまさら流行って少々戸惑いを覚える。

EX7300 は、 たった 6000円なのに性能的には悪くない。 ルータ機能が不要ならお買得と思う。 ノートPC Lavie HZ を Wi-Fi で EX7300 につないで iperf3 を走らせて測定してみたら 530Mbps くらい出ている。

C:\Users\sengoku>c:\bin\iperf3.exe -c esaka -P 5
Connecting to host esaka, port 5201
[  4] local 192.168.18.147 port 50119 connected to 192.168.18.20 port 5201
[  6] local 192.168.18.147 port 50120 connected to 192.168.18.20 port 5201
[  8] local 192.168.18.147 port 50121 connected to 192.168.18.20 port 5201
[ 10] local 192.168.18.147 port 50122 connected to 192.168.18.20 port 5201
[ 12] local 192.168.18.147 port 50123 connected to 192.168.18.20 port 5201
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-1.00   sec  13.4 MBytes   112 Mbits/sec
[  6]   0.00-1.00   sec  13.2 MBytes   111 Mbits/sec
	...

[SUM]   9.00-10.01  sec  63.9 MBytes   533 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bandwidth
[  4]   0.00-10.01  sec   130 MBytes   109 Mbits/sec                  sender
[  4]   0.00-10.01  sec   130 MBytes   109 Mbits/sec                  receiver
[  6]   0.00-10.01  sec   128 MBytes   108 Mbits/sec                  sender
[  6]   0.00-10.01  sec   128 MBytes   108 Mbits/sec                  receiver
[  8]   0.00-10.01  sec   128 MBytes   107 Mbits/sec                  sender
[  8]   0.00-10.01  sec   128 MBytes   107 Mbits/sec                  receiver
[ 10]   0.00-10.01  sec   126 MBytes   106 Mbits/sec                  sender
[ 10]   0.00-10.01  sec   126 MBytes   106 Mbits/sec                  receiver
[ 12]   0.00-10.01  sec   124 MBytes   104 Mbits/sec                  sender
[ 12]   0.00-10.01  sec   124 MBytes   104 Mbits/sec                  receiver
[SUM]   0.00-10.01  sec   636 MBytes   534 Mbits/sec                  sender
[SUM]   0.00-10.01  sec   636 MBytes   534 Mbits/sec                  receiver

iperf Done.

たまたま手元にあった TP-Link WiFi 無線LAN ルーター Archer C3150 11ac にも Wi-Fi でつないで同様に iperf3 を走らせて比較してみたら、 ほとんど差がない。 同等と言ってもいいレベル。 値段は 3倍くらい違うのだから、 EX7300 をブッチぎって欲しかった。

しかもこの Archer C3150 は OpenVPN を使ってると腐るバグがあるように感じる (いちど電源を切らないと復旧しない)。 ヘンテコな付加機能よりも安定性こそ一番重要なのではないか? ついでに言うと勝手に外部と通信しているのが気持ち悪い。 値段が高い Wi-Fiルータにはもうちょっと頑張ってもらいたいところ。

もちろん、 このノートPC を無線LAN ではなく有線LAN につなげば 940Mbps くらいは出るので、 ノートPC の性能がボトルネックになっているわけではない。 というか Wi-Fi のパフォーマンスは周囲の電波状況に強く影響され (もちろん空いてるチャンネルを使っている)、 有線側の状況はほとんど関係がない。 電波暗室とかで測定すれば、 EX7300 と Archer C3150 の差が開くのかもしれないが、 実用上は無意味。

性能でも安定性でも申し分のない EX7300 ではあるが、 残念なことに Web管理画面を隅から隅まで調べてみても通信量を見る方法がない。 どんな安物 Wi-Fiルータ (or Wi-Fi AP) でも、 通信量 (パケット数とか) を表示させる方法はあるものだと思っていたが、 中継機は Wi-Fiルータとは製品コンセプトが違うのかも?

通信量を見ることができない通信機器があるとは思っていなかったので、 意表を突かれた感じ。 今後 Wi-Fi AP を買う際は注意したい。 やっぱり (ルータ機能が必要なくても) 素直に Wi-Fiルータを買うべきなのかも。 ほんとうは (ルータ機能がない) アクセスポイント専用機が欲しいのだけど...

幸い、 この EX7300 を接続しているスイッチ NETGEAR GS108E が「スマート」で、 スイッチの各ポートごとの通信量 (≒ Wi-Fi AP の通信量) を GS108E/GS116E 付属の Windows アプリ ProSafe Plus で見ることができる。

Web アプリではなく Windows アプリなので、 表示された通信量の数値を Web スクレイピングなどのお手軽な方法で取り込むことはできないが、 google で検索したら ProSafe Plus と同等のことができる Python プログラムが見つかった。 6年前に開発が終了しているが、 このスイッチを買ったのも 6年前なので無問題。

さっそくダウンロードして (ざっと内容を確認したのち) 走らせてみる:

senri $ git clone https://github.com/Z3po/Netgearizer.git ↵
Cloning into 'Netgearizer'...
remote: Counting objects: 88, done.        
remote: Total 88 (delta 0), reused 0 (delta 0), pack-reused 88        
Unpacking objects: 100% (88/88), done.
Checking connectivity... done.
senri $ Netgearizer/netgearizer.py ↵
please select one of the following switches with "selectSwitch $NR"
--> 0: 192.168.18.239
Information: 
switch-name: living
switch-dhcp: disabled
switch-type: GS108Ev2
switch-ip: 192.168.18.239
switch-mac: 4c:60:de:69:01:23
switch-firmware: 1.00.06
switch-netmask: 255.255.255.0
switch-gateway: 192.168.18.1
(Cmd) 

スイッチは 3つ (GS116E と GS108E 2台) あるのに、 なぜか 1つ (リビングに設置した GS108E) しか表示されない。 selectSwitch コマンドでスイッチを選択し (選択肢は 1つしか無いが)、 getPortStatistics コマンドでポートごとの通信量を表示できるようだ。

実行してみる:

(Cmd) selectSwitch 0 ↵
(Cmd) getPortStatistics ↵
switch-port-statistics: 
 -> Port 01: 
   >> send: 2664610542955
   >> receive: 5993649462240
   >> crcerrors: 0
 -> Port 02: 
   >> send: 6062498001
   >> receive: 328715447216
   >> crcerrors: 0
 -> Port 03: 
   >> send: 183424114643
   >> receive: 8723285000144
   >> crcerrors: 0
 -> Port 04: 
   >> send: 39935317815
   >> receive: 26550837425456
   >> crcerrors: 0
 -> Port 05: 
   >> send: 137960667631
   >> receive: 790398362288
   >> crcerrors: 0
 -> Port 06: 
   >> send: 3948410207
   >> receive: 1156872131840
   >> crcerrors: 0
 -> Port 07: 
   >> send: 2789623
   >> receive: 20875141728
   >> crcerrors: 0
 -> Port 08: 
   >> send: 1989939088
   >> receive: 2852348890112
   >> crcerrors: 0
(Cmd) 

一見、 正しく動いているように見えるが、 「send:」に表示されている数値は (ProSafe Plus で見たときの) 受信バイト数で、 「receive:」に表示されている数値は送信バイト数を 16倍した値になっている。

受信と送信が入れ替わっているのはご愛敬だが、 16倍になっているのは... netgearizer.py (Python で書かれたスクリプト) を読んでみると、 単純なバグだった。 次のパッチをあてると正常に動作した:

--- netgearizer.py.org	2018-04-07 22:04:42.292912972 +0900
+++ netgearizer.py	2018-04-09 11:34:50.124297214 +0900
@@ -284,5 +284,5 @@
             for port in hexvalue:
-                sendstats = self.__convertFromHex(port[2:18],'cipher')
-                receivestats = self.__convertFromHex(port[19:35],'cipher')
-                crcerrors = self.__convertFromHex(port[36:53],'cipher')
+                receivestats = self.__convertFromHex(port[2:18],'cipher')
+                sendstats = self.__convertFromHex(port[18:34],'cipher')
+                crcerrors = self.__convertFromHex(port[82:98],'cipher')
                 result.append(( 'Port ' + str(port[:2]), (('send', sendstats), ('receive', receivestats), ('crcerrors', crcerrors))))
@@ -345,15 +345,16 @@
                 for key in self.switches.keys():
                     self.switchList.append(key)
                     print '--> ' + str(counter) + ': ' + key
                     print 'Information: '
                     self.selectedSwitch = key
                     self.__printResult(True)
                     self.selectedSwitch = None
-                    return True
+                    counter += 1
+                return True
             else:
                 print 'please select one of the switches you get with getSwitches first.'
                 return False
 
         if 'ERROR' in resultdict:
             found = None
             for key in  self.switchattributes.keys():

送信バイト数が 16倍になるのは、 部分文字列の切り出しで「port[18:34]」 (変数「port」の文字列の 18文字目から 34文字目まで切り出す) とすべきところを、 「port[19:35]」 としてしまっていたため。 変数「port」には、 スイッチから受信したデータを 16進数に変換した文字列が格納されている。 1文字ずれて切り出したため、 16倍 (16進数で 1桁ぶん) になっていた。

16進数で 16文字つまり 64bit だが、 ProSafe Plus で表示される GS116E の送受信バイト数が小さすぎると思っていたら、 GS116E はバイト数が 32bit (4GiB) でラップアラウンドしているようだ。 つまり上位 32bit は常にゼロ。ポートによっては毎日ラップアラウンドしている。
GS108Ev2 は 40bit (1TiB) を超えるバイト数になっているポートもあるので、 どこでラップアラウンドするか確認できるまで、 まだだいぶかかりそう。 仮に 1ヶ月で 1TiB くらいの通信量だとすると、 もし 48bit (256TiB) が上限なら 20年もかかってしまう。
もし 64bit 全部使ってカウントしているなら、 上限は 16EiB (エクスビバイト) ということになり、 140万年くらいかかってしまう。 ちょっとのコスト増を惜しんでラップアラウンドするより、 64bit きっちり使って半永久にラップアラウンドしないほうがいい。

実は、 私は今まで Python でプログラミングしたことが無い。 文法もろくに知らなかったのだけど、 部分文字列を切り出す際の Python の位置の指定方法は独特だと思う。 このプログラムの作者も Python は慣れていなくて、 うっかりこのようなバグを作り込んでしまった (前の行で 18文字目まで切り出したので、 その次は 19文字目からとしたくなる気持ちは分かる) のではないか?

さらに興味深いのが、 3つあるはずのスイッチが 1つしか見つからなかったバグ。 なんと、 「return True」文のインデント (字下げ) が間違っていただけ。 インデントでブロックを表わす Python ならではのバグ?

インデントが一段階深かったので、 「return True」文が forループの中に入ってしまい、 必ずループが 1回だけで終了してしまっていた。 だからスイッチを 1つ見つけてすぐループを終了していた。 「return True」文の前の空白を削除してループの外へ出したら、 正しく 3つのスイッチを見つけるようになった。 空白の有無で挙動が変わってしまうのは、 やっぱり気持ち悪い。

この netgearizer.py は対話的にコマンドを入力するソフトウェアだが、 通信量を自動計測する際には使いにくいので、 対話せず通信量を取得するだけのプログラムに書き換えた。 不要な機能をばっさり削除したので、 プログラムの行数が半分以下の 317行になった。 この改変版は、

http://www.gcd.org/sengoku/docs/netgearizer.py

からダウンロードできる。 わずかな改変とはいえ、 なにぶん私が初めて書く Python プログラムなので、 変なところがあったらご指摘頂けると幸い。

この改変版を実行する (引数としてインタフェースを指定する) と、 以下のように LAN 内 (同一セグメント内) の全ての NETGEAR スマートスイッチ (GS116E, GS108E, GS105E など) の、 各ポートの受信バイト数、送信バイト数、エラー数を表示する。

senri:~ $ netgearizer.py eth0 ↵
IP:   192.168.18.239
MAC:  4c:60:de:69:01:23
Type: GS108Ev2
switch-port-statistics: 
 Port01 rx:2673873720246 tx:375671471563 err:0
 Port02 rx:6410023533 tx:21992185424 err:0
 Port03 rx:183424114643 tx:545205312509 err:0
 Port04 rx:40052812544 tx:1664344574414 err:1
 Port05 rx:138616484625 tx:52251007903 err:0
 Port06 rx:3954090302 tx:72431873899 err:0
 Port07 rx:2802623 tx:1308888305 err:0
 Port08 rx:1989939088 tx:178271805632 err:0
IP:   192.168.18.237
MAC:  4c:60:de:69:04:56
Type: GS108Ev2
switch-port-statistics: 
 Port01 rx:786331851 tx:1283347 err:0
 Port02 rx:0 tx:0 err:0
 Port03 rx:0 tx:0 err:0
 Port04 rx:0 tx:0 err:0
 Port05 rx:0 tx:0 err:0
 Port06 rx:474556 tx:875905 err:0
 Port07 rx:0 tx:0 err:0
 Port08 rx:0 tx:0 err:0
IP:   192.168.18.236
MAC:  84:1b:5e:90:81:72
Type: GS116E
switch-port-statistics: 
 Port01 rx:2245190893 tx:3192491053 err:0
 Port02 rx:2079058908 tx:4030809209 err:0
 Port03 rx:0 tx:0 err:0
 Port04 rx:2815311290 tx:1592352771 err:0
 Port05 rx:3838533539 tx:2573311206 err:0
 Port06 rx:1441584273 tx:1249733039 err:0
 Port07 rx:747795050 tx:97733030 err:0
 Port08 rx:889171012 tx:559797414 err:0
 Port09 rx:3757714660 tx:749158734 err:0
 Port0a rx:3210386057 tx:2724670950 err:0
 Port0b rx:441710237 tx:2183781562 err:0
 Port0c rx:0 tx:0 err:0
 Port0d rx:4837745 tx:252496639 err:0
 Port0e rx:2058650283 tx:3610878767 err:0
 Port0f rx:1435416601 tx:403439849 err:0
 Port10 rx:0 tx:0 err:0
senri:~ $ 

私の自宅の LAN ではタグVLAN を使っているため、 以上のコマンドを実行している Linux マシン「senri」の eth0 インタフェースには IP アドレスを割当てていないが、 問題なく動作している。 しかも、 この Linux マシンは GS116E の Port04 に接続していて、 2台の GS108E には GS116E 経由で間接的に繋がっているのだけど、 GS108E の通信量も取得できている。 Windows アプリの ProSafe Plus だと、 スイッチに直接繋げないと管理できないことがある。

改変版 netgearizer.py で取得した送受信バイト数を Cacti (最近の流行りは Prometheus とか Fluentd ?) に読み込ませて EX7300 の通信量をグラフ化すると、 こんな感じ:

In がスイッチのポートの送信バイト数、 つまりスイッチから EX7300 が受信したバイト数、 すなわち Wi-Fi 子機 (スマホなど) がダウンロードした合計。

Out がスイッチのポートの受信バイト数、 つまりスイッチへ EX7300 が送信したバイト数、 すなわち Wi-Fi 子機がアップロードした合計。

実は ProSafe Plus のもう一つの Python 実装である ProSafeLinux も試してみたのだが、 IP アドレスを割当てていないインタフェースを受付けないので、 先に netgearizer.py の書き換えを試みた次第。

ProSafeLinux のほうが (active ではないにせよ) 今でも開発が続いているし、 前述したようなバグも無いようなので、 試してみるべきだとは思うが、 プログラムの行数が netgearizer.py の倍以上 (私が作った改変版の 5倍以上) もある。 通信量の取得といった単純な目的であれば、 短い方がいい。

Windows 10 上の VMware Workstation Player のゲストOS でタグVLAN を使ってみる

$
0
0

Windows 上の仮想化ソフトウェアの定番 VMware Workstation Player で、 タグVLAN (tagged VLAN, IEEE 802.1Q) を使うことはできないと今まで思い込んでいたが、 レジストリエディタ (regedt32.exe) でレコードを一つ追加するだけで使えるようになった。 ググっても Windows 上のゲストOS でタグVLAN を使う話は見かけないのでメモしておく。

私の自宅内LAN はタグVLAN を利用している。 つまり物理的な配線は一本のイーサケーブルで、 複数のネットワーク (宅内LAN, DMZ, 対外セグメントなど) を同居させている。 タグVLAN はオフィス等で使われることが多いが、 美観上の理由からケーブルを何本も這わすわけにはいかない家庭内LAN においてこそ、 タグVLAN は有用と思う。

Linux OS などタグVLAN 対応の OS が走るマシンへは、 タグが付いたままのパケットを流し、 Windows OS などタグVLAN に非対応な OS が走るマシンへは、 スマートスイッチでタグを取り除いた (通常の) パケットを流している。

ところが、 マシンによっては Linux と Windows の両方の OS を走らせることがある (いわゆるデュアルブート)。 OS を切り替えるたびにスマートスイッチの設定を変更するのは面倒なので、 そういうマシンには Linux と Windows の両方がアクセスするネットワーク (つまり自宅内LAN) のパケットはタグ無しで流しつつ、 Linux のみがアクセスするネットワーク (DMZ や対外セグメント) のパケットはタグ付で流すことになる。

Windows が立ち上がってるときもタグ付パケットが届くが無視される。 Linux が立ち上がっているときはタグ付、タグ無し両方のパケットを受け取る。

さいきんは PC のメモリも大きくなり、 普段は Windows を使うマシンでも、 VMware Workstation Player (以下 VMware と略記) などの仮想化ソフトウェアを使って Linux をゲストOS として走らせておくことが増えてきた。 デュアルブートよりも同時に走らせておくほうが便利に決まってる。 となってくると、 ゲストOS でもタグVLAN を使いたくなるのが人情というもの。

ゲストOS でタグVLAN が扱えると、 本来 DMZ へ置くべきようなサーバ (WWW サーバとか) をゲストOS 上で動かすことが可能になる。 また、 ゲストOS でも自宅内LAN とインターネットとの両方のネットワークへアクセスできるわけで、 ルータの役割を担わせてもよい。 いずれにしても仮想マシンの応用範囲が一気に広がる。

しかし VMware を使って起動した仮想マシン上では、 (仮想)ネットワークインタフェースにタグ付パケットが上がってこないので、 ゲストOS でタグ付パケットを拾うことができない。 VMware がタグVLAN に対応していないのだろうと諦めていた。 まあタダで利用させてもらってるソフトウェアだし、 対応してなくても仕方がないなぁと。

ところが、 実は VMware 自体はタグVLAN に対応しているらしい。 Windows 10 がタグ付パケットを落としているから、 VMware までパケットが届かないということらしい。 細かく言えば Windows 10 が悪いというよりは、 ネットワークインターフェース (以下 NIC と略記) のドライバがタグ付パケットを落としているらしい。

私が使ってる Windows 10 では NIC が 「Broadcom NetLink (TM) Gigabit Ethernet」 と表示されるので、 「windows10 broadcom capture vlan」 あたりのキーワードで検索していたら、 Windows 上でタグ付パケットをキャプチャする方法について書かれたページを見つけた。

なお google では同じキーワードを使って検索しても、 このページを見つけ出すことはできなかった。 ニッチなものを見つけたい場合に、 google 検索が全く役に立たなくなったのは、 google がモバイル検索にシフトし始めた頃だったろうか? 探しているページが全く見つけられないので、 最近は google 検索を使わなくなってしまった。 google お得意の AI で賢く検索するより、 バカ正直にキーワード検索してくれたほうが (少なくとも私にとっては) 役に立つ。 Stay Foolish !

この見つけたページには、 Windows 上のパケットキャプチャソフトウェア Wireshark でタグ付パケットを見る方法が書かれている:

Display VLAN tags in Wireshark on laptops with Broadcom B57 chipsets から引用:

In order to make these tags visible to Wireshark, specialized drivers or specific NICs that support VLAN tags are usually needed. In the case of the Broadcom B57 chipset in some Dell Latitude laptops, the NIC itself supports VLAN tags (display only, it cannot actively tag outgoing traffic) with a small registry modification and a specific driver.

(意訳) Wireshark でタグ付パケットを見るには、 NIC ドライバがタグVLAN に対応している必要がある。 Broadcom B57 チップセット自体はタグVLAN に対応しているので、 ドライバのレジストリをいじればタグ付パケットを見ることができるようになる。 ただし見るだけでタグ付パケットを送信することはできない。

このページには 「送信できない」 と書いてあるが、 後述するようにゲストOS からタグ付パケットを送信できている。 Wireshark と VMware とでは違うのかも?

レジストリをいじるには、 まず NIC ドライバのインスタンスを見つけなければならない。 このページでは 「TxCoalescingTicks」 を検索せよと説いている。 Broadcom B57 チップセットのドライバのインスタンスであれば、 この名前のレコードを必ず持っているからだろう。

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{クラスID}\番号 を見つけたら、 このインスタンスに 「PreserveVlanInfoInRxPacket」 という名前で文字列値 「1」 を追加すればよい。

ちなみに、 「tx coalesce ticks」 を WWW で検索すると Broadcom 関連のページばかりが出てくる。 Broadcom 製 NIC 特有の機能なのかも? Linux の BCM5700 用ドライバ にも tx_coalesce_ticks というパラメータがあるようだ。

というわけで、 Broadcom 製 NIC でタグ付パケットを落とさないようにする方法は以下のようになる:

VLAN capture setup から引用:

  1. Run the Registry Editor (regedt32).
  2. Search for "TxCoalescingTicks" and ensure this is the only instance that you have.
  3. Right-click on the instance number (eg. 0008) and add a new string value.
  4. Enter "PreserveVlanInfoInRxPacket" and give it the value "1".

意訳:

  1. レジストリエディタ regedt32 を起動する
  2. 「TxCoalescingTicks」 を検索する。 見つけたインスタンスが (Backup 用のインスタンスを除いて) 唯一であることを確認する。
  3. 見つけたインスタンス番号 (例えば 0008) を右クリックし、 「新規 文字列値」 を追加する。
  4. 「PreserveVlanInfoInRxPacket」 を入力し、 その値を 「1」 とする。

Intel 製 NIC の場合も、 ドライバのレジストリをいじることで、 タグ付パケットを落とさないようにできるらしい。

レジストリをいじった後は、 再起動すれば NIC ドライバがタグ付パケットを捨てずに受け取るようになっている。 ドライバが捨てなければ、 タグ付パケットは Windows OS から VMware に渡され、 ゲストOS まで届く。 逆にゲストOS から発せられたタグ付パケットは、 Windows OS から NIC へ送られ、 ネットワークへ送出される。 つまりゲストOS がタグVLAN を扱えるようになった。

UEFI ブートでキーボードが無いと GRUB がハングするバグを修正してみた

$
0
0

遅ればせながら手元の PC を MBR ブートから UEFI ブートに切り替えた。 ハードディスクの最初の 512バイト MBR (Master Boot Record) を読み込んで起動するのが MBR ブート。 Windows だと 2TB 超のディスクは MBR ブートできない (Linux ならブート可能)。 2TB では手狭になってきたのが切り替えを決意した理由だが、 ついでに Linux 専用マシンも PC のファームウェアが対応しているものは UEFI ブートに切り替えた。

UEFI (Unified Extensible Firmware Interface) だと、 普通の FAT32 ボリュームにブートローダのファイルを置いておくだけなので、 わざわざ MBR を書き換えたりするより簡単だし分かりやすい。 PC が起動しなくなった、などのトラブルはよくあるが、 トラブル発生時は気が急くし時間的余裕がないことも多いので、 トラブル時の作業は簡単であればあるほど、 分かりやすければ分かりやすいほど好ましい。

UEFI ブートに切り替えて 1ヶ月ほど経ったある日、 CPU を換装するために落としていた PC の電源を入れたら GRUB のメニュー画面でフリーズしたので私も凍り付いた。 CPU の性能をむやみに上げると、マザーボードとのミスマッチが起りがち。 せっかく買った新しい CPU が問題を起こしたのかと思った。 電源ボタンを長押しして強制的に電源を落とす。

BIOS 設定を確認するためにキーボードをつないで再度電源を入れてみる。 設定に何の問題もない。 続いて GRUB を立ち上げる。問題無く立ち上がる。 Linux を起動する。問題無い。 さっきのフリーズは何だったのだろう?

この時は因果関係に気付かなかったが、 キーボードをつないでいないと GRUB 2.04 (および最新版の 2.05 も) がメニュー画面のカウントダウンでハングする (最初の秒を表示したまま止まる)。 PC を再起動するときは、たいていキーボードをつないでいるので正常に起動し、 この症状は見たことがなかった。

実はこの時も、 なぜキーボードをつないでいなかったかというと明確な理由はない。 CPU 換装後の動作確認なのだからキーボードをつないでおくべきだと思うのだけど、 たまたまつなぐのを忘れていただけかも。 Web で 「headless GRUB hang bug UEFI without keyboard」 などを検索しても似た症例がほとんど見つからないのは、 キーボードをつながずに起動させる人がほとんどいないから?

とはいえ、 何台もあるサーバそれぞれにキーボードをつないでいては邪魔である。 意図して再起動するときはキーボードをつなぐなり KVM スイッチ (Keyboards, Video monitors, Mice Switch) を切り替えてキーボードが効く状態にするのが通常だが、 トラブルや Watchdog タイマーで勝手に再起動したとき、 あるいは遠隔から再起動させたときに、 立ち上がらずにフリーズしてしまったら非常に困る。 不測の再起動に備えて、 急遽テンキーをつないでおくことにした。

GRUB がキー入力を検知する状態であれば、 画面表示の有無自体は関係なくハングする。 逆に言うと、 GRUB がキー入力を検知しなければ、 例えば grub.cfg でキー入力を読む命令が無ければ (menuentry 等が無ければ) ハングしない。 例えば grub.cfg が次のように特定の Linux を起動するだけなら、 正常に起動できる。

insmod all_video
insmod part_gpt
insmod search_label
search --no-floppy --label --set=root /
linux /boot/linuz-4.19.69-x86_64 root=LABEL=/ resume=LABEL=swap ro
initrd /boot/initz-4.19.69-x86_64
boot

もちろんこれでは GRUB を使う意味がないが、 少なくとも問題が GRUB のキー入力関連にあることが分かった。 同じ PC、同じ GRUB でも、 UEFI ブートではなく MBR ブートなら、 キーボードをつながなくてもハングしない (MBR ブート専用の USB メモリを作って確認した)。 また、UEFI ブートであっても UEFI ファームウェアによってはハングしない場合もあると思われる (未確認)。 が、少なくとも私の PRIMERGY MX130 S2 では確実に再現する。

というわけで、 問題の所在はおおむね絞り込めたので、 GRUB のソースコードを読み始めた。 並行して facebook に、 ことの顛末を書込んでみた

すると野中尚道さんから grub-core/tem/efi/console.c が怪しそう、 とのヒントを頂いた。 ありがたい! なにぶん EFI のソースコードを読むのは初めてなので、 この時点ではまだ流れを追いきれていなかった。 efi/console.c の grub_console_getkey まわりを重点的に読んでみる。

grub-core/tem/efi/console.cのget_keyで key_exとkey_conを呼び分けている処理が失敗しているような気がします。 key_exの方はオプション機能なので実装有無を確認してるのですが

なるほど確かに grub_console_getkey_ex は、 grub_efi_open_protocol の返り値が NULL で無いとき (key_ex の実装が有るとき) に限り呼び出されているが、 grub_console_getkey_con には対応するものがない。 key_con は必ず実装されているから確認不要? でも、キーボードをつないでいない場合はどうなる? key_ex の有無を確認するコード:

text_input_ex_guid = GRUB_EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
	...
text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
				    &text_input_ex_guid,
				    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);

をマネして、 key_con の有無を確認するコードをでっち上げてみる。 単に 「_EX_」 の部分を取り除いただけだが、 キーボードをつないでいるか否かを正しく検出しているようだ。

text_input_guid = GRUB_EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
				    &text_input_guid,
				    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);

あとはキーボードの有無に応じて grub_efi_console_input_init の返り値を変えるだけ。 grub_efi_console_input_init が 1 (非ゼロ?) を返せば、 GRUB は grub_console_getkey を呼ばなくなるようだ。

以上をまとめると、 次のようなパッチになった:

diff --git a/grub-core/term/efi/console.c b/grub-core/term/efi/console.c
index 4840cc5..f2be32f 100644
--- a/grub-core/term/efi/console.c
+++ b/grub-core/term/efi/console.c
@@ -207,7 +207,12 @@ grub_efi_console_input_init (struct grub_term_input *term)
 				      GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   term->data = (void *)text_input;
 
-  return 0;
+  if (text_input) return 0;
+  grub_efi_guid_t text_input_guid = GRUB_EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
+  text_input = grub_efi_open_protocol(grub_efi_system_table->console_in_handler,
+				      &text_input_guid,
+				      GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+  return text_input == NULL;
 }
 
 static int

わずか 6行だが、 これで GRUB がハングしなくなった。

IoT な人感センサをトリガーとした照明の点灯/消灯を IFTTT を使って行っていたけど反応が遅いので IFTTT をショートカットしてみた

$
0
0

さいきん流行りの IoT 機器。 多くの家電がネットからコントロールできるようになった。 IFTTT を使うと、 そういった機器を手軽に連携できるので便利。 IoT 機器同士だけでなく、 (私が管理する) WWW サーバを IFTTT がアクセスするように設定したり、 あるいは逆に私のサーバが IFTTT をアクセスする (トリガーを送る) こともできるので、 思いのままに IoT 機器を制御できる。

例えば、 人感センサで照明を点灯/消灯させる場合、 防犯用ライトなら人の動きを感知したときだけ点灯し、 人の動きが無くなれば速やかに消灯する、 といった単純なルールで充分だが、 部屋の照明となると人の動きが無くなったからと言ってすぐに消されては困る。 部屋を退出したことを確認してから消灯して欲しいし、 時間帯、あるいは在宅/不在時に応じて (さらにはその時々の天気に応じて)、 適切な点灯/消灯制御を行いたい。

つまり、 部屋の外にも人感センサを設置し、 部屋の中で人の動きが検知できなくなった後、 部屋の外で人の動きを検知すれば、 部屋を出ていったと判断し部屋の照明を消灯する。 さらに、 特定のスマホが LAN (家庭内 Wi-Fi) に接続していないときは不在とみなし、 部屋の外で人の動きを検知しただけでは照明を点灯しないけど、 そのスマホが LAN に接続した直後は帰宅したとみなし、 夜間であれば人の動きを検知したら速やかに点灯するなど。 私自身のサーバ (以下、「自サーバ」と略記) を IFTTT と連携させれば、 いくらでも複雑な制御ルールを設定できる。

IFTTT (IF This Then That) は、 その名の通り特定の条件 (This) が満たされたとき特定の動作 (That) を行わせることができる。 IoT 機器の多くは IFTTT との連携をサポートしているので、 例えば「This」として、 「人感センサが人の動きを検知」を設定し、 「That」として、 「照明をオン」を設定すれば、 単純な防犯用ライトが実現できる。

この連携に自サーバを絡めるには、 「This」および「That」を自サーバと結び付ければ良い。 それには IFTTT の 「webhooks」を用いる。

「This」は、 IFTTT の特定の URL をアクセスするだけ。 例えばこんな感じ:

senri:~ $ curl https://maker.ifttt.com/trigger/light_on/with/key/dD-v7GCx46LnWaF1AD9nwSUeA_N1ALvDHKS57cP1_Md
Congratulations! You've fired the light_on event

「light_on」の部分は任意に定めることができる。 「with/key/」以降の部分はユーザごとに IFTTT が割当てる認証用キー。 このキーが他人に漏れると勝手に操作されてしまうので適切な管理が必要。 そして、 「https://maker.ifttt.com/trigger/light_on/with/key/... へのアクセスがあった」(This) ならば、 「照明をオン」(That) を行う、 というルールを設定することで、 自サーバから照明を点灯させることが可能になる。

いっぽう 「That」 は、 IFTTT に自サーバをアクセスさせる。 例えば 「https://www.gcd.org/ifttt へ POST メソッドでアクセス」させる。 POST の body として json データを送るよう設定することができて、

{"magic": "0svikYKbcsxDbkty", "type": "Motion detected", "CreatedAt": "{{CreatedAt}}", "DeviceName": "{{DeviceName}}"}

などと設定する。 「"magic": "0svikYKbcsxDbkty"」は認証用。 https://www.gcd.org/ifttt は誰でもアクセスできるので、 "magic" の文字列が一致しないリクエストは無視する。 「"type"」は 「This」の機器の種類 (この例では人感センサ) を伝えるために設定。 「{{CreatedAt}}」と 「{{DeviceName}}」は、 「This」の機器が IFTTT へ送信したデータ。 例えば人感センサが検知 (This) すると IFTTT が次のようなアクセスを www.gcd.org へ行ってくれる (That)。

POST /ifttt HTTP/1.1
Content-type: application/json
host: www.gcd.org
content-length: 134
x-newrelic-id: ZW1uPtmAO9tRDSFGGvmp
x-newrelic-transaction: VGhpcyBpcyBmYWtlIHgtbmV3cmVsaWMtdHJhbnNhY3Rpb24uCg==
Connection: close

{"magic": "0svikYKbcsxDbkty",
 "type": "Motion detected",
 "CreatedAt": "November 19, 2019 at 09:15AM",
 "DeviceName": "廊下センサ"
}

この IFTTT からのアクセスを受信することで、 人感センサが人の動きを検知したことを自サーバが知ることができる。 そして自サーバにおいて様々な条件を加味した後、 前述した 「https://maker.ifttt.com/trigger/light_on/with/key/...」 へアクセスすれば照明を点灯することができる。

以上で、 IoT の連携に自サーバを絡ませることができるようになった。 ところがこの方法は、いかんせん遅い。 人感センサ ⇒ IFTTT ⇒ 自サーバ ⇒ IFTTT ⇒ 照明 などと IFTTT とのやりとりを 2度も行うため、 人の動きを検知してから照明が点灯するまで 6秒ほどかかってしまう。 部屋に入るまで 6秒も待てないので、 暗いままの部屋に入る羽目になる。 なお、 点灯するのは素早さが肝要だが、 消灯するのは数秒程度の遅れなら全く問題にならない。

まず後半 「自サーバ ⇒ IFTTT ⇒ 照明」 の IFTTT をショートカットする。 私は自室の照明を点灯/消灯するために IoT 対応赤外線リモコン Nature Remo-1W2 を使っている。 赤外線でコントロールできる家電ならなんでも IFTTT で連携できるので便利。

Remo は IFTTT との連携ができるだけでなく、 同じ LAN 内にある自サーバから直接アクセスできる (Local API)。 例えば次のように http://Remo-XXXXXX.local/messages (「XXXXXX」は Remo の個体ID) へ POST メソッドでアクセスすると Remo から赤外線が発射され、 部屋の照明を点灯させることができる。 ただし Remo-XXXXXX.local の名前解決には Bonjour (DNS Service Discovery, RFC 6763) が必要。私はメンドーなんで LAN のネームサーバに固定アドレスで登録済。

senri:~ $ curl -X POST "http://Remo-XXXXXX.local/messages" -H "Accept: application/json" -H "X-Requested-With: curl" -H "Expect: " -d '{"format":"us","freq":36,"data":[3353,1720,402,1288,400,...中略...,460,402,449,383]}'

ただし Remo はアイドル状態が続くとスリープに入る。 スリープ状態だと http アクセスに反応しないので、 アクセスに失敗した時はリトライしなければならない。 私はメンドーなんで 1分に一度くらいの頻度で Remo に GET メソッドでアクセスを行って、 スリープ状態へ入らないようにしている (ping を打ち続けてもスリープになることを防げない)。

POST の body 中の "data" は「中略」しているが、実際には 539個の数字が並ぶ赤外線の波形データ。 昔懐かしい PC用学習リモコン (13年前! 当時は IoT なんて言葉は無かった) と仕組み的には同じで、 家電付属のリモコンが発射する赤外線を受信して記憶し、 同じ波形を発射することができる。 まず、 家電付属のリモコンを Remo に向けて「点灯」ボタンを押すと、 「点灯」ボタンに対応する赤外線の波形が Remo に記憶される。 次に、 http://Remo-XXXXXX.local/messages へ GET メソッドでアクセスすると、 記憶された赤外線波形データが返される:

senri:~ $ curl -i "http://Remo-XXXXXX.local/messages" -H "Accept: application/json" -H "X-Requested-With: curl"
HTTP/1.0 200 OK
Server: Remo/1.0.77-g808448c
Content-Type: application/json

{"format":"us","freq":37,"data":[3353,1720,402,1288,400,...中略...,460,402,449,383]}

この波形データをそのまま POST メソッドで送信すれば、 Remo から (家電付属のリモコンと) 同じ赤外線が発射される仕組み。 これで後半 (自サーバ ⇒ IFTTT ⇒ 照明) の IFTTT をショートカットして、 人感センサ ⇒ IFTTT ⇒ 自サーバ ⇒ 照明 という経路になって 2秒ほど短縮できた。 でもまだ 4秒もかかっている。 残る前半の IFTTT もショートカットできないか?

あいにく私が使っている人感センサ DCH-S150 には Remo みたいな Local API は無さそう。 しかも既に製造中止でファームウェアのバージョンアップも期待できそうにない。 仕方ないので人感センサが IFTTT (正確に言えば D-Link のサーバ) とどんな通信を行っているか tcpdump で調べてみた。

人感センサが動きを感知すると、 まず LAN のネームサーバ 192.168.18.1 に対して api.dch.dlink.com. の A レコードの問合わせを行い、 その 0.1 〜 0.4 秒後に 54.200.253.23 (api.dch.dlink.com.) へ https アクセスしている。

11:02:00.282700 IP (tos 0x0, ttl 64, id 28255, offset 0, flags [DF], proto UDP (17), length 63)
    192.168.18.134.52276 > 192.168.18.1.53: [udp sum ok] 84+ A? api.dch.dlink.com. (35)

11:02:00.401084 IP (tos 0x0, ttl 64, id 9685, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.18.134.33116 > 54.200.253.23.443: Flags [S], cksum 0x7197 (correct), seq 2008959912, win 5840, options [mss 1460,sackOK,TS val 60124797 ecr 0,nop,wscale 1], length 0

動きを感知したら直ちに照明を点灯させたいので、 0.1 〜 0.4 秒とはいえできればネームサーバ問合わせの段階で点灯させたい。 ところが、 動きを感知していないときも 30分に一度くらいの頻度で api.dch.dlink.com. の A レコードの問合わせを行っているようだ。 人の動きがないのに照明が勝手に点灯するのはよろしくないので、 api.dch.dlink.com. への https アクセスがあったら、 Remo に「点灯」の赤外線を発射させるようにしてみた。 なお、人感センサが https アクセスしたことを自サーバで知るには、 iptables の NFLOG を使うと簡単に実現できる。

これで 人感センサ ⇒ 自サーバ ⇒ 照明 という経路になって、 さらに 2秒ほど短縮できた。 まだ 2秒ほどのタイムラグがあるが、 人感センサが動きを検知するのに 1秒ほどかかっていて、 Remo の Local API をアクセスしてから赤外線を発射して実際に照明が点灯するまでにも 1秒ほどかかっているようなので、 これ以上の短縮は無理?。

IFTTT は便利だが、 便利さを追求していくと IFTTT を使わない方が実用になる、というオチとなった。 IoT 機器は、 Nature Remo のような Local API を公開しているものを使うようにしたい。


IFTTT に登録できないのでお蔵入りになってた Eco Plugs RC-028W & CT-065W が、UDP パケットを送るだけでコントロールできた!

$
0
0

IoT 機器の多くが、 専用のスマホアプリだけでなく Googleアシスタントや Amazonアレクサからコントロールできる。 しかし、 いちいち音声でコントロールするのはメンドクサイ (なぜ音声以外の方法でもコントロールできるようにしないのか?)。 出かけるときに毎回 「行ってきま〜す」 などと Googleアシスタントに呼び掛けるのは、 いかがなものかと思う。 外出を勝手に検知して家電を適切にコントロール (例えば電気ポットの電源を切る) してくれるほうがずっといい。

IoT 機器を IFTTT に登録すると、 自前のプログラムからコントロールできるようになる。 IoT 機器は Googleアシスタントでコントロールするより、 自前のプログラムでコントロールするに限る。 例えば自宅の Wi-Fi LAN にスマホが繋がっているかプログラムで監視し、 繋がってるスマホがいなくなったら留守になったと判断して、 自動的に電気ポットの電源を切れば、 電気ポットのコンセントを抜いたかどうか出先で心配せずに済む。 あるいはコンセントを抜くのを忘れて寝てしまい、 翌朝電気ポットのお湯が熱いままなのを見て愕然とするより (先月の電気使用量が 600kWh だったので驚いた)、 部屋が暗いときは自動的に電源が切れている方がいい (これはプログラムを書かなくても IFTTT だけで実現できる)。

というわけで持ってる IoT 機器を片っ端から IFTTT に登録したのだけど、 IFTTT に登録できない IoT 機器も残念ながら若干ある。 いまどき IFTTT に登録できない IoT 機器に何の意味があるのだろう? (今なら絶対に買わない) と思うのだけど、 IFTTT の便利さを知る前に買ってしまったのだから後悔先に立たず。 IFTTT の便利さを知ってからは、 お蔵入りになっていた。

Eco Plugs もそんな「使えない」IoT 機器の一つ。 当時としては安価だった (今ならもっと安い) ので Walmart で購入してしまった。 Googleアシスタントや Amazonアレクサには登録できるのに、 肝心の IFTTT に登録できない。

といって通信プロトコルを解析しようにも、 いまどきの IoT 機器はクラウド (ベンダが運用するサーバ) と https で通信するので調べる取っ掛かりがない。 最後の手段、 分解するしかないのか?

ところがググっていると、 Eco Plugs は平文で通信しているという投稿を見つけた。 Eco Plugs はクラウドに登録しなくても、 同一 LAN セグメントならスマホアプリでコントロールできるが、 同一 LAN 内では平文の UDP パケットを飛ばしているらしい。

ありがたいことに Eco Plugs をコントロールする JavaScript プログラムが GitHub に公開されていた。 JavaScript は文法もロクに知らない (^^; のだけど、 見よう見まねで perl で書き直してみる:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
use IO::Socket::INET;
our ($opt_v);
(getopts('v') && @ARGV == 3) || &help;
my ($ip, $id, $on) = @ARGV;

my $state = 0x0100;
$state = 0x0101 if $on eq "on";
my $buf = pack("H260", 0);
# Byte 0:3 - Command 0x16000500 = Write, 0x17000500 = Read
substr($buf, 0, 4) = pack("N", 0x16000500);
# Byte 4:7 - Command sequence num - looks random
substr($buf, 4, 4) = pack("N", rand(0xffffffff));
# Byte 8:9 - Not sure what this field is - 0x0200 = Write, 0x0000 = Read
substr($buf, 8, 2) = pack("n", 0x0200);
# Byte 16:31 - ECO Plugs ID ASCII Encoded - <ECO-xxxxxxxx>
substr($buf, 16, 16) = $id;
# Byte 116:119 - The current epoch time in Little Endian
substr($buf, 116, 4) = pack("L", time());
# Byte 124:127 - Not sure what this field is - this value works, but i've seen others 0xCDB8422A
substr($buf, 124, 4) = pack("N", 0xCDB8422A);
# Byte 128:129 - Power state (only for writes)
substr($buf, 128, 2) = pack("n", $state);

my $sock = IO::Socket::INET->new(PeerAddr => $ip, PeerPort => 80,
    Proto => 'udp', Timeout => 1) || die;
my $flags;
print unpack("H*", $buf) . "\n" if $opt_v;
print $sock $buf;
$sock->recv($buf, 1024, $flags);
print unpack("H*", $buf) . "\n" if $opt_v;

# Byte 10:14 - ASCII encoded FW Version - Set in readback only?
my $fwver = substr($buf, 10, 5);
# Byte 48:79 - ECO Plugs name as set in app
my $name = substr($buf, 48, 32);
$name =~ s/\0*$//;
printf("%s (ver %s)\n", $name, $fwver);

sub help {
    print <<EOF;
Usage: ecoplugs <opt> <IP> <ID> <on/off>
opt:   -v           ; verbose
EOF
    exit 1;
} 

長さ 130 バイトの UDP パケット (変数 $buf) を作って Eco Plugs へ送信している (print $sock $buf;) だけなので、 いたってシンプル。 ユーザ認証もないので LAN 内なら誰でもコントロールできる。

Eco Plugs の IP アドレス (第1引数) と、 Eco Plugs の ID 「ECO-XXXXXXXX」(第2引数, XXXXXXXX は MACアドレスの第3〜6オクテット, ただし 16進数の A〜F は大文字限定)、 および「on」あるいは「off」の 3引数を付けて、 この perl プログラムを実行すると、 該当 Eco Plugs をオン/オフし、 Eco Plugs の名前 (スマホアプリで設定できる。以下の例では 「potplug」) と、 ファームウェアのバージョン (以下の例では 「1.6.3」) を表示する。

senri:~ $ ecoplugs -v 192.168.15.123 ECO-01234567 on
16000500940c163b020000000000000045434f2d303132333435363700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a625df5d00000000cdb8422a0101
160005000000163b0000312e362e330045434f2d30313233343536370000000000000000000000000000000000000000706f74706c7567000000000000000000000000000000000000000000000000003031323334353637000000000000000000000000000000000000000000000000a8e23b7ea625df5d00000000cdb8422a
potplug (ver 1.6.3) 

「-v」オプションを付けた場合、 最初に表示される行が Eco Plugs へ送った長さ 130バイトの UDP パケット (260桁の 16進数)、 2行目が Eco Plugs から返ってきた長さ 128バイトの UDP パケット (256桁の 16進数)。 第1引数で指定した IP アドレスが Eco Plugs のものでなかった場合、 あるいは第2引数で指定した ID が間違っている場合など、 応答が返ってこない時は待ち続ける。 ID の 16進数において A〜F が小文字だと応答しないので注意。

Eco PlugsRC-028W (屋外用) および CT-065W (屋内用) で動作を確認したが、 おそらく同シリーズの他の機器でも使えるだろう。 Woods の WiON (スマホアプリが Eco Plugs そっくり) でも使えるらしい。

学習リモコンの赤外線波形データを変換してみた 〜 Nature Remo で取得した波形データを PC-OP-RS1 用に変換

$
0
0

人感センサ (人の動きを感知するセンサ) 付であることに魅力を感じて IoT な学習リモコン Nature Remo を買ったら、 人の動きをトリガーにした IFTTT との連携ができないばかりか、 センサの感度もあまりよくなかった。 仕方ないので人感センサを新たに買ってみた

人感センサとしての感度は、 だんぜんこの +Style ORIGINAL スマートセンサー(人感) PS-SMT-W01 のほうがいい。 IFTTT と連携できないので他の IoT 機器との連携を考えている場合は注意が必要だが、 私は IFTTT をショートカットするので無問題。 思わず買い増ししてしまった。

Nature Remo から人感センサを引き算したら、 残りは「学習リモコン」ということになるが、 そこで思い出したのが 13年前に買ったパソコン用学習リモコン PC-OP-RS1。 いま流行りの IoT では無いが、 サーバが置いてある部屋で使うのであれば IoT である必要はなく、 むしろ PC-OP-RS1 のように (ネットを介さず) USB で直接コントロールできるほうが、 赤外線を発射するまでの遅延が少なくてすむ。

学習リモコン PC-OP-RS1 と人感センサ PS-SMT-W01 を組合わせれば Nature Remo は不要? と思ったので押し入れの中から PC-OP-RS1 を発掘した。 ところが、 家電のリモコンの赤外線を学習させようと、 PC-OP-RS1 の受光部に向けて赤外線を発射しても、 PC-OP-RS1 側では何も受け取っていない様子。 10年くらい使ってなかったから赤外線受光素子が劣化してしまったのか?

赤外線の受光はできないものの、 発光は可能みたい。 13年前に書いた Perl スクリプトを使って Nature Remo に向けて赤外線を発射してみると、 ちゃんと Nature Remo で波形データを生成できた。 ということは、 波形データさえ用意できれば今でも使えそう。

ただし、 13年前に PC-OP-RS1 を買ったときは、 波形データのフォーマットを知らなくても使えたので、 単に PC-OP-RS1 が出力した波形データを 16進数の羅列として perl スクリプトに取り込んだだけ。 当時書いた「日記」からスクリプト (の冒頭部分) を引用:

#!/usr/bin/perl
use strict;
use warnings;
use Device::SerialPort;
use Getopt::Std;

my %Ir;
$Ir{'vPower'} = [
    pack("H*", "ffffffffffffffffffffff0700000000007ef0831ff8c00f7e00003f00800ffc00003f00801f00c00700f00300f8c10f7c00003f00801f00e00700f0831f00c00f7ee0033ff8c10ffc00003ff00100fc00007e00001f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    ];
$Ir{'aPower'} = [
    pack("H*", "ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff"),
    ];

 ...以下略 ...

スクリプト中 「vPower」 はビデオテープレコーダ (VTR) の電源をオン/オフする赤外線のデータ。 「aPower」 は (おそらく) エアコンのオン/オフ。 後に続く 16進数の羅列が赤外線の波形データ。 どちらの家電もすでに無く (VTR なんてすでに死語?)、 そのリモコンも捨ててしまった。 なのでこのスクリプトが (今でも) ちゃんと機能するかは確認のすべがない。

とりあえず vPower の 16進数を 2進数で表示してみる:

senri:~ $ perl -e 'print unpack("b*", pack("H*", "ffffffffffffffffffffff0700000000007ef0831ff8c00f7e00003f00800ffc00003f00801f00c00700f00300f8c10f7c00003f00801f00e00700f0831f00c00f7ee0033ff8c10ffc00003ff00100fc00007e00001f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))."\n"'
111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000000000000000000000000000000000000000001111110000011111100000111111000000111110000001111110000011111100000000000000000111111000000000000000001111100000011111100000000000000001111110000000000000000011111100000000000000000111110000000000000000011111100000000000000000111111000001111110000001111100000000000000000111111000000000000000001111110000000000000000111111000000000000000001111110000011111100000000000000000111111000001111110000001111100000011111100000111111000001111110000001111110000000000000000111111000000111110000000000000000011111100000000000000000111111000000000000000001111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

おお、 (なんとなく ^^;) 赤外線の波形データっぽい。 2進数で表示すると、 最初の 91個の「1」と続く 46個の「0」の連続を除けば、 「1」は 5〜6個続くのに対し、 「0」は 5〜6個か、15〜17個続く。 これは赤外線リモコンの通信フォーマットにおける 1T (5〜6個) および 3T (15〜17個) の区間に対応するのだろう。 ということは T (変調単位) は 2進数 5.5個くらいに対応する、 つまり 2進数 1個は 100μ秒くらいなのだろう。

ちなみに unpack("B*", ...) (descending bit order) も試してみたのだが、 「"b*"」(ascending bit order) のほうが赤外線の波形データっぽかったので、 「"b*"」と仮定して作業を進めた。 とりあえず 2進数に変換してみる、みたいな試行錯誤を 1行スクリプトでサクっと書けてしまえるのは perl ならでは。 さいきんあまり人気がない perl だが、 この手の試行錯誤をするときには今でも一番ではなかろうか?

いっぽう Nature Remo の赤外線波形データはこんな感じ:

senri:~ $ curl -i -X GET "http://Remo-XXXXXX.local/messages" -H "Accept: application/json" -H "X-Requested-With: curl" -H "Expect: "
HTTP/1.0 200 OK
Server: Remo/1.0.77-g808448c
Content-Type: application/json

{"format":"us","freq":37,"data":[3357,1717,385,1305,377,468,386,460,386,460,387,459,383,463,385,462,382,463,383,463,383,462,384,465,381,465,379,1306,386,463,382,460,385,465,381,467,381,459,385,462,384,462,377,1312,386,1309,384,459,387,462,382,460,386,460,385,461,385,462,385,1306,385,461,384,1307,384,461,386,1306,402,1291,399,1291,382,1307,388,456,404,1290,401,443,403,1287,407,440,402,445,401,443,386,462,400,445,384,463,382,464,381,464,384,1306,401,1292,383,1302,409,1285,402,1289,383,1309,383,1306,387,1304,379,1312,407,1285,402,443,405,441,386,460,403,443,385,460,404,442,385,464,403,440,404,1289,405,1285,404,1282,388,1304,409,1282,390,1301,406,441,386,1306,407,436,410,441,401,440,406,443,385,1304,407,1284,404,1287,405,441,386,1305,408,1287,404,1285,385,1304,407,441,383,462,389,40199,3376,1697,384,1306,407,441,402,442,407,440,384,465,400,443,405,442,383,460,385,464,384,461,401,445,402,441,404,1291,400,446,384,461,400,446,381,463,383,466,399,444,402,444,383,1304,407,1287,401,444,384,460,404,442,407,439,406,442,403,441,404,1287,386,460,403,1289,400,445,403,1287,407,1282,409,1285,385,1307,400,444,405,1285,404,442,404,1289,402,442,405,441,401,444,386,459,404,442,405,441,403,443,404,442,407,1286,405,1288,398,1286,406,1285,409,1283,404,1287,407,1286,401,1294,406,1284,401,1288,401,446,381,462,402,445,401,444,402,444,384,462,383,466,398,442,407,1286,401,1288,403,1288,404,1288,403,1288,401,1290,407,439,404,1284,406,441,404,441,409,436,408,439,407,1285,406,1285,403,1289,400,446,402,1288,403,1287,405,1284,404,1286,409,437,406,444,404,39760,3379,1697,402,1288,404,442,405,441,401,444,407,439,406,441,401,446,402,441,407,439,402,445,406,441,401,443,401,1290,405,437,404,446,405,441,402,441,402,442,406,441,403,441,404,1290,402,1290,400,446,404,439,407,439,406,439,404,442,404,442,406,1288,401,442,402,1290,385,462,401,1289,401,1290,404,1288,399,1288,404,441,386,1306,402,446,402,1287,403,446,401,442,402,444,401,445,404,441,402,444,402,444,402,446,400,1290,398,1290,402,1287,385,1309,404,1287,400,1289,403,1292,401,1283,390,1302,387,1304,404,445,384,460,408,436,405,442,385,462,402,443,404,441,404,442,385,1306,404,1287,402,1292,383,1307,401,1289,404,1290,400,443,405,1282,388,461,406,439,404,446,384,461,383,1303,404,1289,385,1303,405,442,404,1288,405,1286,404,1287,402,1290,403,442,406,440,405]}

Nature Remo に向けて赤外線を発射した後、 http でアクセスすれば JSON 形式で波形データを返してくれる。 で、この波形データの意味は? と思う間もなく答が見つかってしまった。 つまんない。

data配列の各要素は、赤外線ONの期間、OFFの期間、ONの期間、OFFの期間、、、、を表している。 厳密には、これは38kHzの変調をデコードしたあとの結果である。実際にはONの期間は38kHzの変調信号になっている。

ぱっと見 400前後の数値が多いなぁと思ったが、 1T 区間に対応するわけね、納得。 ざっと見た感じ 「赤外線ONの期間」 のほうが 「OFFの期間」 より短めになっている感じがしたので、 前者は 85 で割り算し、 後者は 115 で割り算してみた。 この「商」(割り算した結果) の個数だけ 2進数の 1 と 0 を並べ、 16進数に変換すればオシマイ。

Nature Remo 形式から PC-OP-RS1 形式への変換スクリプト:

#!/usr/bin/perl
use strict;
use warnings;

my @data = (3357,1717,385,1305,377,468,386,460,386,460,387,459,383,463,385,462,382,463,383,463,383,462,384,465,381,465,379,1306,386,463,382,460,385,465,381,467,381,459,385,462,384,462,377,1312,386,1309,384,459,387,462,382,460,386,460,385,461,385,462,385,1306,385,461,384,1307,384,461,386,1306,402,1291,399,1291,382,1307,388,456,404,1290,401,443,403,1287,407,440,402,445,401,443,386,462,400,445,384,463,382,464,381,464,384,1306,401,1292,383,1302,409,1285,402,1289,383,1309,383,1306,387,1304,379,1312,407,1285,402,443,405,441,386,460,403,443,385,460,404,442,385,464,403,440,404,1289,405,1285,404,1282,388,1304,409,1282,390,1301,406,441,386,1306,407,436,410,441,401,440,406,443,385,1304,407,1284,404,1287,405,441,386,1305,408,1287,404,1285,385,1304,407,441,383,462,389,40199,3376,1697,384,1306,407,441,402,442,407,440,384,465,400,443,405,442,383,460,385,464,384,461,401,445,402,441,404,1291,400,446,384,461,400,446,381,463,383,466,399,444,402,444,383,1304,407,1287,401,444,384,460,404,442,407,439,406,442,403,441,404,1287,386,460,403,1289,400,445,403,1287,407,1282,409,1285,385,1307,400,444,405,1285,404,442,404,1289,402,442,405,441,401,444,386,459,404,442,405,441,403,443,404,442,407,1286,405,1288,398,1286,406,1285,409,1283,404,1287,407,1286,401,1294,406,1284,401,1288,401,446,381,462,402,445,401,444,402,444,384,462,383,466,398,442,407,1286,401,1288,403,1288,404,1288,403,1288,401,1290,407,439,404,1284,406,441,404,441,409,436,408,439,407,1285,406,1285,403,1289,400,446,402,1288,403,1287,405,1284,404,1286,409,437,406,444,404,39760,3379,1697,402,1288,404,442,405,441,401,444,407,439,406,441,401,446,402,441,407,439,402,445,406,441,401,443,401,1290,405,437,404,446,405,441,402,441,402,442,406,441,403,441,404,1290,402,1290,400,446,404,439,407,439,406,439,404,442,404,442,406,1288,401,442,402,1290,385,462,401,1289,401,1290,404,1288,399,1288,404,441,386,1306,402,446,402,1287,403,446,401,442,402,444,401,445,404,441,402,444,402,444,402,446,400,1290,398,1290,402,1287,385,1309,404,1287,400,1289,403,1292,401,1283,390,1302,387,1304,404,445,384,460,408,436,405,442,385,462,402,443,404,441,404,442,385,1306,404,1287,402,1292,383,1307,401,1289,404,1290,400,443,405,1282,388,461,406,439,404,446,384,461,383,1303,404,1289,385,1303,405,442,404,1288,405,1286,404,1287,402,1290,403,442,406,440,405);
my $str = "";
my $bit = 1;
for my $d (@data) {
    if ($bit) {
	$str .= $bit x ($d / 85);
	$bit = 0;
    } else {
	$str .= $bit x ($d / 115);
	$bit = 1;
    }
}
$str = unpack("H*", pack("b*", $str)). "\n";
print "$str\n";

1行スクリプトに書けなくもないが、 まあ無理に 1行にしなくても、 このくらいならソッコーで書ける。 やっぱり perl が一番 :-)。

実行してみると ↓ こんな感じ。 波形データを PC-OP-RS1 形式に変換して初めて気付いたが、 80個以上の 0 が連なる区間 (2進数だと 320個以上、つまり 32ミリ秒以上の空白) があり、 3つの波形データに分けられることが分かる。

senri:~ $ ./irconv.pl
ffffffff7f00e001f0f0f0f07878787878787878003c3c3c3c3c1e1e1e000f80c7c3c3c3c3c303e0e101f0f00078003c001e008f07c0e301f0783c1e1e0f0f0f0f8007c003e001f00078003c001e000f8007c0e3f1f078783c3c1e000f8007c003e001f000783c001e8fc7e301f00078003c1e000f8007c003e0f1f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0ffffffff0f003c001e8fc7c3e3f1f0f0f0783c001e0f8f8787c7e301f000783c3c1e8fc703e0e101f078003c001e000f80c703e0f100783c1e8fc7e3f178003c001e000f8007c003e001f00078003c001e0f8fc7e3e1e1f10078003c001e000f8007c0e301f0783c1e0f8007c003e0f10078003c001e008fc703000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8ffffffff03000f80c7e3f1783c1e8fc7e3f100783c1e8fc7e3f10078003c1e8fc7e3f100783c001e1e000f8007c003e0f100783c001e8fc7e3f1783c1e000f8007c003e001f00078003c001e000f80c7c3e3f1f0783c1e000f8007c003e001f000783c001e1e8f8707c003e001f078003c001e000f80c7e301

というわけで、 上記「変換スクリプト」をちょこっと書き直して、 赤外線信号が表現しているデータを表示するようにしてみる。 NECフォーマットでも、 家製協(AEHA, 家電製品協会)フォーマットでも、 赤外線OFFの期間が 1T のとき「0」で、 3T のとき「1」だから、 赤外線ONの期間は無視して、 赤外線OFFの期間が 1000以上の時は 1 で、以下なら 0、 そして 3000以上なら信号の切れ目。

Nature Remo 形式から家製協(AEHA)フォーマットへの変換スクリプト:

#!/usr/bin/perl
use strict;
use warnings;

my @data = (3357,1717,385,1305,377,468,386,460,386,460,387,459,383,463,385,462,382,463,383,463,383,462,384,465,381,465,379,1306,386,463,382,460,385,465,381,467,381,459,385,462,384,462,377,1312,386,1309,384,459,387,462,382,460,386,460,385,461,385,462,385,1306,385,461,384,1307,384,461,386,1306,402,1291,399,1291,382,1307,388,456,404,1290,401,443,403,1287,407,440,402,445,401,443,386,462,400,445,384,463,382,464,381,464,384,1306,401,1292,383,1302,409,1285,402,1289,383,1309,383,1306,387,1304,379,1312,407,1285,402,443,405,441,386,460,403,443,385,460,404,442,385,464,403,440,404,1289,405,1285,404,1282,388,1304,409,1282,390,1301,406,441,386,1306,407,436,410,441,401,440,406,443,385,1304,407,1284,404,1287,405,441,386,1305,408,1287,404,1285,385,1304,407,441,383,462,389,40199,3376,1697,384,1306,407,441,402,442,407,440,384,465,400,443,405,442,383,460,385,464,384,461,401,445,402,441,404,1291,400,446,384,461,400,446,381,463,383,466,399,444,402,444,383,1304,407,1287,401,444,384,460,404,442,407,439,406,442,403,441,404,1287,386,460,403,1289,400,445,403,1287,407,1282,409,1285,385,1307,400,444,405,1285,404,442,404,1289,402,442,405,441,401,444,386,459,404,442,405,441,403,443,404,442,407,1286,405,1288,398,1286,406,1285,409,1283,404,1287,407,1286,401,1294,406,1284,401,1288,401,446,381,462,402,445,401,444,402,444,384,462,383,466,398,442,407,1286,401,1288,403,1288,404,1288,403,1288,401,1290,407,439,404,1284,406,441,404,441,409,436,408,439,407,1285,406,1285,403,1289,400,446,402,1288,403,1287,405,1284,404,1286,409,437,406,444,404,39760,3379,1697,402,1288,404,442,405,441,401,444,407,439,406,441,401,446,402,441,407,439,402,445,406,441,401,443,401,1290,405,437,404,446,405,441,402,441,402,442,406,441,403,441,404,1290,402,1290,400,446,404,439,407,439,406,439,404,442,404,442,406,1288,401,442,402,1290,385,462,401,1289,401,1290,404,1288,399,1288,404,441,386,1306,402,446,402,1287,403,446,401,442,402,444,401,445,404,441,402,444,402,444,402,446,400,1290,398,1290,402,1287,385,1309,404,1287,400,1289,403,1292,401,1283,390,1302,387,1304,404,445,384,460,408,436,405,442,385,462,402,443,404,441,404,442,385,1306,404,1287,402,1292,383,1307,401,1289,404,1290,400,443,405,1282,388,461,406,439,404,446,384,461,383,1303,404,1289,385,1303,405,442,404,1288,405,1286,404,1287,402,1290,403,442,406,440,405);
my $str = "";
my $bit = 1;
my $skip = 2;
for my $d (@data) {
    next if $skip-- > 0;
    if ($bit) {
	$bit = 0;
    } else {
	if ($d > 3000) {
	    print unpack("h*", pack("b*", $str)). "\n";
	    $str = "";
	    $skip = 2;
	} elsif ($d > 1000) {
	    $str .= "1";
	} else {
	    $str .= "0";
	}
	$bit = 1;
    }
}
print unpack("h*", pack("b*", $str)). "\n"; 

実行結果を以下に示す。 3つの波形は同じデータ 「10010305fa00ff30cf2cd3」(低 nybble が先) の繰り返しだった。 前掲した PC-OP-RS1 形式への変換スクリプトで得た波形データは 454バイトもあったが、 3つの波形が同じなら最初の 1波形 124バイトだけでよいことになる。 PC-OP-RS1 は一度に送ることができる赤外線データが 240バイトという制限があるので、 1波形のみ送ることにした。

senri:~ $ ./iraeha.pl
10010305fa00ff30cf2cd3
10010305fa00ff30cf2cd3
10010305fa00ff30cf2cd3

以下は、 PC-OP-RS1 で赤外線の送信を行うスクリプト。 -d オプションで PC-OP-RS1 のデバイスを指定する。 受光部分が壊れてしまったので、赤外線を学習する機能はない。 前述したような方法 (Nature Remo 等の学習リモコンで元データを生成して変換) で赤外線の波形データを作成し、 連想配列 %Ir に設定する。

緊張しながらこのスクリプトを実行 「./pc-op-rs1 -d /dev/PC-OP-RS1 off」 すると...
みごと 日立LED照明器具 LEC-AHS810K が消灯した。 ということは日立製作所のメーカ識別コードが 0x1001 ってこと? どこかに家製協のメーカ識別コード (カスタマーコード) の一覧って無いだろうか? ちなみに「全灯」ボタンは「10010305fa00ff20df2cd3」だった。

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
use Device::SerialPort;

my %Ir;
$Ir{'on'} = pack("H480", "ffffffff7f00e001f0f0783c1e8fc7e3f1783c001e1e8fc7e3f178003c001e1e8fc7e3f100783c001e1e000f8007c003e0e101f0f00078783c1e8fc7e3f10078003c001e000f8007c003e001f078003c1e0f8fc7c303e0e101f00078003c001e000f80c703e0e1e1f1f00078003c001e1e000f8007c003e0e1f1");
$Ir{'off'} = pack("H480", "ffffffff7f00e001f0f0f0f07878787878787878003c3c3c3c3c1e1e1e000f80c7c3c3c3c3c303e0e101f0f00078003c001e008f07c0e301f0783c1e1e0f0f0f0f8007c003e001f00078003c001e000f8007c0e3f1f078783c3c1e000f8007c003e001f000783c001e8fc7e301f00078003c1e000f8007c003e0f1f0");

our ($opt_v, $opt_d, $opt_c);
getopts("vd:c:") || help();
defined $opt_d || die "option -d is needed\n";

my $port = new Device::SerialPort($opt_d) || help();
$port->user_msg(1);
$port->error_msg(1);
$port->baudrate(115200);
$port->databits(8);
$port->parity("none");
$port->stopbits(1);
$port->handshake("none");
$port->read_const_time(100); # 0.1 sec
$port->read_char_time(5);
send_ir($port, "\x69");
recv_ir($port, 1, 3);

my $ch = 1;
if ($opt_c) {
    if ($opt_c =~ /^[1-4]$/) {
	$ch = $opt_c;
    } else {
	help();
    }
}

while ($_ = shift @ARGV) {
    defined $Ir{$_} || help();
    send_ir($port, "\x74")
	&& recv_ir($port, 1, 3) eq "\x59"
	&& send_ir($port, pack("C", 0x30+$ch))
	&& recv_ir($port, 1, 3) eq "\x59"
	&& send_ir($port, $Ir{$_})
	&& recv_ir($port, 1, 3) eq "\x45"
	&& next;
    die;
}
$port->close;
exit 0;


sub send_ir {
    my ($port, $data) = @_;
    $port->write($data);
    print STDERR "send: ", unpack("H*", $data), "\n" if $opt_v;
}

sub recv_ir {
    my ($port, $len, $timeout) = @_;
    my $i = 0;
    my $j = 0;
    my $data;
    while ($i < $len) {
	my ($l, $d) = $port->read(1);
	if ($l > 0) {
	    $data .= $d;
	    $i += $l;
	    $j = 0;
	} else {
	    $j++;
	    if ($timeout > 0 && $j > $timeout) {
		print STDERR "TIMEOUT to read $len byte\n";
		return "";
	    }
	}
    }
    print STDERR "recv: ", unpack("H*", $data), "\n" if $opt_v;
    return $data;
}

sub help {
    print STDERR <<EOF;
Usage: pc-op-rs1 [opt] <com>...
opt:   -d <dev>   device (MUST)
       -c <ch>    channel (1..4)
       -v         verbose
EOF
    print "com: ", join(" ", sort keys %Ir), "\n";
    exit 1;
}

サーバの MAC アドレスを偽装 (MAC spoofing) してエッジ・スイッチの MAC アドレスと同一にしてみた 〜 プロバイダの接続台数制限を回避する

$
0
0

多くの機器で MACアドレス (Media Access Control address) は偽装できる。 例えば Linux サーバなら次のような感じ:

# ip link set dev eth0 down
# ip link set dev eth0 address 00:11:22:33:44:55
# ip link set dev eth0 up
# ip link show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 00:11:22:33:44:55 brd ff:ff:ff:ff:ff:ff

インタフェースが立ち上がった状態では変更できないので、 まず eth0 を down させているが、 サーバの起動時などインタフェースが立ち上がる前なら、もちろん不要。 そして MACアドレスを 「00:11:22:33:44:55」 に偽装している (MAC spoofing)。

     インターネット
        │
        │
   ┌────┴────┐
   │ マンション共用部│
   │   のルータ  │
   └─┬┬┬┬┬┬┬─┘
 ┌───┘│││││└───┐
 │ ┌──┘│││└──┐ │
 │ │ ┌─┘│└─┐ │ │
 : : :  │  : : :
  各戸へ   │   各戸へ
        │
     ┌──┴──┐
     |スイッチE|
     └┬─┬─┬┘
  ┌───┘ │ └───┐
  │     │     │
  :  ┌──┴──┐  :
 各部屋 |スイッチR| 各部屋
     └┬┬┬┬┬┘
  ┌───┘│││└───┐
  │ ┌──┘│└──┐ │
  ↓ ↓   ↓   ↓ ↓
  他のPC  サーバ  他のPC

私の自宅内の LAN において、 サーバ (左図下端) の MACアドレスを偽装して、 スイッチE (マンション共用部のルータにつながるエッジ・スイッチ) の MACアドレスと同一にしてみた。

なぜこんなことをするか?を、順を追って説明する:

マンション共用部のルータ (以下、「ルータM」と略記) は、 各戸に最大 5個のグローバル IPアドレスを割当てる。 つまり、 「ルータM」は各戸 (マンション専有部) ごとに接続された機器を数えていて、 接続を検知した順に先着 5台にのみグローバル IPアドレスを割当てる。

割当てられるグローバル IPアドレスが 5個なのではなく、 先着 5台の機器のみに、 DHCP リクエストがあれば IPアドレスを割当てる点に注意。 DHCP リクエストを行わない機器の MACアドレスがルータM に届いてしまえば、 それも 1台としてカウントされる。

困ったことにスイッチE (GS108E) は、 (L2スイッチのくせに) MACアドレスを持っていて、 定期的に UDP ブロードキャスト (NSDP) を行う。 このブロードキャストがルータM に届くと、 スイッチE が「接続機器」とみなされてしまい、 貴重なグローバル IPアドレス枠が一つ浪費されてしまう

NSDP のブロードキャストを止めることが可能ならそれが一番だが、 残念ながら GS108E の管理ツール (Windows アプリ) にはそういう設定項目は無い。 スイッチE の MACアドレスがルータM に届くのを阻止する術は無さそうだ。 もちろん、 スイッチE とルータM との間にブリッジを挟んでブロードキャストをフィルタリングすれば阻止できるが、 そんなボトルネックは作りたくない。

スイッチR (GS116E ポート数が異なる他は GS108E と同等) も同様に MACアドレスを持っていて NSDP のブロードキャストを行うし、 もちろん (グローバル IPアドレスを持たない) その他の PC 等も様々なパケットを送信するが、 いずれもルータM と直接つながっていないので、 VLAN を設定することでパケットがルータM へ届くのを阻止することが可能。

上記ネットワーク図には、 家庭内LAN に必ずある NATルータ (Wi-Fiルータ等) が見当たらないが、 Linux サーバが NATルータの役割を果たしている。 GS108E のようなタグVLAN 機能付スイッチを使うと、 物理的な配線にとらわれずに自在に LAN を構成できるので便利。

つまり、 MACアドレスがルータM に届くのを阻止できないのは、 ルータM と直接つながっているスイッチE のみ、 ということになる。

そこで、 スイッチE が「接続機器」と見なされるのが避けられないなら、 逆にスイッチE の MACアドレスでグローバル IPアドレスを取得してやろうと考えた次第。 サーバの MACアドレスを偽装してスイッチE の MACアドレスと同一にすることで、 このサーバは無事グローバル IPアドレスを取得できた。

もちろん、 これでは同一セグメント内に同じ MACアドレスを持つ機器が存在することになってしまう。 これはネットワークの教科書的には、 決してやってはいけないことだ。

一番の問題はスイッチの MACアドレス学習が混乱する点。 上図でいうと「スイッチR」 (部屋ごとに設置しているスイッチ) は、 上の「スイッチE」からも下の「サーバ」からも、 同じ MACアドレスを送信元とするパケットが届いてしまう。

また、2番目の問題として、 スイッチE に自身の MACアドレス宛のパケットが届いたとき、 それを正しくサーバへ中継するのではなく、 自身宛と見なして中継しない (そのまま捨ててしまう) 恐れがある。

まず 1番目の問題は、 スイッチE のブロードキャストの間隔が充分長ければ、 実用上の問題は起きないだろうと考えた。 確かに、 スイッチE のブロードキャストがスイッチR に届けば、 スイッチR で誤学習が起きる。 この状態でスイッチR にサーバ宛のパケットが届くと、 スイッチR はサーバへ送らずに、 スイッチE へ送ってしまう。

が、サーバは常時通信を行っているわけだから、 スイッチR には速やかにサーバからパケットが届いて誤学習状態は直ちに解消される。 一時的な誤学習は、 それが低頻度かつ短時間であればパフォーマンス上の問題は起きないだろう。

2番目の問題は、 スイッチの機種にも依存するが、 少なくとも私が使ってる GS108E-100JPS の場合は問題にならない。 つまり自身の MACアドレス宛のパケットが届いても、 それを自分宛とは見なさず、 他のパケットと同様に中継する。

例えばインターネットからサーバ宛に届くパケットの場合、 ルータM はサーバの MACアドレス (つまりスイッチE の MACアドレス) を宛先としたパケットを送信するが、 このパケットはスイッチE において何事もなく中継されて、 サーバに正しく届く。

なお、 GS108E-100JPS の後継機 GS108E-200JPS は、 管理ツール以外に WWW ブラウザで管理することもできる。 つまり (簡易な) Web サーバを内蔵しているわけで、 このようなスイッチの場合は、 スイッチの MACアドレスを宛先とするパケットは、 この Web サーバによって受信されてしまい、 このスイッチにおいては中継されないと予想される。

実際に GS105E-200JPS (ポート数が異なる他は GS108E-200JPS と同等と考えられる) で実験してみたところ、 スイッチの MACアドレスを宛先とするパケットは、 スイッチで中継されなかった。 したがって GS105E-200JPS や GS108E-200JPS を、 スイッチE として使うことは、 グローバル IPアドレス枠を一つ浪費することになるので適切ではない。

もちろん、 スイッチE として一番適切なのは、 無用なブロードキャストを吐かない (あるいは吐かない設定が可能な) スイッチである。 接続台数に制限があるプロバイダを利用する際は、 エッジ・スイッチの挙動の細かな違いにも気を配りたい。

IFTTT のアプレットが 3個に制限されてしまったので、IFTTT を使わずに スマート家電リモコン RS-WFIREX4 をコントロールしてみた 〜 RS-WFIREX4 の通信プロトコルの解析

$
0
0

IoT機器を IFTTT (IF This Then That) に登録すると、 自前のプログラムからコントロールできるようになる。 つまり IFTTT の webhooks を使うことで、 IFTTT の特定の URL を自前のプログラムからアクセスするだけで IoT機器がコントロールできる。 例えばこんな感じ:

senri:~ $ curl https://maker.ifttt.com/trigger/light_on/with/key/dD-v7GCx46LnWaF1AD9nwSUeA_N1ALvDHKS57cP1_Md
Congratulations! You've fired the light_on event

「light_on」の部分は任意に定めることができる。 この例では照明を点灯させている。

Nature Remo のように API を公開している IoT機器なら、 自前のプログラムから API を直接たたけばよいが、 残念ながら IoT機器の多くが API 非公開なので、 IFTTT が唯一のコントロール手段となっていた。 IoT機器の操作一つ一つ (例えば light_on) に、 IFTTT アプレットを作ることになるので、 私の場合は 50個以上のアプレットを作っていた。

ところが!

有料版の IFTTT Pro が新たに発表され、 従来の無料版は登録できるアプレットが 3個に制限されてしまった。 有料版なら無制限にアプレットを作ることができるが、 無料版だと最大 3個しかアプレットを作ることができない。

かくなる上は、 IoT機器の API (通信プロトコル) を解析して IFTTT 抜きで IoT機器をコントロールするしかない。 いままでも IFTTT に登録できない IoT機器については API を解析していたので、 なんとかなるだろう。

IoT機器のほとんど (全て?) がスマホからコントロールできるので、 root 権限を取得できる Android 端末があれば、 スマホのアプリと IoT機器との間の通信を tcpdump 等で観察することができる。 通信が暗号化されていなければ API を解析するのは (比較的) 容易。

というわけで、 ラトックシステム社 スマート家電リモコン RS-WFIREX4 の API を解析してみた。 幸い、 RS-WFIREX4 の Android 用アプリ スマート家電コントローラ は、 家中モード (スマホと RS-WFIREX4 が同一セグメントにある場合) では通信が暗号化されていない。 スマホ上で tcpdump を実行することで通信 (TCP/IP) 内容を見ることができた。

RS-WFIREX4 は温度、湿度、明るさを計測することができる。 アプリが RS-WFIREX4 の TCP ポート 60001番に対して 5バイトのデータ 「AA 00 01 18 50」 を送信すると、 RS-WFIREX4 から 13バイトのデータ 「AA 00 09 18 00 01 5E 00 E5 00 0A B2 08」 が返ってきた。 最初の 5バイト 「AA 00 09 18 00」 は部屋の温度等に関係なく常に同一だったので、 ヘッダ (つまり計測データは含まれない) と考えられる。

この手の通信プロトコルでは、 可変長のデータを扱うためにヘッダにペイロード (ヘッダ以外のデータ本体) の長さが含まれる (ことが多い)。 この例ではヘッダ以外のデータの長さは 13 - 5 = 8 バイトなので、 おそらく 「00 09」 が (ビッグエンディアンの) ペイロード長だろうとあたりをつける。 つまりヘッダは 「AA 00 09 18」 の 4バイトで、 続く 9バイト 「00 01 5E 00 E5 00 0A B2 08」 がペイロードということになる。

┌─┬─┬─┬─┬─┐
│頭│ペイ長│測│検│
└─┴─┴─┴─┴─┘
│←─ヘッダ─→│ペ│

アプリが送信したデータ 「AA 00 01 18 50」 も、 先頭が 「AA」 (図中では 「頭」 と略記) であることから同じフォーマットである可能性が高い。 つまり 「00 01」 がペイロード長 (図中では 「ペイ長」 と略記) で、 「50」の 1バイトがペイロードなのだろう。 「50」は 「計測データを送信せよ」 という命令の可能性も無くはないが、 おそらく後述するチェックサムだろう (図中では 「検」 と略記)。

そして、 ヘッダ末尾の 「18」 のほうが、 「計測データを送信せよ」 という命令である可能性が高い (図中では 「測」 と略記)。 RS-WFIREX4 の応答のヘッダの末尾も 「18」 だが、 これは命令 「18」 に対する応答であることを示しているのだろう。

┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│頭│ペイ長│測│0│湿度%│温度℃│明るさ│安│検│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
│←─ヘッダ─→│←─────ペイロード─────→│

部屋を明るくしたり暗くしたり、 温度を上げたり下げたりしたときに、 RS-WFIREX4 からの応答がどのように変化するか調べることで、 ペイロードの 2, 3バイト目 「01 5E」 (10進数で 350) は、 「湿度 35.0 %」 を示していると推測できた。 以下同様に、 4, 5バイト目 「00 E5」 (10進数で 229) は、 「温度 22.9 ℃」 を、 6, 7バイト目 「00 0A」 (10進数で 10) は、 「明るさ」 を示している。

ペイロードの 8バイト目 「B2」 は、 RS-WFIREX4 の電源を入れた瞬間は 0 で、 時間の経過と共に増大し、 充分時間が経つと 255 になる。 RS-WFIREX4 は電源投入後 30分間はセンサーが使えないので、 おそらくセンサーの安定度合い (0〜255) を示していて、 この数値が一定以上 (255?) でないとセンサーの値が正確でないことを示しているのだろう。

末尾の 1バイトは後述するチェックサムと思われる。 なぜなら、 末尾の 1バイトを除くペイロードの内容が同じ (つまり湿度・温度・明るさ・安定度の組合わせが同一) 応答データであれば、 末尾の 1バイトも (少なくとも私が観察した範囲では) 同じ値になっているから。

AA 00 AE 11
00 00 AA
22 11 04 04 05 04 04 0D 04 0D 04 04 05 0C 05 04
04 04 05 04 04 0D 04 04 05 04 04 0D 04 04 05 0C
05 04 04 0D 04 04 05 04 04 0D 04 04 05 04 04 04
05 04 04 04 05 04 04 0D 04 0D 04 04 05 0C 05 04
04 04 05 0C 05 04 04 0D 04 04 05 04 04 0D 04 04
05 04 04 FF FF FF 07 23 10 05 04 04 04 05 0C 05
0C 05 04 04 0D 04 04 05 04 04 04 05 0C 05 04 04
04 05 0C 05 04 04 0D 04 04 05 0C 05 04 04 04 05
0C 05 04 04 04 05 04 04 04 05 04 04 04 05 0C 05
0C 05 04 04 0D 04 04 05 04 04 0D 04 04 05 0C 05
04 04 04 05 0C 05 04 04 04 05
69

次に赤外線を発射させて家電をコントロールしてみる。

← 左の 178バイトのデータをアプリが送信すると、 RS-WFIREX4 が発射した赤外線を受けて天井照明が点灯し、 RS-WFIREX4 から 6バイトの応答 「AA 00 02 11 00 D1」 が返ってきた。

送信データの最初の 4バイト 「AA 00 AE 11」がヘッダで、 ペイロードの長さが 00AE (10進数だと 174) であることが分かる。 ヘッダ末尾の 「11」 が、 「赤外線を発射せよ」 という命令なのだろう (図中では 「射」 と略記)。 アプリを操作して RS-WFIREX4 にいろいろ (長さが異なる) 赤外線を発射させてみたところ、 3行目 「22 11 04 04 ...」から始まる 170バイトが赤外線の波形データ (後述) で、 その直前 (2行目) の 「00 AA」 (10進数で 170) が赤外線の波形データの長さ (図中では「デー長」と略記) を表わしているようだ。

┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐
│頭│ペイ長│射│0│デー長│赤外線の波形データ│検│
└─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘
│←─ヘッダ─→│←─────ペイロード─────→│

ペイロード末尾の 1バイト 「69」 の算出方法は不明だが、 赤外線の波形データによって値が変わることと、 末尾であることからチェックサムのようなものと思われる。 もちろん単純なチェックサムではなく、 なんらかのエラー検出符号のようなものなのだろう (図中では 「検」 と略記)。 この 1バイトは、 「ペイロード長」には含まれるが、 「赤外線の波形データの長さ」には含まれない。

このペイロード末尾の 1バイトだけ異なるデータを送信すると RS-WFIREX4 はこの 178バイトの送信データ全体を無視する。 赤外線も発射しないし、 何の応答も返さない。 前述したように家中モードでは何のセキュリティもないので、 この末尾の 1バイトが (正規の) アプリから送信されたことを保証する唯一の認証手段なのだろう。 ここでは、 RS-WFIREX4 に対する送信データ (あるいは RS-WFIREX4 からの応答データ) におけるこの末尾の 1バイトを「チェックサム」と呼ぶことにする。

170バイトの赤外線の波形データは、 1バイト目の 「22」(10進数だと 34) が赤外線の ON の区間を表し、 2バイト目の 「11」が赤外線の OFF の区間を表し、 以下同様に奇数バイト目が赤外線の ON の区間を表し、 偶数バイト目が赤外線の OFF の区間を表す。 各バイトの数値の 1/10000 が各区間の秒数になる。 例えば 「04」 の場合は 0.4ミリ秒になり、 家製協(AEHA)フォーマットの 1T 区間に相当する。 1バイト目の 「22」は 8T 区間、 2バイト目の 「11」は 4T 区間、 8バイト目の 「0D」は 3T 区間に相当する。

つまり赤外線の波形データの最初の行 (3行目) は、 赤外線 ON が 8T (3.2ミリ秒) 続き、 次に OFF が 4T (1.6ミリ秒) 続き、 以下 ON 1T, OFF 1T, ON 1T, OFF 1T, ON 1T, OFF 3T, ON 1T, OFF 1T, ON 1T, OFF 3T, ON 1T, OFF 1T という波形になる (上図 ↑)。 ただし ON の区間は赤外線が点きっぱなしになっているのではなく、 38kHz の赤外線パルス (デューティ比 1/3) を送信している。

この赤外線の波形データは、 アプリで 「リモコンデータ受け渡し⇒エクスポート⇒メールで送信」 を行うことで得られる XML データに含まれる (<code>...</code> の部分)。 あるいは他の学習リモコンの赤外線波形データを変換しても良い。

実を言うと、 ここまでは昨年の段階で解析済だった。 ペイロード末尾の 1バイトの算出方法が判明したら公開しようと思っていたのだが、 いろいろ他にも忙しくて :-) 放置してしまっていた。 アプリを逆コンパイルするのは骨が折れるのと、 算出しなくても tcpdump で見れば値が得られるので実用上は困らなかったから。 で、今回 IFTTT が有料化したので急遽公開することにした次第。

チェックサムの算出方法が分からないといっても高々 1バイトである。 256通りなんてブルートフォース攻撃というほど brute でもない。 幸い、 RS-WFIREX4 はチェックサムが違うデータを立て続けに受信しても、 異常動作することはないようだ (もちろん常時チェックサム違いのデータを送信することは推奨できない)。

赤外線の波形データごとに 00 〜 FF まで 256通りのチェックサムを試して、 RS-WFIREX4 から応答が返ってきたら、 赤外線の波形データにそのチェックサムを付加して記憶しておけばよい。 赤外線の波形データの前の 3バイトおよびヘッダは、 発射する際に都度算出すれば良い。

チェックサムが正しい場合、 RS-WFIREX4 は赤外線を発射して 「AA 00 02 11 00 D1」 を返す。 ヘッダ末尾の 「11」 は、 命令 「11」 (赤外線を発射せよ) の応答であることを示す。

┌─┬─┬─┬─┬─┬─┐
│頭│ペイ長│射│0│検│
└─┴─┴─┴─┴─┴─┘
│←─ヘッダ─→│←ペ→│

ペイロードは 「00 D1」 の 2バイトだが、 ペイロードが 2バイト以上の場合、 ペイロードの先頭は常に 「00」 であるようだ (図中では 「0」 と表記)。 末尾の 「D1」 はチェックサムだろう。 実質的にチェックサムだけのペイロードならば、 1バイトのペイロードで充分だと思うが、 アプリが送信するデータの場合は 1バイトのペイロードが有り得ても、 RS-WFIREX4 が返す応答データの場合は常に 2バイト以上になるのかもしれない。

RS-WFIREX4 をコントロールする perl スクリプト wfirex.pl を以下に示す:

#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Std;
use IO::Socket::INET;

my %Ir;
$Ir{'on'} = pack("H*", "221104040504040D040D0404050C050404040504040D04040504040D0404050C0504040D04040504040D040405040404050404040504040D040D0404050C05040404050C0504040D04040504040D0404050404FFFFFF07231005040404050C050C0504040D040405040404050C05040404050C0504040D0404050C05040404050C050404040504040405040404050C050C0504040D04040504040D0404050C05040404050C050404040569");
$Ir{'off'} = pack("H*", "221005040404050C050C0504040D040405040404050C05040404050C0504040D0404050C05040404050C0504040405040404050C050C050C050C0504040D040405040404050C050C05040404050C0504040405FFFFFF07221104040504040D040D0404050C050404040504040D04040504040D0404050C0504040D04040504040D0404050404040504040D040D040D040D0404050C050404040504040D040D04040504040D040405040437");
$Ir{'small'} = pack("H*", "221104040405040D040D0404040D040504040504040D04040405040D0404050C0504040D04040504040D04040405040404050404050C050C050C0504040D04040405040D040D040D04040504040D0404040504FFFFFF08221104040405040D040D0404050C040504040405040D04040405040D0404050C0405040D04040405040D04040504040404050404040D050C050C0504040D04040504040D040D040D04040405040D0404040504E9");
$Ir{'aoff'} = pack("H*", "211005040404040d0404040d040404040504040c050c040405040404040d040c0405040404040504040405040404040405040404050404040404050c0404050404040405040404040504040c0504040404050404040d040404040504040404050404040c0504040c050c040d040c040d040c04d7");
$Ir{'study_on'} = pack("H*", "2210050C04050404040405040404050404040405040404050404040D0404040504040405040404050404040D040D040404050404040504040405040C0504040D0404040D040D040C050C0405040C0504040D04040405040404050404040405040404050C050C040D040C050C050C040D040D0404050C040405040404050404040504040C0504040D040C050C050C040D040D0404040D0404050404040504040C050C050C0405040C050C040D040D0404040504FF2E2111040C05040405040404050404040404050404040504040405040C0504040404050404040504040405040D040C040504040405040404050404040D0404050C0405040C050C040D040D0404040D0404050C04050404040504040404050404040504040D040C050C040D040D040C050C040D0404050C040504040405040404050404040D0404040D040D040D040C050C050C0405040C0504040404050404040D040D040C0504040D040C050C050C0405040404FF292111040D04040405040404050404040504040405040404050404040D0404040504040405040404050404040D040D040404050404040504040404050C0405040C0504040D040C050C040D0404050C0405040C05040404050404040504040404050404040D040D040C050C050C040D040C050C0504040D040404040504040405040404050C0504040C050C050C040D040D040C0504040D0404040504040404050C040D040D0404050C040D040C050C05040404042F");
$Ir{'study_off'} = pack("H*", "2210040D04050404040504040405040404040504040404050404040D0405040404050404040503050404040D040D040404050404040504040405040C0504040D0404040D040D040C040D0405040C0504040D04040405040404050404040504040405040C050C040D040D040C050C040D040D040C050C04050404040504040405040404050404040D040D040C050C040D040D0404040D0405040404040405040D040D040C0405040C050C040D040D0404040504FF2E2111040D04040405040404050404040504040405040404050404040D0404040504040405040404050404040D040C050404050305040404050404040D0405040C0504040D040C050C040D0404050C0405040C05040404050404040405040404050404040D040D040C050C040D040D040C050C040D040D04040504040405040404050404040504040C050C050C040D040C050C0504040D0404040504040404050C040D040D0404040D040D040C050C0504040404FF292210050C04050404040504040405040404050404040404050404040D0405040404050404040404050404040D040D040404050404040504040405040C0504040D0404040D040D040C050C0405040C0504040D04040405040404050404040405040404050C040D040D040C050C040D040D040D040C050C04050404040405040404050404040504040D040C050C040D040D040D0404040D0404040504040405040D040C040D0405040C040D040D040D040404050408");
$Ir{'study_aoff'} = pack("H*", "2c2b070f0610060f061006050605060f060506050605060406050610060f0605061006050604060506050605060505100610060f06100610060f0610060f060506050605060506040605060506050605060f06100605060f0610060506040605060506050605050506050605061005100610060506040605060506050605050506050610060505100610060505100610060505332d2b0610060f0610061005050605061006050505060506050605060f06100605060f0605060506050605050506050610060f0610061005100610060f06100605060506040605060506050605050506050610060f06050610060f06050605060506050505060506050605060505100610060f06050605060506050505060506050605060f06050610060f06050610060f06050675");

our ($opt_v, $opt_s);
getopts('vs') || help();
my $ip = shift || help();
my $command = shift || help();

if ($command eq "get") {
    my ($il, $te, $hu) = get_wfirex($ip);
    if (defined $il) {
	$te /= 10;
	$hu /= 10;
	print "il=$il te=$te hu=$hu\n";
    } else {
	print "wfirex get TIMEOUT $ip\n";
    }
} elsif (defined $Ir{$command}) {
    my $ret;
    if ($opt_s) {
	my $ir = substr($Ir{$command}, 0, length($Ir{$command})-1);
	for (my $i=0; $i < 256; $i++) {
	    my $checksum = pack("C", $i);
	    printf("try %02x ...\n", $i);
	    $ret = send_wfirex($ip, $ir . $checksum);
	    if ($ret) {
		printf("success ! checksum=%02x ret=%02x\n", $i, $ret);
		last;
	    }
	    sleep 1;
	}
    } else {
	$ret = send_wfirex($ip, $Ir{$command});
    }
}
exit 0;

sub get_wfirex {
    my ($ip) = @_;
    my $sock = IO::Socket::INET->new(
	PeerAddr  => $ip,
	PeerPort  => 60001,
	Proto     => "tcp",
	Timeout   => 5,
	);
    if ($sock) {
	print $sock "\xaa\x00\x01\x18\x50";
	my $buf;
	my $flags;
	$sock->recv($buf, 256, $flags);
	my @data = unpack("CCCCCnnnCC", $buf);
	close($sock);
	print join(" ", @data) . "\n" if $opt_v;
	return ($data[7], $data[6], $data[5]);
    }
    return undef;
}

sub send_wfirex {
    my ($ip, $ir) = @_;
    my $len = length($ir) - 1;
    $ir = "\xaa" . pack("n", $len+4) . "\x11\x00" . pack("n", $len) . $ir;
    my $sock = IO::Socket::INET->new(
	PeerAddr  => $ip,
	PeerPort  => 60001,
	Proto     => "tcp",
	Timeout   => 5,
	);
    if ($sock) {	
	print $sock $ir;
	my $buf;
	my $flags;
	$sock->recv($buf, 256, $flags);
	my @data = unpack("CCCCCC", $buf);
	close($sock);
	if (@data) {
	    print join(" ", @data) . "\n" if $opt_v;
	}
	return $data[5];
    }
    return undef;
}

sub help {
    print <<EOF;
Usage: wfirex <opt> <IP> <com>
opt:   -v   ; verbose
       -s   ; scan checksum
com: get    ; get sensor value
EOF
    print "     " . join(" ", sort keys %Ir) . "\n";
    exit 1;
}

連想配列 %Ir に赤外線の波形データを 16進数の文字列で格納しておく。 末尾の 2文字 (つまり 16進数 1バイト) がチェックサム。 チェックサムが不明のときは、 とりあえず「00」をつけておいて「-s」オプションでチェックサムを探索する。

senri:~ $ ./wfirex -vs wfirex4l on
try 00 ...
try 01 ...
try 02 ...
try 03 ...

  … 中略 …

try 67 ...
try 68 ...
try 69 ...
170 0 2 17 0 209
success ! checksum=69 ret=d1

赤外線の波形データ $Ir{'on'} のチェックサムが 「69」 (16進数) であることが判明したので、 とりあえずつけた末尾の 「00」 を 「69」 で置き換える (前掲のスクリプトは置き換え済)。

PayPay STEP の新基準をクリアしてみた 〜住民税と健康保険料の支払で1.5%還元〜

$
0
0

今月 7月から PayPay STEP の条件が改訂された。 先月までは合計 10万円以上 PayPay 残高払いすれば、 翌月の還元率が 0.5% から 1% へ 0.5 ポイントアップした。 ところがこれからは合計 5万円以上かつ 30回以上 PayPay 残高払いしないと 1% にならない。 10万円が 5万円に下がったが、 一ヶ月に 30回以上という条件は厳しすぎる。

なぜ還元率を 1% にしたいかというと、 住民税と健康保険料、合わせて 150万円の納付を PayPay 請求書払いで行いたいから。 還元率が 0.5% から 1% へ上がると 7500円ほど還元額が増える。 1% を超える高還元率のクレジットカードは多いが、 たいてい納税等には使えなかったり、 使えても上限額が低かったりする。 一ヶ月に 150万円までの納税等が 1% 還元でできる PayPay は貴重。

6月に健康保険料の第1期分 99,000円を PayPay 請求書払いで納付した。 で、その後ランチで 1133円を PayPay 残高払いした。 これで合計 10万円以上になったので、めでたく翌月 7月の還元率が 1% になった。

Toyonaka PayPay 20% campaign

7月になって請求書払いの還元率が 1% になったのが確認できたので、 住民税と健康保険料の残り (116万円ほど) を全て払ってしまおうと思ったが、 豊中市 x PayPay 20% 還元キャンペーンが始まってしまった。

私は普段 PayPay 残高払いを使わないが、 それは還元率が 0.5% と低いから。 PayPay しか使えないお店では PayPay クレジットカード払いを使うが、 PayPay 残高払いと違って PayPay クレジットカード払いだと PayPay の還元は無いし、 PayPay STEP の回数としてカウントされない。

たいていのお店でもっと高い還元率の支払手段があるし、 PayPay クレジットカード払いなら PayPay に登録したクレジットカードの還元 (私の場合は 2%) が得られる。 わざわざ低還元率の PayPay 残高払いを使う理由は何もない。 が、20% も還元してくれるとなると話は別である。

豊中市に住んでいて、 かつ普段ランチを食べる店や、 最寄りのドラッグストアが 20% 還元の対象店舗なので、 一ヶ月に 30回くらい PayPay 残高払いを使うのは造作もない。 これで 8月も還元率 1% を達成できるメドが付いた。

こうなってくると欲が出て、 さらに上の 1.5% 還元を目指したくなった。 還元率が 1% から 1.5% へ 0.5 ポイント上がると、 住民税と健康保険料の支払で付与される PayPay ボーナスが 7500円増える。 ただし付与上限が 15,000円なので、 1.5% 還元の場合は 1ヶ月に 100万円までの支払しか対象にならない。

1.5% 還元を得るには、以下の 4条件を満たさなければならない:

1) PayPay 支払
合計 5万円以上かつ 30回以上 PayPay 残高払い

2) 次の対象サービスのうち 3つ以上を利用
PayPayモール または Yahoo!ショッピング
PayPayフリマ または ヤフオク!
Yahoo!トラベル
ebookjapan
LOHACO by ASKUL
ただし、 ebookjapan は 300円以上の購入、それ以外は 1000円以上の購入が必要。

3) Yahoo!プレミアム会員登録
あるいはソフトバンクスマホユーザーかワイモバイルユーザー。

4) PayPayアカウントとYahoo! JAPAN IDを連携

条件 1) はメドが付いた。条件 3), 4) は登録するだけの話なので簡単。 私の場合 Yahoo!プレミアム会員登録が 6ヶ月無料だった。 問題は条件 2) である。 いずれのサービスも私は利用したことがない。 もちろん高々 1000円利用するだけだから、 要らないものを買ってもよければすぐ達成できる。 しかし還元率を 0.5 ポイント上げるために 3000円 (ebookjapan を利用する場合は 2300円) をドブに捨ててしまっては本末転倒である。 出来る限り有意義な買物をしたい。

私がなぜ普段 Yahoo!ショッピングを利用しないかと言えば、 割高なものばっかり目につくから。 どーみても amazon など他のサイトのほうが安い。 例えば Type-C の USBメモリを検索してみると、 64GB で 3000円もする。 64GB の相場は 1000円だと思っている私としてはアリエナイ。

ところが、私は夏のPayPay祭の 「はじめてのお買い物で使える半額クーポン」(上限 800円) の対象者らしい。

PayPay debut coupon

いくら割高でも、 半額なら買えるものもあるかもしれない。 ということで欲しいものを探してみた。 USBメモリは何個も持っているので今更欲しいとは思わないが、 Type-C/Type-A 両用型は 2個しか持っていない。 風見鶏で SanDisk製 128GB 両用型を税込1979円 (送料無料)で売っているのを見つけた。 半額クーポン (上限 800円) を適用して 1179円。 amazon だと 2080円なので悪くない。

これを 5のつく日、7/15 に買った。

PayPay 5 day campaign

私がなぜ普段 ヤフオク! などのフリマを利用しないかと言えば、 保証がない中古品を買う気が起きないから。 いくら安くてもゴミを買っては意味がない。 ただしいくつか例外がある。 チケットなど新品でも中古でも品質が変わらないものや、 CPU や DRAM (メモリ) など中古でも品質の劣化が (ほとんど) 問題にならないものは買うことがある。 メモリといっても、 もちろん USBメモリや SDカードなどのフラッシュメモリや SSD は、 使えば使うほど劣化するから決して中古品は買わない。

ちょうど DDR4 8GB の SO-DIMM (メモリ) を増設したいと思っていた。 ヤフオク! で検索してみると、 即決価格 3750円の SKhynix 製 DDR4-2400 8GB メモリが見つかった。 掲載されている写真を見ると、 私が使っているメモリと型番が同じだったので相性はバッチリ。 3750円というのは中古品としては特段安くないが、私は 「落札はじめて・ごぶさたの方 50%OFF クーポン」(上限 1000円) の対象者らしいので、 2750円になった。 もちろん、 受け取った落札品を厳重にメモリテストしてみたが、 全く問題は検出されなかった。

yahoo auctions coupon

というわけで、 残り 1サービスとなった。 Yahoo!トラベル、ebookjapan、LOHACO のどれを選ぶか? どれも食指が動かない。どうしても割高に見えてしまう。 仕方がないので一番損失が少なくなる ebookjapan を選ぶ。 ebookjapan なら 300円以上の購入で済むが、他は 1000円以上が必要。

ebookjapan bonus

私がなぜ電子書籍を買わないかと言えば、 Book Off で買う方が安いから (しかも株主なので優待券を利用できる)。 Book Off で 200円以下で買えそうな書籍は避けて、 日高敏隆著「ホモ・サピエンスは反逆する」 を買ってみた。 50%OFF クーポン (初回ログインでもらえる) を使って 363円になった。 さらに、金曜日に買うと 45% 戻ってくるらしく、 実質 200円以下になる?

以上で、 PayPay STEP の全条件をクリアしてゴールドメダルを獲得した。 来月 8月は PayPay 残高払いの還元率が 1.5% になる。 ただし 15,000円が付与上限なので、 100万円の支払までが対象。

健康保険料 990,000円のうち、第1期 99,000円は 6月に PayPay で払ったので、 7/1 時点で残り 891,000円。 住民税 486,800円のうち、第1期 123,800円は 6月に nanaco で払ったので、 残り 363,000円。 7月に健康保険 3期分 297,000円 (うち 99,000円は 7/3 に納付済) を払い、 8月に残り 957,000円 (健康保険料 594,000円+住民税 363,000円) を払うのが最適解と思われる。

8月の上限に 43,000円足らないが、 1.5% の還元率なら何か別の支払に使ってもよい。 得られる PayPay ボーナスは、 7月 2,970円 (297,000円の 1%)、 8月 14,355円 (957,000円の 1.5%) となる。

Viewing all 47 articles
Browse latest View live