ラズパイ版 IchigoJam でアナログ入力

この記事は、IchigoJam Advent Calendar 2017 8日目の記事です。

1. はじめに

LPC1114版 IchigoJam にあって ラズパイ版にないものはけっこうあります。

  • ディスプレイ関連機能 (VIDEO/SWITCH コマンド)
  • サウンド関連機能 (BEEP/PLAY/TEMPO/SOUND コマンド)
  • マシン語関連機能 (USR コマンド)
  • 省電力機能 (SLEEP コマンド)
  • アナログ入力 (ANA コマンド)

その中でもとくに重要なアナログ入力ですが、今後も本体への実装は厳しそうなので、
回避策をご紹介したいと思います。

今回は、I2C通信対応のADコンバータを外部接続し、
ラズパイ版の IchigoJam BASIC でアナログ入力を実現してみます。

とはいえ、アナログ入力が使えないという課題自体は、Raspbian などのOSで
Raspberry Pi を利用している人にも共通しているため、ネット上にたくさんの情報がありました。

特にこちらの記事がとてもわかりやすく参考になりました。
http://kinokotimes.com/2017/02/02/python-mcp3425-16bits/

2. 準備物

Raspberry Pi本体

今回はPi3で試しました。

ADコンバータ

MCP3425というADコンバータのDIP化モジュールを秋月電子さんで購入しました。
http://akizukidenshi.com/catalog/g/gK-08018/

アナログセンサ

弊社によく転がっているシャープ製 測距センサ GP2Y0A02YK を使いました。
http://akizukidenshi.com/catalog/g/gI-03158/

3. ADコンバータの仕様

I2Cアドレス

上位4bit がデバイスアドレスで 0b1101
下位3bit は発注時に指定がなければ 0b000
ということなので、普通に購入した場合、0b1101000 = 0x68 になります。

設定の書き込み

1バイト送信して設定を行います。
bit7: 1に固定
bit4: (1:設定後、連続して読み取り可能; 0: 読み取りのたびに設定必要;)
bit3,2: (0b00:12ビット値を取得; 0b01:14ビット値を取得; 0b10:16ビット値を取得;)

今回は連続読み取り、12bitモードを試しました。(0b10010000)

電圧の読み取り

2バイト受信して読み取ります。(コマンドは0x00)
0バイト目: 上位8bit(右詰め)
1バイト目: 下位8bit

12bit,14bit,16bit の各先頭のビットは符号ビットなので、
実際の数値は 11bit, 13bit, 15bit マスクして取得するようです。

4. 配線

下記の接続でうまくいきました。(あとで図を載せたいです!)

測距センサ

VDD — RPi(5V)
VSS — RPi(GND)
VO — ADC(VIN+)

ADコンバータ

VIN+ — センサー(VO)
VIN- — RPi(GND)
VSS — RPi(GND)
VDD — RPi(3.3V)
SCL — RPi(SCL)
SDA — RPi(SDA)

5. BASIC コード

下記のコードで、なんとなくそれらしい値が取得できました。

6. 今後の課題

  • 14bitモードや16bitモードも試してみましたが、うまくいきませんでした。
  • 下位3bitのI2Cアドレスを指定できれば、切り替えて複数アナログ入力もできるかもしれません。

Haskell(wai) による Webアプリケーション開発の実際(DB編)

この記事は、スタートトゥデイ工務店 Advent Calendar 14日目の記事です。

wai(Haskell製のWebアプリケーション規格)に準拠し、warp(wai準拠のHaskell製Webサーバ)上で動作するWebアプリケーションの開発について紹介する。基本性能を際立たせるため、便利なフレームワークはあえて使用しない。

以前に非常に単純なサンプルを紹介した記事の続編となる。
今回は、本格的なWebアプリケーションに欠かせない、データベースとの連携を試してみた。
バックエンドにはMySQLを使用した。

ソースコードは https://github.com/mitsuji/wai-example-mysql にある。

1. サンプルアプリケーションの概要

下記のようなデータを扱うJSON-APIを実装した。
1件のコーディネートに対して、ジャンルが1つ、タグが複数(0..N)紐づくものとする。

オブジェクト図は下記のようになるだろう。

また、MySQLのCREATE文は下記のようになる。

Haskellのデータ定義は下記のようになった。

2. 実装の方針

下記の方針で実装した。
* mysql-simpleを使用してMySQLのデータをHaskellのデータに変換する。
* aesonを使用してHaskellのデータをJSONに変換する。
* waiを使用してWebのインターフェースを提供する。

また、実用的なサンプルとするため、下記を盛りこんだ。
* トランザクション処理
* コネクションプール
* N対Nのリレーション

3. MySQLのデータをHaskellのデータに変換する。

mysql-simple では QueryResult という型クラスが用意されており、
任意のデータ型をこの型クラスのインスタンスにすることで、
MySQLに投げたクエリの結果をHaskellのデータに変換することができる。
下記のように convertResults を実装すればよい。

上記を定義すると、下記のようにクエリを投げることができる。
結果は、関数の型からも分かるように Genre’ のリストとして取り出すことができる。

4. HaskellのデータをJSONに変換する。

aeson では ToJSON という型クラスが用意されており、
任意のデータ型をこの型クラスのインスタンスにすることで、
JSONへの変換を定義することができる。
下記のように toJSON を実装すればよい。

ソースコードのプロジェクトにREPLで入ると、
下記のようにaesonの動作を試すことができる。

5. コネクションプール

コネクションプールはresouce-poolというライブラリを使用して実装した。
このライブラリを使うと、DBの接続に限らず任意のリソースのプールを簡単に実装することができるようだ。

アプリケーション起動時に createPool でプールを作っておく。
createPool にはプールにリソースが確保されるときの処理、プールからリソースが開放されるときの処理と、プールのチューニングに関するいくつかのパラメータを設定する。

リソースプールの変数を引き渡しておけば、アプリケーションの任意の箇所で
withResource を使用して、プール内のリソースを使うことができる。
関数の呼び出しが深くなる場合は、リソースプールの共有に
Readerモナドなどを使うとよいだろう。

6. トランザクション処理

トランザクション処理は下記のように withTransaction を使うだけで実現されるようだ。

7. ルーティング

URLのパスやHTTPメソッドの種類と、評価される関数をどのように紐づけるか。
wai が Text型のリストとしてURLのパスを提供してくれているので、
下記のようにパターンマッチを使ってルーティングを定義することができた。
フレームワークを使った場合ほどではないが、比較的簡潔にできた。

HTTPメソッドも合わせてパターンマッチすれば、RESTっぽいことも簡単にできる。

8. SQLインジェクション防止機能

mysql-simple では SQLインジェクションを防止するため、query や query_ には
文字列のリテラルでSQL文を渡さなければならないようにしてあるようだ。

文字列結合を使ってSQL文を組み立てることができないため、
条件を任意で指定するSELECT文が少し作りにくかったが、
下記のようにすることで対応できた。

9. まとめ

  • mysql-simple などのライブラリを使えば DBのデータとHaskellのデータのマッピングは簡単。
  • aeson を使えば Haskellのデータを JSON化するのは簡単。
  • resource-pool を使えばコネクションプールの導入は簡単。
  • mysql-simple のトランザクション処理は簡単確実。
  • Webアプリケーションのパス/メソッドルーティングは単純なパターンマッチでもある程度可能。
  • mysql-simple を使えば SQLインジェクションの危険性が低下。

10. 今後の課題

  • mysql-simple 以外のDBライブラリもいろいろ試してみたい。
  • 入力のバリデーションをもっとちゃんと実装してみたい。
  • ファイルのアップロードも試してみたい。
  • フロントエンドを実装して、実際に使ってみたい。

Haskell で 覆面算

Haskell で 覆面算 をやってみた。

DEBT + STAR = DEATH が与えられたとき、等式が成立するには
D = 1, E = 0, B = 8, T = 5, S = 9, A = 6, R = 7, H = 2
とならなければならない、という問題を解くものである。
先日とある脱出ゲームで出題されたが手計算ではまったく解けなかった。

ソースコードはこちら。
https://github.com/mitsuji/verbal-arithmetic

1. 特殊解

まずは、特殊ケースとして "DEBT + STAR = DEATH" だけを解くことを考える。
リストの要素を指定された数だけ使用した全ての順列に評価される関数
permutation を 作ると、下記のように総当りで解くことができる。

単語の先頭の数字はゼロにならないので、D != 0 と S != 0 を条件に追加している。

また、繰り上がりを考えると等式を見ただけで D = 1 が明らかなので、
これを条件に加えると、試行回数が激減して実行時間を短縮できる。

2. 一般化

ここからが本番だ。

脱出ゲームに勝つためには、わざわざ関数を書かなくても、
例えば下記のように記述したら解答を表示してくれるライブラリが必要だ。

また、このような関数をあらかじめコンパイルしておけば、

このように、コマンドにパラメータを渡すだけで解答を得ることができるだろう。

3. 式のデータ

まずは、入力となる数式(文字式?)を表現するためのデータ型を考えてみる。
今回は式(VExp)と等式(VEqu)を別の型として定義してみた。

このデータ型を使用すると、
式 DEBT + STAR = DEATH は下記のようなデータになる。
ポーランド記法とかいうやつだ。

関数を演算子として使えば、下記のように書くこともできる。

ここで、下記の関数を定義してみると、

下記のような記述が可能になる。だいぶ見やすくなってきた。

そして、VExp を IsString 型クラスのインスタンスにして、

ソースコードの先頭に {-# LANGUAGE OverloadedStrings #-} を書くと
下記のような記述が可能になる。
これは、コード上に文字列リテラルがあり、VExp型に推論されるときは
VExp の fromString を使って、VExp型のデータを作りなさいという意味である。
コード上で式を扱うには、まずはこれで充分だろう。

4. 一般解

特殊解の関数を参考にして作成した関数が下記である。
chars 関数で式内のユニークな文字を抽出し、総当りの場合の数を決めている。
また、firstChars 関数で式内の単語の先頭の文字を抽出し、!= 0 条件を追加している。

match 関数に渡された数字を元に実際に計算を行う関数は下記のようになった。
listToInt 関数は、数字のリストを10進数で数値に変換する関数である。

5. パーサー

ここまでで計算自体はできるようになったが、コマンドラインに式を渡せるようにするには、
式全体を文字列として受け取って、式のデータに変換するパーサーを書く必要がある。

こんな感じのパーサーになった。スペースの扱いで1日くらいハマった。

最後に VEqu を IsString のインスタンスにして
式全体を文字列として記述し、直接式のデータとして扱えるようにした。

6. まとめ・感想

  • 実行形式バイナリにコンパイルすればHaskellでもそこそこ速かった。
  • IsString 型クラスを使うと、いろんな型のデータを文字列リテラルから作れる。
  • パーサーを書くときはスペースの処理が重複しないように注意しないとハマる。
  • 式の表現は完璧なので、計算のアルゴリズムを改善してみたい。非総当りとか。
  • 引き続き、掛け算と引き算くらいまでは対応してみたい。

Haskell(wai) による Webアプリケーション開発の実際

2016年12月追記: この記事のDB(MySQL)編はこちら

wai(Haskell製のWebアプリケーション規格)に準拠し、warp(wai準拠のHaskell製Webサーバ)上で動作するWebアプリケーションの開発手順をまとめる。基本性能を際立たせるため、便利なフレームワークはあえて使用しない。

1. サンプルアプリケーションの概要

サーバの現在時刻をクライアント側で装飾して表示するWebアプリケーション。動作サンプルは下記を参照。

http://mitsuji.org:9997/

このアプリケーションは、下記のエンドポイントからなる。

  • /posixtime — GETでサーバの現在のUNIX時間(ミリ秒単位)を返す。
  • /main.html — 画面のHTML。クライアント側のエントリポイント。
  • /main.js — main.html で読み込まれる JavaScript。posixtime をGETして装飾してから画面に表示する。

2. 開発環境(stack) の準備

下記から stack をダウンロードしてインストールする。解凍してPATHを通すだけでよい。

http://docs.haskellstack.org/en/stable/README.html

下記のようにバージョンが表示できれば準備完了。

3. サンプルアプリケーションのコードの取得

コードは下記に置いてある。

https://github.com/mitsuji/wai-example

ソースコードを clone して、

ディレクトリに cd してから、

下記のコマンドを叩くとghc(Haskellのコンパイラ)などの環境が自動的に準備される。いろいろとダウンロードされるため、初めての時は少し時間がかかるかもしれない。

ディレクトリ内のファイルのうち、開発に直接関係があるのは下記の三つだけである。

  • app/Main.hs — Webアプリケーション本体のソースコード。
  • static/main.html — Webアプリケーション動作時にアクセス可能となる main.html そのもの。
  • static/main.js — Webアプリケーション動作時にアクセス可能となる main.js そのもの。

4. REPL(ghci) を使用した動作確認

下記のコマンドでHaskell の REPL である ghci に入ることが出来る。

REPLを使用すると、ソースコード内の関数を直接評価することが出来る。例えば、Main.hs内のこの関数は

このようにして直接評価してみることが出来る。

ソースコードを書き換えたときは、下記のようにしてリロードすれば変更が反映される。

また、下記のようにしてコマンドラインパラメータ付きでmain関数を評価することもできる。この場合はWebサーバが実際に起動する。

ここで、Main.hs 内の関数を見てみよう。

main は Haskell のプログラムのエントリポイントとなる関数であり、プロセス起動時に最初に評価される。ここでは、コマンドラインパラメータからWebサーバの待ち受けIPアドレスとポート番号を取得して、warp を起動している。HTTPリクエスト発生時に評価される関数には routerApp が指定されている。

routerApp ではリクエストされたURLを元に評価する関数を振り分けている。/posixtime がリクエストされれば dateAppが、それ以外がリクエストされれば staticApp が評価される。

dateApp は リクエスト毎にその時点でのサーバーの時刻をレスポンスとして返している。Int型で得られるUNIX時間をレスポンスとして返すため、Int => String => Text => ByteString(strict) => ByteString(lazy) の変換が行われている。

staticApp は main.html や main.js を レスポンスとして返すための、通常のWebファイルサーバである。ここでは、staticディレクトリ以下のファイルを処理対象としている。

通常のWebファイルサーバであるため、Webアプリケーション動作中はコンパイルや再起動を行わなくても main.html や main.js を編集すれば次回リクエスト時に変更が反映される。

5. runghc を使用した動作確認

REPLに入らずに動作確認したいときは、下記のようにしてmain関数を評価することもできる。Haskellでは、スクリプト的な使い方も可能となっているのである。

6. ghc を使用した実行形式バイナリのビルド

下記のコマンドで実行形式バイナリがビルド(コンパイル)され、

下記のコマンドでインストールされ

~/.local/bin/wai-example-exe が生成される。実行形式バイナリの動作確認は下記のようにして行うことができるだろう。

wai(Webアプリケーションライブラリ)やwarp(Webサーバ)を含むすべてのライブラリがwai-example-exeに静的にリンクされるため、wai-example-exe と staticディレクトリ を ビルド環境と同一アーキテクチャ、同一OSのマシンにコピーすればそのまま動作する。

この時、カレントディレクトリ配下の static ディレクトリ内のファイルが静的Webコンテンツとして参照されるが、ビルド時に settings を下記のように書き換えると、staticディレクトリ内のファイルを実行形式バイナリにすべて埋め込むことができる。静的Webコンテンツも含め、Webアプリケーションを一つのファイルにまとめることができるので、場合によってはとても便利である。

7. まとめ

  • Haskell の 環境構築は stack を使えば超簡単。
  • 一つのソースコードをまったく書き換えることなく REPL、スクリプト、実行形式バイナリの三態から利用可能。
  • WebアプリケーションとWebサーバが一体のため、実行形式バイナリをフロントエンドエンジニア や Webデザイナー に渡せばそのまま開発環境として使ってもらえる。
  • 実行形式バイナリでデプロイすれば、本番に必要な依存環境を最小化できるため、Vagrant や Docker などの環境構築ツールに頼らなくてよい。
  • 静的Webコンテンツを実行形式バイナリに埋め込めば、より安全確実なバージョン管理とデプロイが可能に。

8. おまけ

筆者の利用実績はないが、IDE とか欲しい人向けの情報。

  • Leksah — Haskell で作られた Haskell用 IDE。
  • Haskell for Mac — Mac向け。本格的な開発ができるかは不明だけど楽しそう。
  • haskell-idea-plugin — IntelliJ用 のプラグイン。

Eitherモナドの使い道 部分的な失敗を全体的な失敗とする計算(2)

※この記事のMaybe編はこちら

idと名前のデータベースと、順位とidのデータベースからなる、下記のようなフレームワークを考えてみよう。

2位の名前を取り出すには、idFromRank で 2位のidを取り出し、その id を nameFromId に渡して名前を得るものとする。
idFromRank も nameFromId も要求された順位やidのデータがなければ Left に評価されるものとする。

このフレームワークを使って、上位3位の名前を取得する関数を作成してみよう。
ポイントは、idFromRank も nameFromId も Left に評価されることがあるため、この関数もEither型に評価されるものとし、1位から3位までのidがすべて存在し、それらの名前がすべて存在する場合のみRightに評価され、それ以外のときはLeftに評価されるようにすることだ。

下記のように呼び出されるようになればよいだろう。

式を使ったプログラミングで素直に記述する場合、筆者の能力では下記の記述が限界である。
これはどう考えても地獄である。我々が関数プログラミングに求めていたものはこんなものではなかったはずだ。

Haskell には case 文があるので、効果的に使えば下記のように改善することができる。
しかし case 文の入れ子も本質的には地獄である。こんなことなら手続き型言語を使えばよいのではないか。

そこで Eitherモナド登場。
Either型のモナドとしての性質を使うと、上記とまったく同じ意味の関数を下記の内容だけで記述することができる。
モナドとしてのEither型には、失敗するかもしれない計算どうしを組み合わせるときは、一部でも失敗したらすべてが失敗したことにする
という性質が最初から組み込まれているためである。

全部のせておく。

Maybeモナドの使い道 部分的な失敗を全体的な失敗とする計算(1)

※この記事のEither編はこちら

idと名前のデータベースと、順位とidのデータベースからなる、下記のようなフレームワークを考えてみよう。

2位の名前を取り出すには、idFromRank で 2位のidを取り出し、その id を nameFromId に渡して名前を得るものとする。
idFromRank も nameFromId も要求された順位やidのデータがなければ Nothing に評価されるものとする。

このフレームワークを使って、上位3位の名前を取得する関数を作成してみよう。
ポイントは、idFromRank も nameFromId も Nothing に評価されることがあるため、この関数もMaybe型に評価されるものとし、1位から3位までのidがすべて存在し、それらの名前がすべて存在する場合のみJustに評価され、それ以外のときはNothingに評価されるようにすることだ。

下記のように呼び出されるようになればよいだろう。

式を使ったプログラミングで素直に記述する場合、筆者の能力では下記の記述が限界である。
これはどう考えても地獄である。我々が関数プログラミングに求めていたものはこんなものではなかったはずだ。

Haskell には case 文があるので、効果的に使えば下記のように改善することができる。
しかし case 文の入れ子も本質的には地獄である。こんなことなら手続き型言語を使えばよいのではないか。

そこで Maybeモナド登場。
Maybe型のモナドとしての性質を使うと、上記とまったく同じ意味の関数を下記の内容だけで記述することができる。
モナドとしてのMaybe型には、失敗するかもしれない計算どうしを組み合わせるときは、一部でも失敗したらすべてが失敗したことにする
という性質が最初から組み込まれているためである。

全部のせておく。

Readerモナドの使い道 仮想グローバル変数

現実的かどうかはさておき、税込み価格の商品と税抜き価格の商品が混在している店を考えてみよう。
会計の都合上、購入金額の合計を計算するときは、一旦すべての単価を税抜きに揃えてから集計し、まとめて税額を計算する必要があるものとする。

合計を計算するコードは下記のようになりそうだ。
このコードを眺めていると、toTaxIncluded と fromTaxIncluded の 引数 taxRate を消したくなってくるだろう。
ほぼ定数のようなものであり、合計の計算とは本質的に関係のない要素であるためだ。

そこでコードを下記のように改善してみよう。taxRate が引数から消えてすっきりした。
Haskell のコードをスクリプトとして使用している場合はこれで十分だろう。
ただ、一旦コンパイルされてしまうと、toTaxIncludedG と fromTaxIncludedG が使用する税率は固定されてしまう。

そこでReaderモナド登場。Readerモナドを使用すると、手続き型プログラミングでグローバル変数として保持したいような要素を、自然に保持することができる。
runReader関数 の 第2引数で指定した値を、モナド内の任意の関数内で ask 関数を使用して取り出すことができる。

全部のせておく。

Stateモナドの使い道 純粋関数内で状態を扱う

System.Random について調べるコードを考えてみよう。
0から9までのランダムな整数を繰り返し生成するとき、最初に5が現れるのが何回目か知りたいとする。

System.Random には randomRs という関数があり、型と範囲と乱数生成器を指定すると、ランダムな値の無限リストに評価される。
この関数とリストを操作する関数を使えば、下記のようにすっきり記述できる。

場合によってはこれで十分だろう。ただ、処理効率や可読性の面で、より手続き型に近い記述にしたい場面がありそうだ。

方法1. Stateモナドを使う

System.Random には randomR という関数があり、型と範囲と乱数生成器を指定すると、ランダムな値と新しい乱数生成器の組に評価される。randomRs の単発版である。

execState は初期状態と Stateモナドを使用する関数を指定すると、終了状態に評価される関数だ。
Stateモナドは、副作用を扱うという点ではIOモナドと似ているが、純粋関数内に閉じ込められる点が異なる。
Stateモナド内で現在の状態を得るにはgetを、状態を更新するにはputを使用する。

方法2. STモナドを使う

また、IOモナドから入出力に関する機能を取り除き、純粋関数内で評価できるようにした、STモナドも使用できる。
IOモナド内で IORef を使用する感覚で STモナド内で使用できる STRefという型があり、状態を保持することができる。
STRef は 複数作成しても良いので状態の管理が複雑なときは便利かもしれない。

全部のせておく。

Writerモナドの使い道 計算の経過を得る

1から10まで足し算するコードを考えてみよう。関数型言語では高階関数を使ってすっきり表現できる。
ただ、このコードには欠点がある。最後の結果求めるには十分だが、足し算の経過を見たいときどうしてよいか分からない。

手続き型言語で書かれたコードだったら、一行追加するだけで良いかもしれない。
では関数型言語ではどうするのか?

方法1. IOモナドを使う

(+) の IOモナド対応版(addIO)を作り、foldl を foldM に変えれば、addIO内でputStrLnが使えるようになる。ただ、これだと純粋な関数ではなくなり、IOを引きずっている箇所でしか利用できない。

方法2. trace を使う

Haskell には純粋な関数内での計算をデバッグ出力する関数が用意されている。trace は 第一引数を標準出力に表示し、第二引数と同じものに評価される関数だ。場合によってはこれで十分だろう。ただ、経過の値を他の計算でも利用したいとき、標準出力に表示されてしまったものを利用することはできない。

方法3. Writerモナド を使う

そこでWriterモナド登場。WriterモナドはIOモナドと違い純粋関数内で実行でき、コードに下記のように手を加えることで、結果にいたるまでの過程の値を最後にリストとして得ることが出来る。

全部のせておく。

継続モナドの使い道 早期リターン

引数をチェックして、問題があれば Left に包んだエラーメッセージに、問題がなければ Right に包んだ計算結果に評価される関数を考えてみよう。
純粋な関数でもロジックを表現することは可能だが、if文のネストが深くなればなるほど地獄である。手続き型言語であれば早期リターンで書きたいところだが、関数型言語ではどうするのか。

方法1. Eitherモナドを使う

※某所で指摘を受けたので追記。
Either モナド自体の性質を利用すれば、無尽蔵にネストが深くなっていく状況は避けることができる。場合によってはこれで十分だろう。ただ、より早期リターン風に表現したいときどうしたらよいか?

ついでにApplicativeスタイルでの記述も載せておく。

方法2. 継続モナドを使う

そこで継続モナド登場。純粋関数内で、早期リターンのような記述が可能となる。

全部のせておく。