黒猫の単語帳2nd公開停止のお知らせ
黒猫の単語帳2ndですが、現在動作していません。辞書提供元のiKnowがAPIの公開を停止している模様です。元々、iKnow APIは5年以上前に公開停止されていましたが、「保証はしないものの、動いているなら利用しても構わない」とのことで、そのまま黒猫の単語帳も公開を継続していました。
今現在、APIが完全に止まったわけではなく、JSONでエラーが返ってきています。そのため、もしかすると復帰する可能性も0ではないですが、限りなく低いと思われます。
近日中にVectorなどからも公開取り消しを行います。
代替辞書
代替辞書も検討したいところですが、iKnowレベルを求めると、無料では存在しないと言って良いところです。これはiKnow API公開停止通告があってから探してはいましたが、未だに状況は変わっていません。
iKnowに直接掛け合うことも策の1つですが、無料は流石に無理でしょう。月額いくらになると思いますが、黒猫の単語帳を有料化しない限り公開は難しいと思います。
Google TranslateやBing Translateも有料で、さらにアカウント辺りいくらの利用になるため、自分でサーバを立てて一元管理しなければなりません。サーバ代とAPI利用料がかかってくるので、これまた頭の痛いところです。
随分前にどこかで書きましたが、無料のWebAPIを利用するというのは、切られても文句の言えない弱い立場に置かれてしまいます。利益を出す仕組みを考えるというのは、安定的なサービスを提供する上で非常に大事なことで、お金が二の次になりがちなエンジニアであっても、そのことをよく念頭に置かなければならないと、ここ数年、特に強く思っています。
RaspberryPi ZeroでGPIOを使って音声出力するお話
RaspberryPi Zero(W)は3.5φジャックがないので、音声出力はHDMIかGPIOを利用して頑張るしかありません。GPIOを使って音声出力するお話は、既にいくつかの方々がまとめられています。
Raspberry Pi Zero のGPIOを利用した音声出力 – Roguer
https://learn.adafruit.com/adding-basic-audio-ouput-to-raspberry-pi-zero/pi-zero-pwm-audio
が、外部ソースについて説明がなかったりリンク切れしてたりしたので、ちょっと補足しておきます。
最も簡単な方法
dtoverlayを使用します。/boot/config.txt
に、以下を追加します。ちなみに/boot/config.txt
は、ラズパイSDをPCに挿すと見えます。
# GPIO18とGPIO13の組み合わせ dtoverlay=pwm-2chan,pin=18,func=2,pin2=13,func2=4
あるいは、
# GPIO12とGPIO13の組み合わせ dtoverlay=pwm-2chan,pin=12,func=4,pin2=13,func2=4
既にdtoverlayが別に定義されている場合はどうすればいいの?
ただ下に書き足してください。こんな感じで。
dtoverlay=dwc2 dtoverlay=pwm-2chan,pin=18,func=2,pin2=13,func2=4
gpio_altを利用して実行時に書き換える方法
WiringPiとgpio_altを使ってPWMが利用可能になるよう動的に変更します。詳しい手順は冒頭のリンク先を参照してください。下記はWiringPiとgpio_altのインストール方法補足です。
WiringPi
drogon氏のプロジェクトの1つです。Adafruitのページではtar.gzをbootの下に入れようみたいな説明を書いていますが、普通にgit cloneしても構わないと思います。
このページのトップに書いてある通り、git clone git://git.drogon.net/ + project name.
なので、
git clone git://git.drogon.net/wiringPi
でcloneできます。tar.gzで欲しい場合はsnapshotをクリックするとダウンロードできます。
2018. 2. 28追記
wiring-piは、apt-getでもインストールできるようです。
$ sudo apt-get install wiringpi
gpio_alt
konsuktaner氏のプロジェクトの1つです。githubに置いてあります。
README.mdに書いてある通り、gccでビルドすると、gpio_altのバイナリが生成できます。WiringPiについても書いてあるので、実はここを紹介すれば終わりとも言えなくもない。
gccを利用するので、動かない場合はsudo apt-get install build-essential
を実行しましょう。
何をしてるのかの解説
ちょっとした解説を書きました。PWMとALTの話です。
RaspberryPiでOLED液晶モジュールに出力する文字のフォントを変えたり日本語を出したり
この続き。
RaspberryPiを使ってHiLetgo 0.91" I2C シリアルLCDモジュールを表示させてみる - キノコの自省録
対象製品はこれ。今売り切れてますね……。
HiLetgo 0.91" IIC I2C シリアルOLED液晶ディスプレイモジュール 128x32 3.3V/5V AVR PIC Arduino UNO MEGA [並行輸入品]
これ使って、時計とか作ろうかなと思ってたりしてます。
フォントとフォントサイズの変更
このexplamples/stats.pyをベースに改変してみます。フォントの指定は、98行目のここで行っています。
# Load default font.
font = ImageFont.load_default()
このImageFontというのはPillowのモジュールです。ドキュメントはこちら。
ImageFont Module — Pillow (PIL Fork) 4.2.1 documentation
stats.pyにもコメントとして書かれてあるように、ImageFont.truetype()メソッドを使用すれば、フォントやフォントサイズの変更ができます。
# Alternatively load a TTF font. Make sure the .ttf font file is in the same directory as the python script! # Some other nice fonts to try: http://www.dafont.com/bitmap.php # font = ImageFont.truetype('Minecraftia.ttf', 8)
.ttfフォントがpythonスクリプトと同じディレクトリに入ってるか確認してね、って書いてありますが、そこに配置せず、今回はapt-getでIPAフォントをinstallする方法を試みます。
$ sudo apt-get install fonts-ipafont
インストールされたフォントは、/usr/share/fontsにあります。
$ ls /usr/share/fonts/truetype/
fonts-japanese-gothic.ttf fonts-japanese-mincho.ttf
このフォントを使って表示させてみます。フォントサイズは倍の16に変更します。
#font = ImageFont.load_default() font = ImageFont.truetype("fonts-japanese-gothic.ttf", 16) : draw.text((x, top), "hello,", font=font, fill=255) draw.text((x, top+16), "world!", font=font, fill=255)
システムがフォントのパスを解決しますので、ttfのファイル名だけでOKです。結果はこちら。
日本語の表示
IPAフォントは日本語フォントなので、そのまま日本語が表示できます。pythonスクリプトの文字コードをutf-8にして、ファイル先頭にcoding:utf-8を入れます。
# -*- coding:utf-8 -*- : draw.text((x, top), "こんにちは、", font=font, fill=255) draw.text((x, top+16), "世界!", font=font, fill=255)
これを実行すると、見事に化けます。draw.textはunicodeを要求しますので、正しくデコードできずに文字化けが発生します。そのため、下記のように修正します。
# -*- coding:utf-8 -*- : draw.text((x, top), u"こんにちは、", font=font, fill=255) draw.text((x, top+16), u"世界!", font=font, fill=255)
結果はこちら。
シャウトアラームver2.0.1を公開しました
シャウトアラーム 叫んで止める!目覚まし時計の最新版ver2.0.1をAppStoreに公開しました。
※この記事を書いている時点ではまだver1.0.0ですが、既に審査通過→公開準備状態となっているため、しばらくすれば切り替わると思います
更新内容としては、
- iOS11用ビルド
- アイコン、UI等のデザインを変更
- アラームのアンロックをもう少し楽に
- iPad対応
といったところです。この対応で、iOS11以降でも使えると思います。
アラームのアンロックについては、iOS7あたりからゲージ上昇が渋くなっていたので、緩くしました。解除条件を何段階かで切り替えられるようにした方がいいかなあと思いつつ、とりあえず今回はそのままになっています。
アラームの設定にカレンダーを使用していましたが、UIDatePickerに吸収させました。アイコンは、今風なすっきりとした感じに変更しました。元々こういう感じのアイコンにしたかったんですが、2013年当時はデザイン力が足りずに無理でした。4年も経つと、それなりにデザイン力も上がるもんですね。
iPad触っている時に気づいたんですが、通知の許可をリクエストするコードを入れるのを忘れてた模様。たぶん、今までは動かない端末がいくつかあったのではないかと思います。。。ちなみに、おやすみモード中はアラームが鳴らないので注意してください。
AngularJS1.xで$scopeを使わず親子間通信
今ホットなライブラリのAngularJSですが、あまりにも更新が速いためか、ちょっと前の情報でもすぐ陳腐化してしまっています。そのせいか、なかなか欲しい情報に辿り着けないという困った状況になっている気がします。
そんなAngularJSですが、1.5以上ではDirectiveの代わりにComponentが導入され、さらに$scopeを使わない書き方が推奨されています。Directiveの代わりにComponentを使用する点はあまり問題がないのですが、$scopeの便利さにべったり依存していると、いざ$scopeが禁止されてしまった場合、どう書いていいかわからない事態に陥ります。特に親子間通信($emit, $broadcast)で困ります。実際困りました。ということで、$scopeの甘い誘惑に耐えて親子通信をするお話がメイン。
解決課題
$scopeを使わず、
- 子から親のオブジェクトを更新
- 親から子へのオブジェクト更新通知
- 子から親へ引数付きメソッド呼び出し
を行います。
なお、対象はタイトルの通り、AngularJS(1.x系)です。Angular2系ではありません。このエントリは2017年9月27日に書かれています。
例題
例として、こんなイメージのページを作るとします。左に毒キノコの一覧が出ていて、キノコを選択すると、右ペインに説明や写真が表示されるといった具合です。
Componentおさらい
DirectiveはComponentに置き換えましょう。もうng-controllerとかも使っていませんよね?ng-controllerは早めに消しましょう。オブジェクトの意識が低くなり、再利用が非常にしづらくなります。
下記は、キノコの一覧をサーバから取得して表示するみたいなイメージのコードです。
<!-- kinoko.html --> <div> <!-- キノコ一覧(左ペイン)--> <kinoko-list></kinoko-list> </div> <div> <!-- キノコの説明文(右ペイン) --> <kinoko-explain></kinoko-explain> </div>
//kinokolist.js class KinokoListController { //インジェクションはコンストラクタで指定可能 constructor($http) { this.$http = $http; } $onInit() { this.kinokolist = []; //$httpでリストを取得するなど var self = this; this.$http({ method: 'get', url:'/kinokolist', }) .then(function(res) { //メンバ変数kinokolistにデータを入れる self.kinokolist = [...]; }, function(err) { //エラー処理 }); } onclick(kinoko) { console.log(kinoko); // 親へ通知したい! } } angular.module() .Component("kinokoList", { template: ['<div id="kinokolist" ng-repeat="kinoko in KinokoCtrl.kinokolist">', '<p ng-click="KinokoCtrl.onclick(kinoko)">{{kinoko}}</p>', '</div>'].join(""), controller: KinokoListController, controllerAs: 'KinokoCtrl' });
※ $httpですが、Angular1.6ではsuccess()が使えなくなっています。上記例のようにthen()に置き換えましょう。
親への通知
左ペインのキノコ一覧のキノコが選択されたとき、選択されたキノコを親に通知したいという場合を考えます。
敗北例
$emitを使用する敗北例です。constructor($http, $scope)にして$scopeをインジェクション、onclickで$emitを使用するとこんな感じになります。
//kinokolist.js constructor($http, $scope) { this.$scope = $scope; ..(略).. onclick(kinoko) { this.$scope.$emit('selectedkinoko', kinoko); }
これで親に通知されます。しかし$scopeを使用しています。敗北です。
勝利例
bindingsを使用して、親のオブジェクトを透過的に参照する方法を使います。イメージとしてはこんな感じです。
コード的には、以下のスキームに従います。
- 親のコントローラにオブジェクト実体を定義
- コンポーネントのhtmlタグに属性を追加し、valueとして親コントローラのオブジェクトを参照するよう定義
- 子のコンポーネント(KinokoList)のbindingsに、オブジェクト参照(つまり'=')で属性を追加
- onclickでその属性を書き換え
① 親のコントローラにオブジェクト実体を定義
親コンポーネントを司るControllerをKinokoParentControllerとして、次のような感じでオブジェクトを保持しておきます。
//kinokoparent.js class KinokoParentController { constructor() { } $onInit() { this.selected_kinoko = "" } } app.Component("kinokoParent", { templateUrl: "kinoko.html", controller: KinokoParentController, controllerAs: 'ParentCtrl' }
② コンポーネントのhtmlタグに属性を追加し、valueとして親コントローラのオブジェクトを参照するよう定義
kinoko.htmlのkinoko-listタグに属性を追加します。
<!-- kinoko.html --> <div> <!-- キノコ一覧(左ペイン)--> <kinoko-list selectkinoko="ParentCtrl.selected_kinoko"></kinoko-list> </div> <div> <!-- キノコの説明文(右ペイン) --> <kinoko-explain></kinoko-explain> </div>
③ 子のコンポーネント(KinokoList)のbindingsに、オブジェクト参照(つまり'=')で属性を追加
要するに、②で追加したselectkinokoのbindingsを定義するということです。
//kinokolist.js app.Component("kinokoList", { template: ['<div id="kinokolist" ng-repeat="kinoko in KinokoCtrl.kinokolist">, '<p ng-click="KinokoCtrl.onclick(kinoko)">{{kinoko}}</p>', '</div>].join(""), controller: KinokoListController, controllerAs: 'KinokoCtrl' bindings: { selectkinoko: '=' } });
④ onclickでその属性を書き換え
//kinokolist.js onclick(kinoko) { this.selectkinoko = kinoko; }
これで、親(KinokoParentController)のselected_kinokoが書き換わります。
子への通知
親のオブジェクトが変わったことを子が知らないと、記事の更新ができません。今回の例ですと、選択されたキノコに応じて、説明文や写真を変更する必要がありますので、なんとかして変更を知る必要があります。
敗北例
$broadcastを使用します。略。
勝利例
bindingsとLifeCycle Hookの$onChangesを使用します。
まず、親コンポーネントのhtmlであるkinoko.htmlに定義したkinokoExplainコンポーネントに対し、次のような形でselectkinoko属性を追加します。
<!-- kinoko.html --> <div> <!-- キノコ一覧(左ペイン)--> <kinoko-list selectkinoko="ParentCtrl.selected_kinoko"></kinoko-list> </div> <div> <!-- キノコの説明文(右ペイン) --> <!-- ここを変更 --> <kinoko-explain selectkinoko="{{ParentCtrl.selected_kinoko}}"></kinoko-explain> </div>
これは、kinokoExplainコンポーネントの属性selectkinokoに対して、文字列としてParentCtrl.selected_kinokoの値を設定しています。次に、kinokoExplainコンポーネントのbindingsを設定します。
//kinokoexplain.js app.Component('kinokoExplain', { templateUrl: kinokoexplain.html, controller: KinokoExplainController, controllerAs: 'KinokoExplainCtrl' bindings: { selectkinoko: '@' } });
最後に、コントローラクラスにLifeCycle Hookの$onChangesを定義します。
//kinokoexplain.js class KinokoExplainController { constructor($http) { this.$http = $http; } $onChanges(changedobj) { if (changedobj.selectkinoko) { var obj = changedobj.selectkinoko; if (!obj.isFirstChange()) { //selectkinokoが更新されたので、記事を取りに行くなど } } } }
これで、キノコリストコンポーネント→親コンポーネント→キノコ説明文コンポーネントという更新通知の系列が出来上がりました。
親がselected_kinokoの書き換わりを知るには
実は、selected_kinokoが書き換わった時、親コンポーネント自身は、値が書き換わったことがわかりません。親コンポーネント自身がキノコリストの変更を受けて何か処理をしたい場合、これでは困ってしまいます。解決策として、親のメソッドを呼び出してみます。
<!-- kinoko.hml --> <div> <!-- キノコ一覧(左ペイン)--> <kinoko-list selectkinoko="ParentCtrl.selected_kinoko" change="ParentCtrl.change();"></kinoko-list> </div> <div> <!-- キノコの説明文(右ペイン) --> <kinoko-explain selectkinoko="{{ParentCtrl.selected_kinoko}}"></kinoko-explain> </div>
//kinokolist.js onclick(kinoko) { this.selectkinoko = kinoko; this.change(); }
//kinokolist.js app.Component("kinokoList", { : controllerAs: 'KinokoCtrl' bindings: { selectkinoko: '=', change: '&' } });
change属性をメソッドでバインディングして、onclick()発生時にParentCtrlのchange()メソッドを呼び出しています。あとは、ParentControllerにchangeメソッドを定義してやるだけなんですが、実はこれ、問題があります。
//kinokoparent.js class KinokoParentController { : change() { console.log(this.selected_kinoko); } }
console.logで出力すると、書き換わる前のselected_kinokoが出力されます。これでは困ります。なので、引数を付けて呼び出しましょう。その引数ですが、JSONで指定する必要があります。
//kinokolist.js //kinokolist.js onclick(kinoko) { this.selectkinoko = kinoko; //this.change(kinoko)はダメ this.change({selectkinoko: kinoko}); }
呼び出されるchangeメソッドは、次のようになります。
//kinokoparent.js class KinokoParentController { : change(selectkinoko) { console.log(selectkinoko); } }
終わりに
Angular2系の親和性のために、$scopeを投げ捨てようという風潮に従ってみました。
AngularJSは一つの課題に対し、現状、複数のやり方が存在するので、これ以外にも方法はあると思います。どれがObsoleteで、どれがup-to-dateなのかわかりにくいため、参照する記事はくれぐれも慎重に選びましょう。この記事も、実際のところ正しい保証はありません。
ESP-WROOM-02のライターを作る
SwitchScienceに、ESP-WROOM-02の開発ボードが売っていますが、これはFTDIのUSBシリアルモジュールとLDOレギュレータが最初から乗っている上、書き込みモードとフラッシュブートモードが切り替えられ、形も非常におさまりが良い優れものですが、単価1944円とちょっと値が張ります。
ESPr Developer(ESP-WROOM-02開発ボード)
積んでいる部品を足し算するとまあそれくらいにはなるんですが、入手難易度を度外視すると、RaspberryPi Zero Wとコストが変わらなくなります。開発用にはいいんですけど、このモジュール積んだプロダクトを数個作ろうとなると、この値段が重しになります。
そもそもFTDIは書き込み時と、必要に応じてデバッグ時に用いる程度で、ランニング時には不要です。ということで、専用ライターを作ることにしました。対象は、秋月電子のDIP化モジュールです。これなら1個650円で済みます。
Wi−Fiモジュール ESP−WROOM−02 DIP化キット: 無線、高周波関連商品 秋月電子通商-電子部品・ネット通販
ということで、ESP-WROOM-02用のライターを作ってみました。
ちなみに、形が気にくわない場合、矩形のピッチ変換基板が単体で売っています。ただし、自分で半田付けの必要があります。
ESP-WROOM-02ピッチ変換用基板《フル版》(基板のみ) - スイッチサイエンス
部品
ざっと箇条書きで。
- ユニバーサル基板
- 抵抗 10K
- 細ピン用ピンソケット(WROOM-02用とUSBシリアルモジュール用)
- USBシリアルモジュール
- レギュレータ(3.3V降圧)
- タクトスイッチ
- アルミ電解コンデンサ 47μF
- 積層セラミックコンデンサ 0.1μF
- すずメッキ線、ワイヤー
ユニバーサル基板
ライターは2つ作ったのですが、1つ目はサンハヤトのICB-86という、ICによく使われるパターンのユニバーサル基板を使用しました。使用しましたが、今回の用途にはあんまり向いていない感じがしました。普通のユニバーサル基板の方が配線しやすいです。かなり無理やりワイヤー引っ張る羽目になりました。2つ目は普通のCタイプで作成しました。
USBシリアルモジュール
1つ目はFT234Xを積んだ小型モジュール(AE-FT234X)を使用しました。FTDI積んでいるにしては安価です。秋月電子で600円。ただし、100mAまでしか出力できないので、ESP-WROOM-02をフル稼働させると電流不足に陥ります。とはいえ、書き込みだとWiFi動かさないので、これで足りることは確認しました。足少ないので楽というのもあります。
2つ目はFT232RLを使用したモジュール使用しました。なんとAmazonで1つ326円です。奇跡の安さ。はんだ付けしなくてもRX,TX,DTR,CTSとVCC,GNDピンは最初から出ているという親切設計で、VCCは3.3Vか5Vかをジャンパピンで設定可能。ちなみにマイクロBではなく、ミニBです。3.3Vだと50mAしか流れず、動作しません。5Vを使用しましょう。
SODIAL(R)FT232RLモジュール5V 3.3V FTDI USB TTL変換アダプタレッド
※ちなみにWROOM-02は最大170mAとありますが、Flashブートの起動時、波形を確認すると300mAくらい流れているように見えます。500mAは欲しいところです。
※最近確認したら、500mAは供給してね、と書いてありました。ええ……
コンデンサとレギュレータ
コンデンサはパスコンです。降圧している分、コンデンサを差さないと電圧が安定しませんので、手抜きしないようにしましょう。秋月で三端子レギュレータを買うと、同梱していることが多いです。VIN-GND, VOUT-GNDに少なくとも1つずつアルミ電解コンデンサを配置しておきましょう。
レギュレータは三端子レギュレータでOK。5Vから3.3Vへ降圧します。別にDCDCでも構いません。
配線
1次資料であるESPrのデータシートを参考に。
http://espressif.com/sites/default/files/documentation/0c-esp-wroom-02_datasheet_en.pdf
- 3.3V ... 3.3V直結
- GND ... GND直結
- IO0 ... GNDへ
- EN ... 10K接続して3.3Vへ
- IO15 ... 10K接続してGNDへ
- RST ... プルアップ3.3V, タクトスイッチ経由でGNDへ接続
- TX ... RXへ接続
- RX ... TXへ接続
残りはオープンのままです。ライターなので、IO0は直GNDでOK。RSTについては、書き込み時にsync failedが出ることがたまにあるので、その場合、スイッチ押してリセットをかけます。なので、GNDへタクトスイッチ通して繋いでおきます。
完成品
写真は2作目のライター(FT232RL使用)です。L字のピンソケットがなかったので、USBシリアルモジュールが真上から刺さるという少々間抜けな形をしていますが。
ArduinoIDEからも、ちゃんと書き込み確認できました。
回路図
回路図も載せておきます。
RaspberryPiを使ってHiLetgo 0.91" I2C シリアルLCDモジュールを表示させてみる
送料無料の上にとても安価なHiLetgo品をAmazonでたまに買うのですが、HiLetgo製品の中に、420円で買えるLCDモジュールがあります。
HiLetgo 0.91" IIC I2C シリアルOLED液晶ディスプレイモジュール 128x32 3.3V/5V AVR PIC Arduino UNO MEGA [並行輸入品]
カスタマーコメントにも、RaspberryPiでも表示できた旨のコメントがありましたので、購入して表示させてみました。とっても簡単です。このエントリも覚書程度。
まずこれは何物?
ドライバIC SSD1306で制御されるLCDモジュールです。ということで、SSD1306を制御してやればよいということです。これはAdafruit製のようですね。データシートはこちら。
https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
かなり親切なチュートリアルもあります。
https://learn.adafruit.com/ssd1306-oled-displays-with-raspberry-pi-and-beaglebone-black?view=all
配線
I2Cです。電源電圧は3.3Vでも5Vでもいいようです。必要な電流値はMax15mAとなってますので、3.3Vからの給電で十分です(RaspberryPiの3.3Vピンは最大50mAなので)。あとは普通にI2C接続をすればOKです。raspi-configから、I2Cを有効にするのを忘れないようにしましょう。結線完了したら、
$ i2cdetect -y 1
で確認。アドレス3cが見えていたら成功です。
表示
チュートリアルのUsageの通りです。抜粋しておきます。なお、これらはRaspberryPi上で実行します。
$ sudo apt-get update
$ sudo apt-get install build-essential python-dev python-pip
$ sudo pip install RPi.GPIO
ビルドツールやらpipやらの基本セットですね。RPi.GPIOやpython-smbusは、最近のRaspbianには既にインストールされていると思いますが、とりあえずそのまま記載しておきました。これらをインストールした後、gitからライブラリをcloneしてビルド~インストールします。
$ sudo apt-get install git
$ git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
$ cd Adafruit_Python_SSD1306
$ sudo python setup.py install
上手くいけば、準備完了です。exampleディレクトリにサンプルコードが入っていますので、実行してみます。
$ cd example $ python stats.py
上手くいけばこんな感じにスタッツが表示されます。
$ python image.py
これを実行すると、猫の絵が表示されます。
とりあえずこんな感じで。非常に小型で、かなり綺麗です。ただ、Amazonのカスタマーレビューにもありますが、取り付けする場合にちょっと困った形をしています。うまくカバーを作ってやらないと、基盤が見えてしまいます。