Web業界の人がArduinoを触ってみた
ここ2週間くらい、Arduinoを触っていたので感想を少し書いておく。
こんな人がArduinoを触ってみた
まずは簡単にどんな人がArduinoを触ってみたのか?というところから。 組み込み系を触ったことはない。学生の頃は電気を専門にしてたけど、学生の頃からソフトウェアの方に興味が向いていて、電気の記憶なんてほとんどない。抵抗の記号くらいは知ってます! プログラミングについては、IT業界でWeb系の仕事をしているからある程度書ける…と言っても、C言語は初心者レベル。学生時代は電気を専門にしつつソフトウェアに興味があり、ソフトウェアを仕事にしたらハードウェアにも興味を持ち…なんなのだ。
Arduino Uno R3
今回使った機器はArduino Uno R3。 何を買ったらいいかわからない人は "Arduinoをはじめようキット" がおすすめ。 比較的ベーシックだと思われるArduino, ワイヤー(配線用の線), ブレッドボード(配線をお試しする便利板), 素子(LEDや抵抗)が揃っているので、とりあえず触って動かしてみることができる。 自分でもこれを買って試した。 Arduinoと接続するUSBケーブルは付属してないので、USBケーブルだけは別途購入が必要。 ケーブルはこれを買った。はまりポイント
それにしても、いろんなところではまったし、理解に時間がかかった。 悩んでいたのはだいたいこんな感じ。- 配列の初期化
- リソースを意識する
- シリアル出力
シリアル出力が思った以上に幅広く動作に影響を与えていたように感じた。
今回はメモ程度に書いておくけど、似たようなことで悩む人がいそうな気がするので、今後はソースを載せつつ少し細かく書いていきたい。
配列の初期化
これは単にC言語を忘れていただけなので、書くまでもないのだが…。int data[] = {};「よーし、配列確保したぞ!」なんて思っていたけど、動作が怪しい。 急に変なところから実行されたりする。
Serial.println("start"); // "start" と出力されずに、なぜかここに書いた処理が実行される。 Serial.println("end");上記コードのように、本来は "start" と表示後に処理されるはずなのに、なぜか処理が実行される。 しかも、そんなところに到達するロジックになっていないにもかかわらず。
原因は察しの通り、はじめに初期化したつもりの配列にデータを入れていたため、メモリ空間を壊していたんだと思う。
int data[512] = {};
こんな感じに領域を明示して解決。 この辺のCの感覚、すっかり忘れてただけに新鮮(笑)
リソース
PCを使っているとメモリは最低でも数GBは搭載しているから、細かなメモリについては意識してなかったけど、Arduinoでは意識する必要あり!! 例えば、さっきの配列サイズを1024にしたとする。int data[1024] = {};■ Arduino - ArduinoBoardUno http://arduino.cc/en/Main/ArduinoBoardUno
SRAM 2 KB (ATmega328)Arduinoで利用できるメモリは2KBということらしい。
■ Arduino - Int http://arduino.cc/en/Reference/Int Intは2バイト使うらしいので… "int data[1024]" って2KB使い切ってる!! 他に変数を使ったらどうなるんだ?また動きが怪しくなるんじゃない?
まさかバイト単位で気にするとは思わなかったよ…。
シリアル出力
ArduinoとMacをつなげて、Macにデバッグ用の文字列を出力していたんだけど、これが結構曲者。 Arduino関係のブログを見ているとよく目にするこんなコード。Serial.print(data[0]);このdata[0]の中身は数字の123だったとする。 printという関数は文字として出力するため "1", "2", "3" と3バイト送信される。 こういう時はwrite関数を使った方がよい。
Serial.write(data[0]);この場合は "123" という1バイトの送信で済む。
■ Nonlinear Control and Robotics: ArduinoとPC(Processing)のシリアル通信 http://masato-ishikawa.blogspot.jp/2011/07/arduionpcprocessing.html こちらの記事が詳しい。
この事実を知ったところで、別に3バイトと1バイトくらい…って思うんだけど、それは(普段Webアプリに関わっている)自分の中の常識であって、組み込み系の考え方ではないということに気付いた。 ArduinoとMacをつなぐシリアルの通信速度は300〜115200bpsから選択する。 サンプルコードでよく見かけたのは57600bpsだった。 57600bpsってことは57600bit/sec = 57.6bit/msec ≒ 7byte/msec…1ミリ秒に7バイトしか送れないじゃん!!ということを知る。 普段の会話では100MbpsとかGbEとか、そういう単位だからシリアル通信の速度感覚なんて全く持ってないわけで、驚き。 Arduinoのコードではmicro secを扱うコードを書いていたので、msecの時点で単位が違うんですけど…という状況。
この通信速度とシリアル出力にどんな関係があるのか?というと、シリアル通信を行なってMacにデバッグ用の出力をするとArduinoがmicro secで扱いたいところの処理が正常に行えず、期待通りに動作しないことがある(というか、全く期待通りに動作しなかったよ)。 具体的には、リモコンの赤外線を受信しようとした時にON/OFFの切り替えタイミングを見逃すようなこと(ON→OFF→ONのOFFを見逃し、ON状態であると誤認)。
リモコンの赤外線関連でもうひとつ悩んだこと。 なぜかデータを1/10にしている表記があった。
Serial.print(data / 10); Serial.print(",");
このデータ、利用時には10倍して使っている。なぜここで1/10にしているのだろう?という疑問がなかなか解決しなかったが、シリアル通信のことを知って気付いた。 例えば、145というデータだった場合、そのまま出力すると "145" であり、1/10をすることで "14" となる。 Serial.printを使っている時は2バイトと3バイトの差! さらにいうと "," の出力は同様の理由で ", " としてはいけない。 実際に通信速度が57600bpsではデータが期待通りにシリアル出力されず、115200bpsでは期待通りに出力された。
シリアルを利用する時は次の3つは重要そうだ。
- print関数よりもwrite関数を使う。
- 出力はなるべく減らす。
- 処理に影響のないようなところで出力を行う。
シリアル通信は送信後にすぐに処理が戻る非同期に改善されたようなことをどこかで目にしたが、出力が続く場合は通信処理が影響していそうな感じ。
なぜシリアル通信に注目して調べてみたかというと、シリアル出力しない時には期待動作だったことと、通信速度を上げることによって期待動作に変化したからそこに怪しさを感じたため。 怪しさというか、そこに何かあるのは明確だし、明らかに知らないことが埋まっていそうという感覚から。