キノコの自省録

日々適当クリエイト

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しても構わないと思います。

Drogon Projects | Git

このページのトップに書いてある通り、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に置いてあります。

GitHub - konsultaner/word-clock-raspberry-pi-zero-neopixels: This is a project to construct and build a word clock using a raspberry pi zero and the adafruid neopixels.

README.mdに書いてある通り、gccでビルドすると、gpio_altのバイナリが生成できます。WiringPiについても書いてあるので、実はここを紹介すれば終わりとも言えなくもない。

gccを利用するので、動かない場合はsudo apt-get install build-essentialを実行しましょう。

何をしてるのかの解説

ちょっとした解説を書きました。PWMとALTの話です。

RaspberryPi ZeroでGPIOを使って音声出力をするお話(2) - キノコの自省録

RaspberryPiでOLED液晶モジュールに出力する文字のフォントを変えたり日本語を出したり

この続き。

RaspberryPiを使ってHiLetgo 0.91" I2C シリアルLCDモジュールを表示させてみる - キノコの自省録

対象製品はこれ。今売り切れてますね……。

HiLetgo 0.91" IIC I2C シリアルOLED液晶ディスプレイモジュール 128x32 3.3V/5V AVR PIC Arduino UNO MEGA [並行輸入品]

これ使って、時計とか作ろうかなと思ってたりしてます。

フォントとフォントサイズの変更

GitHub - adafruit/Adafruit_Python_SSD1306: Python library to use SSD1306-based 128x64 or 128x32 pixel OLED displays with a Raspberry Pi or Beaglebone Black.

この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です。結果はこちら。

f:id:kinokorori:20171001225045p:plain

日本語の表示

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)

結果はこちら。

f:id:kinokorori:20171001225129p:plain

シャウトアラームver2.0.1を公開しました

シャウトアラーム 叫んで止める!目覚まし時計の最新版ver2.0.1をAppStoreに公開しました。

※この記事を書いている時点ではまだver1.0.0ですが、既に審査通過→公開準備状態となっているため、しばらくすれば切り替わると思います

更新内容としては、

  • iOS11用ビルド
  • アイコン、UI等のデザインを変更
  • アラームのアンロックをもう少し楽に
  • iPad対応

といったところです。この対応で、iOS11以降でも使えると思います。

アラームのアンロックについては、iOS7あたりからゲージ上昇が渋くなっていたので、緩くしました。解除条件を何段階かで切り替えられるようにした方がいいかなあと思いつつ、とりあえず今回はそのままになっています。

アラームの設定にカレンダーを使用していましたが、UIDatePickerに吸収させました。アイコンは、今風なすっきりとした感じに変更しました。元々こういう感じのアイコンにしたかったんですが、2013年当時はデザイン力が足りずに無理でした。4年も経つと、それなりにデザイン力も上がるもんですね。

f:id:kinokorori:20170930213606p:plain

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日に書かれています。

例題

例として、こんなイメージのページを作るとします。左に毒キノコの一覧が出ていて、キノコを選択すると、右ペインに説明や写真が表示されるといった具合です。

f:id:kinokorori:20170904015407p:plain

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を使用して、親のオブジェクトを透過的に参照する方法を使います。イメージとしてはこんな感じです。

f:id:kinokorori:20170903212614p:plain

コード的には、以下のスキームに従います。

  1. 親のコントローラにオブジェクト実体を定義
  2. コンポーネントのhtmlタグに属性を追加し、valueとして親コントローラのオブジェクトを参照するよう定義
  3. 子のコンポーネント(KinokoList)のbindingsに、オブジェクト参照(つまり'=')で属性を追加
  4. 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ピッチ変換用基板《フル版》(基板のみ) - スイッチサイエンス

部品

ざっと箇条書きで。

ユニバーサル基板

ライターは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へタクトスイッチ通して繋いでおきます。

完成品

f:id:kinokorori:20170904231443j:plain:w400

写真は2作目のライター(FT232RL使用)です。L字のピンソケットがなかったので、USBシリアルモジュールが真上から刺さるという少々間抜けな形をしていますが。

ArduinoIDEからも、ちゃんと書き込み確認できました。

f:id:kinokorori:20170905011922p:plain

回路図

回路図も載せておきます。

f:id:kinokorori:20180303181753p:plain

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

$ sudo apt-get install python-imaging python-smbus

ビルドツールやら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

上手くいけばこんな感じにスタッツが表示されます。

f:id:kinokorori:20170820213739j:plain

$ python image.py

これを実行すると、猫の絵が表示されます。

f:id:kinokorori:20170820213806j:plain

とりあえずこんな感じで。非常に小型で、かなり綺麗です。ただ、Amazonのカスタマーレビューにもありますが、取り付けする場合にちょっと困った形をしています。うまくカバーを作ってやらないと、基盤が見えてしまいます。

RaspberryPi ZeroとESP-WROOM-02を繋いでみる

RaspberryPi ZeroにはWiFiモジュールがないので、単独ではネットワークに接続できません。解決方法として、ZeroをホストにしてUSBでWiFiドングルを繋ぐ、というのが一番早そう(試してはいませんが)ですが、ESP-WROOM-02なるWiFiモジュールが巷で評判だったので、繋いでみました。今回は、秋月のDIP化キットを使用しました。

akizukidenshi.com

ESP-WROOM-02って何?

正直、「WiFiモジュールならなんか繋がるんだろう」程度で、ろくに下調べもせず買いました……。まあ650円ですし。

WiFiモジュールなのは確かですが、バスで接続するわけではなく、モジュール単品で動作します。実はArduino IDEを使ってファームを書き換えることができます。Arduinoそのものではないですが、いわばWiFi積んだ疑似Arduinoです。SPIやI2Cなどのピンがなんで出てるんだろう?と不思議でしたが、そういうことです。

デフォルトのファームでは、これらのピンは使用せず、UARTを介してATコマンドで会話します。組み込み機器なんかではよくある、外部マイコンとメインボードが通信するというような感じですね。ということで、面倒くさいです。ラズパイならドングル挿した方が楽でしょう。

回路

ESP-WROOM-02の動作電圧は3.0V~3.6Vということで、3.3Vの電源ピンから供給できそうに見えますが、電流が平均でも80mAで、RaspberryPi3.3Vの最大出力50mAを超えてしまいます。そのため、5Vを使って、3.3Vへ3端子レギュレータでドロップしました。使用した3端子レギュレータはNJU7223F33です。

それ以外は、こちらのエントリを参考にして配線しました。

ESP-WROOM-02 の配線 - Qiita

実物写真はこちら。タイトルにZeroと書いておいて3B使ってますが、まあ同じということで……。あと、送信(Tx)と受信(Rx)はちゃんとクロスするように配線しましょう。たまにTx-Tx, Rx-Rxを結線する人がいますが、それだと動きません。

f:id:kinokorori:20170812223627j:plain

バイスの確認

RaspberryPiは、デフォルトでSerialが無効になっているので、有効化する必要があります。RaspberryPiのコンソールを開いて、

$ raspi-config

から、Serialを有効にします。

正しく機器が接続されているならば、/dev/ttyS0が出現すると思います。とりあえず、

$ ls /dev/tty*

でチェックしてみてください。挿抜で出たり消えたりするデバイスがあれば、それがWROOM-02です。

ターミナルソフトを利用した通信コマンドの送信

物理的な結線が確認できたら、次にシリアルを介してWROOM-02にコマンドを送信してみます。コマンドはいろいろあるのですが、とりあえずは、ATを送信して、OKが返ってきたら成功です。

多くの人はssh経由でRaspberryPiを操作していると思いますが、それだとssh用ターミナル - シリアルターミナル - WROOM-02というように、ターミナルを複数経由することになってしまいます。特に、WROOM-02のコマンドは、改行コードCR+LFを送信、WROOM-02からはLFで受信というようになっており、大抵わけのわからないことになります。

なので、今回はcutecomというUI付きアプリを使って、RaspbianのGUIから直接操作する方法を紹介します。cutecomは、普通にaptから取得できます。

$ sudo apt-get install cutecom

インストールしたら、

$ cutecom &

で起動します。画面はこんな感じです。

f:id:kinokorori:20170813202924p:plain

設定はこのスクリーンショットの通り、Deviceを/dev/ttyS0に、通信設定を115200baud,8bit,1にします。ウィンドウ下部に改行コードの設定がありますので、そこでCR+LFを指定します。Open Deviceボタンで接続した後、Inputから”AT”と入力して送信してみましょう。そこでOKと返ってきたら成功です。”AT+GMR”を送信すると、バージョンが確認できます。

VNC

少々脇道にそれて、VNCリモートデスクトップ)の設定について。HDMI繋いで、キーボードとマウス繋いで……とやっていると非常に面倒なので、直接RaspberryPiのデスクトップを触りたい場合、VNCを利用しましょう。最近のRaspbian Jessieには、デフォルトでVNCがインストールされていますので、利用は簡単です。

  1. raspi-configからVNCを有効にします
  2. $ vncserverを実行して、RaspberryPiのVNCサーバを起動します
  3. クライアント側から、vnc://xxx.xxx.xxx.xxx:5901に接続します

Macの場合、Finderのメニューから、Go->Connect to serverで、上記vnc://~を入力すると接続できます。

http通信のテスト

ほぼこちらのエントリ通りです。

ESP-WROOM-02: ATコマンドによるWifi動作確認 - Qiita

流れとしては、

  1. WiFiルータに接続
  2. TCPコネクション確立
  3. HTTP GET送信

という感じです。まず、WiFiルータへ接続するには、以下のコマンドを実行します。

AT+CWMODE_DEF=1
AT+CWLAP
AT+CWJAP=“SSID”,”passwd”

AT+CWMODE_DEFでWROOM-02のStationモード設定、AT+CWLAPでSSIDリスト取得、AT+CWJAPで接続しにいきます。AT+CWJAPが成功すると、以下のような応答が返ってきます。

AT+CWJAP=“foobar”,"hogehoge12345”

WIFI CONNECTED
WIFI GOT IP

OK

これでWiFiルータに接続できました。次にTCPコネクションをどこか適当なところに張ります。

AT+CIPSTART=“TCP”,”192.168.1.10”,80
AT+CIPMODE=1
AT+CIPSEND
>

AT+CIPSENDが受け付けられると、コマンドプロンプトよろしく、>が出現します。この状態になれば、TCPコネクションが確立されていますので、HTTP GETがリクエストできるようになります。

GET / HTTP/1.0

実際、googleにGETリクエストを投げて取得した応答がこちらです。

>HTTP/1.0 302 Found
Cache-Control: private
Content-Type: text/html; charset=UTF-8
Referrer-Policy: no-referrer
Location: http://www.google.co.jp/?gfe_rd=cr&ei=U46OWb62O5DR8gfkybeQBg
Content-Length: 261
Date: Sat, 12 Aug 2017 05:12:51 GMT

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.co.jp/?gfe_rd=cr&amp;ei=U46OWb62O5DR8gfkybeQBg">here</A>.
</BODY></HTML>

上記コマンドは一体何をやっているのか、他にどんなコマンドがあるかは、こちらのページが非常に見やすいです。

ATコマンド集 — ESP-WROOM-02-Note 1.0 documentation

まとめ

RaspberryPiのUARTを通してESP-WROOM-02と接続してみました。シリアル間通信なので、ブラウジングなどの複雑な用途には不向きですが、サーバにセンサデータをPOSTするなどには使用できると思います。RaspberryPiとWROOM-02を併用しなければならない事態というのはなかなか想像しにくいところですが、覚えておいて損はないかもしれません。