Lepton FLIR を Raspberry Pi で（ちゃんと）動作させる

Yutaka Yasuda / 2018.12.31 (2019.9.28 追記)
（オリジナルの文書は[こちら](https://ylb.jp/Tech/LeptonOnRasPiZero/)にあります）

## 概要

- Lepton FLIR Camera Module を Raspberry Pi Zero で動作させる際の注意点
- 継続的に絵が出てこない（しばらくすると赤い四角表示になる）問題の原因と対処

## 作ったもの

[Lepton FLIR Thermal camera](https://lepton.flir.com) を天井に貼り付け、部屋の中の状態をサーモグラフィ画像としてドア前に提示するシステムを作りました。

![mainpart.jpg](https://qiita-image-store.s3.amazonaws.com/0/132859/14b42f03-d982-717b-a6c4-86c1c305855f.jpeg)
![sample1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132859/b1d181a4-7336-a38e-fa2d-1825986d152d.jpeg)

## Lepton FLIR Camera Module を Raspberry Pi Zero で動作させる際の注意点

基本的には以下の作例情報と、そこで参照されていたソフトウェアを利用しました。

- [FLIR Lepton Hookup Guide]
(https://learn.sparkfun.com/tutorials/flir-lepton-hookup-guide) (sparkfun, 何年も前のもの？)
- [FLiR Dev Kit を試す その２](http://pongsuke.hatenablog.com/entry/2017/12/06/173639) (上記 sparkfun のチュートリアルに沿った報告, 2017/12)
- [LeptonModule](https://github.com/groupgets/LeptonModule) (GitHub, 何年も前のもの)

この段階で注意すべきことが一点あります。結論から書くと、上記作例に沿うなら、カメラモジュールのCSピンはRaspberry Pi の pin #26 ではなく pin #24 に接続する必要があります。

上記二つの作例では FLIR module の CS ピンに接続すべき Raspberry Pi 側 SPI インタフェイスの CE ピンは、CE0 (#24)ではなくCE1 (#26)に接続するように書かれています。しかし実際にはそれでは動作せず（上記二例に書かれている赤い四角が左上に出る状態になる）、CE0 ピンに接続する必要がありました。

![wiring.png](https://qiita-image-store.s3.amazonaws.com/0/132859/191ec1e2-8002-2e6d-3b7a-9c1ad9b3b576.png)

私は SPI などについて今回初めて調べた程度の理解しかありませんが、ざっと調べたところでは、

1. Raspberry Pi にはSPIインタフェイスが2つあり
2. それぞれに CE0, CE1 ピンがある＝最大四つのSPIデバイスが接続でき
3. 上記LeptonModule Software の raspberry_video のコードは /dev/spi0,0 にアクセスするように書かれている
   （LeptonThread.cpp は SpiOpenPort(0); とあり、それは SPI.cpp では open(std::string("/dev/spidev0.0").c_str(), O_RDWR) を呼び出すことになる）
4. 作例にある pin #26 は SPI0 CE1 で、それは /dev/spidev0.1 に相当する
   (see; [Raspberry Pi Pinout](https://pinout.xyz/pinout/spi) )
5. しかし spidev0.0 に当たるはずの SPI0 CE0 は pin #24 のはず

です。そして実際に #24 に接続すると赤四角画像ではなく、きちんとサーモグラフィ画像が出ます。おそらく私の理解は間違ってないでしょう。

実際には現場で一緒に作業していたN君がCE1でなくCE0につないでみることを提案して動作し、後で上記のことを調べました。N君に感謝。なお画面左上に赤い四角が出るだけの状態は、映像をSPIインタフェイス（カメラ）から取得できていないことを示しています。上記ソフトウェアのmain.cppの前の方に、80x60範囲をredで埋めるコードがあります。

## 継続的に絵が出てこない問題の原因と対処

### Shortcut

結果だけに興味のある方は、以下の「主たるコード変更箇所」セクションまで飛ばすと良いかもしれません。

### 問題と原因

さて前節のようにピンさえ間違えなければ動作はするのですが、連続的に動作させてみると何時間、あるいはそれより短い時間で、カメラから映像が出てこない（raspberrypi_video ソフトウェアでは絵が最後に更新されたままになるので、止まっているように見える）状態に繰り返し陥りました。というか、丸一日以上動作したことがほとんどありません。なお私の環境だけかも知れませんが、Raspberry Pi Zero のCPU温度（$ vcgencmd measure_temp などとして表示可能）が高くなればなるほど、発生頻度が高まるように思われました。

面白いことに Raspberry Pi をソフトウェア・リブート（$ sudo reboot）してもこの状態から戻れません（raspberrypi_video ソフトウェアでは赤四角が表示される）。唯一の解決方法はハード・リセット、つまり電源を切って入れ直すことでした。

これは以下のことを想像させます。

1. Lepton FLIR Breakout board あるいは Camera Module が何らかの状態を保持しており、これがおかしくなる
2. システム再起動、つまりドライバの再ロードで解決しないので、
3. つまりドライバにあるはずのデバイス初期化コードはその状態をリセットしない
4. しかしBreakout board あるいは Camera Module は電源の再投入によって自分自身をハード・リセットする

となると、ソフトウェアからこのハード・リセットが呼び出す方法があるかどうかが問題です。

そこで FLIR の [Datasheet](https://www.flir.com/globalassets/imported-assets/document/lepton-engineering-datasheet---without-radiometry.pdf
)を見ると、p.12 の Pin Layout に RESET_L なるピンがあります。p.20 にリセットシーケンスが、p.24 辺りにやはり pin #23, #24 に PWR_DWN_L, RESET_L ピンなどがあることが示されています。しかしこれらのピンはブレイクアウトボードのピンには出ていません。つまり Breakout board 上の回路がこのあたりを制御してスタートアップシーケンスを実行していると思われます。

### 対処

SDKのコードやドキュメントをかなりたくさん読んで理解を整理して、ざっと検索し直したところ、これが自分だけでなく一般的な現象であることがわかりました。

[Lepton common issue, but no solution](https://groups.google.com/forum/#!topic/flir-lepton/wSp9mA_BI28) (2017/3)

> "However, after 5-10 minutes of continuous operation, the Lepton **stops transmitting data**." (b...@labyrinthlabs.ca)

> However, even with this approach - eventually the thing goes off the rails and **wont come back without a hard reset**. (JeffW)

ここでもやはり PWR_DWN_L, RESET_L ピンが Breakout board に出ていないことが指摘されています。読み進むと、最終的に LEP_RunOemReboot() 関数が再起動をしてくれて、これによって再びデータを出してくれるようになる、と報告されています。

ところが私が参照していた [FLIR Software Interface Description Document (IDD)](https://lepton.flir.com/wp-content/uploads/2015/06/flir-lepton-idd.pdf) は2014年のもので、PowerOn コマンドなどが含まれていません。それらのものは後に追加されたようです。[FLIR LEPTON Software Interface Description Document (IDD) Rev 303](https://www.flir.com/globalassets/imported-assets/document/flir-lepton-software-interface-description-document.pdf) には 2018年の情報もあり、こちらには PowerOn や Reboot 関連の記述があります。

また冒頭に引用した Lepton Software も古く、この OEM 関連のコードがライブラリに含まれていません。ざっと検索すると、 [PureThermal 1/2/Mini Reference Firmware](https://github.com/groupgets/purethermal1-firmware) にには LEPTON_OEM.c が含まれており、そこには [LEP_RunOemReboot()](https://github.com/groupgets/purethermal1-firmware/blob/master/Middlewares/lepton_sdk/Src/LEPTON_OEM.c#L131-L138) があります。周囲のファイル群を自分が使っていた古い Lepton Software と較べて問題が無さそうだったので、私はこの LEPTON_OEM.c を自分の環境にバックポートして build しました。

この Google Group で正解を出した Ben Kluwe も恐らく同じ対処をした  [raspberrypi_libs/leptonSDKEmb32PUB](https://github.com/BenKluwe/LeptonModule/tree/master/software/raspberrypi_libs) を用意しているようですから、他の人はこれを利用すると良いと思います。ただ、例えば raspberrypi_video ソフトウェアなどの中に、状態がおかしくなったときに LEP_RunOemReboot() を呼び出して問題を解決するようなコードは含まれていません。プログラマが自分自身で適切なところで Reboot させるコードを追加する必要があります。下の方に私が行った rapberrypi_video ソフトウェアに対するコード追加の例を示しておきます。

## 注意

なお LEP_RunOemReboot() は普通、return code は -26 を返します。LEPTON_ErrorCodes.h を見ると -26 は以下のように定義されています。

```LEPTON_ErrorCodes.h
LEP_ERROR_I2C_FAIL = -26,   /* I2C Bus Error - General Failure */
```

まあリブートして I2C の接続が切れてしまった（正しい終了手続きを踏めなかった）からでしょう。これで問題無くリブートしています。ただ、LEP_RunOemReboot() を呼び出した直後に何らかのアクセス、たとえば LEP_RunSysPing() を呼び出すなどすると、恐らくはレスポンスが無いためにそこでスタックし、永遠に戻ってきません。上の Google Groups でも同様の議論があり、Reboot させてからしばらく待つようにとあります。私の実験範囲では 0.5sec の待ち時間ではダメで、0.75sec では問題ありませんでした。

## 主たるコード変更箇所

GitHub Repository [yyasuda/LeptonModule](https://github.com/yyasuda/LeptonModule) に、そのように修正したraspberrypi_video アプリケーションが得られる状況を作っています。

つまり上記リポジトリからファイルを取得して、以下のようにすると対策した raspberrypi_video アプリケーションが得られます。

```
$ cd software/raspberypi_video
$ qmake && make
```

[Comparing changes](https://github.com/groupgets/LeptonModule/compare/master...yyasuda:master) を見ると、修正箇所が分かるでしょう。大きな修正点を以下に列挙しておきます。

- LEPTON_OEM.c, .h を追加
- Lepton_I2C.cpp/h に lepton_reboot() を追加
- LeptonThread.cpp に lepton_reboot() 呼び出し記述を追加

なおこの Repository に含まれているドキュメント（IDD）は 2015 年のもので、そこには OemReboot に関する記述がありません。

[FLIR LEPTON Software Interface Description Document (IDD) Rev 303](https://www.flir.com/globalassets/imported-assets/document/flir-lepton-software-interface-description-document.pdf) には 2018年の情報もあり、こちらには PowerOn や Reboot 関連の記述がありますので、こちらを参照することをお勧めします。

## 注意

この文書が参照している [groupgets/LeptonModule](https://github.com/groupgets/LeptonModule) Lepton Module は古いもので、恐らく[groupgets/purethermal1-firmware](groupgets/purethermal1-firmware) を利用する方が良いと思います。つまり sparkfun の作例 [FLIR Lepton Hookup Guide](https://learn.sparkfun.com/tutorials/flir-lepton-hookup-guide) は古いのですが、しかし多くの人は前者にしか含まれない raspberrypi_video アプリケーションに魅力を感じるでしょう。

その意味で [BenKluwe/LeptonModule](https://github.com/groupgets/LeptonModule/) も同じことをやろうとしているように見えます。

私自身はまだこれで本当に安定して FLIR camera module が長時間連続動作できることを確認しきれていませんが、ともあれ現時点での情報としてあげておきます。

## 追記

このドキュメントを書くために調べていたら、なんと 2018.8 に同じ問題を同じように対処している人がいたのを発見しました。[novacoast/Lepton-3-Module](https://github.com/novacoast/Lepton-3-Module/commit/b04573a3083e461aa16b35b9ee64102ed9b8501a#diff-903c398ff042c397d6546679372cb7d2) を見ると、おかしくなったらボタンをクリックして OEM Reboot を掛けるようにしています。うーん、気づかなかった。なんでもとりあえず検索するって大事ですね。（なんというオチ）

## 追記2

上のドキュメントは2018年の暮れに書いたので、その時点では「長時間連続動作できることを確認しきれていませんが」とする他ありませんでした。が、それ以来 2019年1月25日現在まで、何度かソフトウェア修正のためのリブートを行った以外は連続稼働させて、問題なく動作し続けている状態です。うまく問題を解決できていると思います。

## 追記3

2019年の9月に @higuchi-toshio-intec さんが Lepton 3 Module 対応の修正と機能追加をされています。参考になると思います。（近日中に取り込み予定）
https://qiita.com/higuchi-toshio-intec/items/da4f12e3e09426f45812

9/28 に @higuchi-toshio-intec さんのコードをマージしました。ありがとうございます！raspberrypi_video はいま撮影されている範囲での最高・最低温度を対象にヒートマップの色を割り付けるのですが、これを固定するためのオプションが追加されており、実用性が上がっています。
https://github.com/yyasuda/LeptonModule/pull/1