https://www.pololu.com/product/1213
pololuのモータードライバモジュールの動かし方を毎度毎度忘れてしまうのでメモ
投稿者: wpmaster
第十項 センサーの活用例
第十回 センサーの活用例
電子工作創作表現(2019/6/29)
スライドPDF
前期課題の参考
- コンセプト解説
- センサーの活用例
前期課題の参考になるような作例を、ハードウェアの使い方から紐解いていきます。
課題
- 1つの入力と、1つの出力を使う作品プラン
課題内容は前回もお伝えした通り「1つの入力と1つの出力を使った作品プランの提出」です。
センサーなど1つの入力装置と、1つの出力装置を使って何ができるかということを考えてみてください。
同じセンサーや同じモニタ・スピーカーでも、使い方の工夫で色々な表現ができると思います。凝った情報を取るというよりは、簡単なセンサーでより面白い表現ができないかということを是非考えてみてください。
入力と出力の関係性について
- 文脈を無視して何でもつなげてしまう
- 「起きること」から少しずれた「起こりそうで起こらないこと」
このような制限を設けたのは、インプットとアウトプットの関係性について考えてみて欲しいという意図があります。
センサーという形を通すと、あらゆる入力を好きな出力と紐づけられてしまいます。これは非常に面白い事でもあるのですが、一方で文脈を無視して接続できてしまうので、この関係性にどんな意味合いを感じ取る事ができるのかをよく考えてみてください。
「XXセンサー」の名前に限らない使い方
- 商品名等にある「XXセンサー」はあくまで一例
- 設置の仕方で多様な情報が取れる
今日は入力側、センサーについてどんなものがあって、どんな使い方ができるのかかいつまんで紹介していこうと思います。部品の紹介などを見ると「XXセンサー」という言い方がされているのですが、あくまで一般的な名称であって、使い方次第で色々な情報を拾ってくることが可能です。
その辺も色々想像を膨らませながら考えてみてください。
センサーの具体例
ボタン・スイッチ
- モーメンタリ/オルタネイトなど種類がある
- 例:ボタンを押した、物が当たった
ボタンやスイッチもセンサの一種と捉えられます。ボタンには押している間だけONになるモーメンタリタイプと、押すたびにONとOFFが切り替わるオルタネイトタイプがあります。
また「マイクロスイッチ」などは指で押すものではなく物が当たった時に反応することで、モーターの原点出しなどに使われます。そのような使い方をすれば、人だけでなく、物が当たった時を検知することも可能です。
ボリューム・スライド抵抗
- いわゆる「ツマミ」と呼ばれるもの
- どうコントロールするかも工夫しやすい
ボリュームやスライド抵抗はシンプルに値を変化させるのに便利なツールです。そのまま使っても楽しい部品ですが、例えば紐をつけて引っ張ってみるなど、インターフェースを変えてみてもそれだけで意味合いが変わってくるので、試す余地は色々とあると思います。
ピエゾ素子
- 入力としても扱える
- 例:振動が加わった、息を吹きかけた等
http://akizukidenshi.com/catalog/g/gP-04118/
音を鳴らすピエゾ素子は、逆に入力装置としても使うことができます。振動を拾ったり、息を吹きかけたりしても値が変わるので、マイク的に使うことができます。小さくて安いので、大量に設置したり狭いところに仕込むなど、普通のマイクではできないような扱い方をするのに適しています。
フォトセンサ
- レーザー等を組み合わせると、遮蔽物を検知できる
- 例:明るさの変化、対象物に遮られた、物が置かれた等
http://akizukidenshi.com/catalog/g/gI-02325/
明るさを取得できるフォトセンサですが、部屋の明るさを拾う以外にもLEDやレーザーと組み合わせて色々な応用がききます。レーザーをセンサーに当てて遮蔽センサとして使ったり、黒い線の上を走るライントレースロボットに使われたりなど部品代も安いので色々な使われ方があります。
圧力センサ
- 物を握ったり、押しつぶす力を取得できる
- 例:押し込まれた、物が置かれた、ぶつかった等
http://akizukidenshi.com/catalog/g/gP-06214/
圧力センサは薄い2枚の導体を押しつぶす事で、接触面積が増えて疑似的に圧力をセンシングできるという仕組みです。ある程度重い物を載せたりしても反応するので、押し込んだ強さの他にも簡易的に重さを測るというような使い方もできるでしょう。
曲げセンサ
- 曲がり具合で抵抗値が変化する
- 例:テンションの張り具合
https://www.switch-science.com/catalog/508/
曲げセンサは細い長い素子の曲がり具合で抵抗値が変わるので、最近ではVRグローブで指の曲げを検知する時なんかに使われます。もう少し張りのある部材と組み合わせて糸の張り具合を計測するというような事もできます。
距離センサ
- 対象物までの距離を測る(1次元)
- 例:近づいた、遠のいた、遮られた
http://akizukidenshi.com/catalog/g/gI-02551/
距離センサは1次元で対象までの距離を測ることができます。今ではKinectやRealSenseのように2次元で距離を測れるカメラが出てしまっているのであまり使われなくなってしまいましたが、比較的安価、小型、PCのリソースが不要など距離センサのメリットもまだまだあります。
加速度センサ
- 加速度(acceleration)が取れ、常に重力加速度がかかっている
- 例:傾き具合、振動、落下判定
http://akizukidenshi.com/catalog/g/gK-07243/
加速度とは物体に速度を与える要素です。物が動き出す時にはこの加速度が必ず生じていて、センサには常に重力加速度が生じています。加速度センサはこの重力加速度のXYZ方向を計測することで傾きを取ることができます。
また振動する時も細かな加速度を計測することができますし、自由落下する時には一瞬加速度が0になるので、物が落ちているかどうかを計測することも可能です。
ジャイロ(角速度)センサ
- 回転している速度を計測できる。加速度より正確に取れる
- 例:物の回転、弱い振動
http://akizukidenshi.com/catalog/g/gK-04912/
静止した物の傾きを取るのは加速度センサが得意ですが、どの方向に回転しているかという情報はこのジャイロセンサの方が正確に計測できます。各速度という形で計測できるので、毎フレームの角速度を回転方向に積算すれば物体の角度を知ることができます。
加速度とジャイロはセットで使われる事が多く、静止した状態で基準となる向きを加速度センサから取得した後、その基準位置からどの方向を向いたかという情報をジャイロセンサによって取得するという使い方が、ゲームコントローラーなどでよく使われます。
加速度とジャイロ・加えて磁気センサがセットになった6軸モジュール:
https://www.switch-science.com/catalog/2845/
その他応用がしにくそうなセンサ
以上が比較的メジャーで扱いやすく、応用が色々期待できるセンサでしたが、もう少しマイナーなセンサについても紹介していきます。
焦電型赤外線センサ
- 赤外線を検知して、人や動物の動きを判定する
http://akizukidenshi.com/catalog/g/gM-09002/
焦電センサは、人や動物から発せられる赤外線を検知できるセンサです。
可視光の影響を受けないので自動で点灯する照明などはこのセンサが良く使われています。
気温・湿度・気圧センサ
- 大気の状態を計測する。数値の動きは比較的地味
http://akizukidenshi.com/catalog/g/gK-09421/
大気の状態を計測するセンサを使えば気温・湿度・気圧なども取得することができます。台風等が通過すると気圧が大きく変化したりしますが、一日の間であまり大きく数字が変わるわけではなく、これで何かを変化させようとするとかなり地味な動きになってしまうので、あまり展示やパフォーマンスのような作品向きではないかもしれません。
心拍センサ
- 血液に光を当て、反射量で心拍を計測する
- 光センサによる計測はあくまで簡易的な物
https://www.switch-science.com/catalog/3208/
指に光を当てて血液が反射する量を計測することで、簡易的に心拍を取れるセンサというのもあります。
医療用の本気のセンサも勿論ありますが、光センサとArduinoで計測するのはあくまで簡易的な物なので、当て方や外乱でうまく計測できないことも多く、あまり細かな情報を取ろうとすると体験としては難しくなりがちのようです。
第九回 PCとの連携・シリアル通信応用
第九項 PCとの連携・シリアル通信の応用
電子工作創作表現(2019/6/21)
スライドPDF
先週の「シリアル通信」前回まで
- PCから数字を1つ送る
- Arduinoから来る数字を受信する
前回は、ArduinoのSerial.write関数を使って数字を送受信するというプログラムを解説しました。今回はその続きで、もう少し込み入った情報をやりとりする方法について解説していきます。
シンプルに数字を送るだけ
- 複数のデータはどう区別する?
- 255より大きい値を送るには?
シリアル通信は1バイトずつ数字を送ることができるので、0~255までの数字を送る事ができます。しかしそうなると、複数の値(アナログ入力の0番と1番や、傾きセンサのXYZ)を送ってもいつどのデータが来ているのか分かりませんし、値も255までしか送れないというのはかなり幅が狭いです。
基本的な文字をやり取りする「アスキーコード」
- 簡単な文字を128種類におさめた「ASCII CODE」
- 英数字記号などの簡単な「文字」が送れる
そこでもう少し複雑なやりとりをしようとすると、数字を文字として置き換えた「アスキーコード」の出番がやってきます。英数字記号とデータ用の特殊な文字で構成されていて、128種類に収められているので全てを1バイトの文字として使うことができます。「アスキーコード 一覧」などで検索すると、一覧が出てきます。
1バイトを1文字として扱う
- 例:111:110:107:97:110 で「onkan」
- 数字も文字として登録されていて「0」は48から始まる
- 文字としてのデータが「アスキー」数字としてのデータが「バイナリ」
1バイトの数字がそのまま文字として扱われるので、例えば上記数字の並びをアスキーコード表に照らし合わせてみると「onkan」という文字になります。そして数字も文字として登録されていて、文字の「0」が48でそこから1ずつ増える形で対応しています。
こうなってくると少しややこしくて、数字のデータを送る時に文字として送るのか数字として送るのか、ルールを決める時に混乱が生じてしまいがちです。そこで文字としてのデータのことを「アスキー」、数字としてのそのままのデータを「バイナリ」と呼ぶのが慣例になっています。
アスキーデータのやり取り
- 送るデータの自由度は上がる
- ただしプログラムは煩雑になる
アスキーデータでやり取りをすると、情報の自由度が上がる一方でプログラムは煩雑になります。
しかしパソコンとの連携をする上でとても重要な方式なので、じっくり説明していこうと思います。
シリアルモニタはアスキーベース
- ArduinoIDEの「シリアルモニタ」はアスキーベース
- 受信したデータを出力して、送信もできる
アスキーベースでのシリアル通信を簡単に試す時には、ArduinoIDEのシリアルモニタが便利です。Arduinoから送られてきたデータをそのままウィンドウに表示(ダンプ)してくれるのと、テキストを入力すればそれをそのままArduinoに送信してくれるので、作りはじめにシリアル通信を試したい時や、何かバグが発生した時の検証などに便利なので活用しましょう。
Arduinoでデータを受信する
- 文字を入れる変数「String」を使う
- 「<on 2>」「<off 2>」というコマンドでLEDをスイッチする例
Arduinoで受信したシリアル信号を文字として扱うためには「String」という変数の型を使って、受け取った文字データをストックしていきます。
どんな文字で指示するかというのは、実現したい事に応じて考える必要があります。ここではデジタルピンのLEDをオンオフするために「on ピン番号」というコマンドを不等号で囲う形にします。
「>」を一つの命令の終わりと認識することで、複数バイトのデータを場合分けすることができるようになります。Stringには文字を抜き出したり、アスキーの数字をバイナリの数字に変換するなど色々な機能が搭載されているので、都度必要な機能を参照しながら使うのが良いでしょう。
https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/
//====================以下Arduinoスケッチ===========================
// Arduinoのシリアルモニタから、デジタルピンを制御する
// でHIGH、 でLOWにする
String recv; //文字列を保持する変数String
void setup()
{
Serial.begin(9600);
//2~13までをすべて出力にする
for (int i = 2;i <= 13;i++)
{
pinMode(i, OUTPUT);
}
}
void loop()
{
while (Serial.available())
{
//charは1文字だけを保持する変数
char data = Serial.read();
//読めるデータは1バイトずつなので、recvに追加していく
recv += data;
//データの最後に「>」を入れるルール
if (data == '>')
{
//先頭3文字が「<on」かチェック
if (recv.substring(0, 3) == "<on")
{
//5文字目以降の文字を抜き出してtrimedに入れる
String trimed = recv.substring(4);
//trimedで抜き出した文字としての数字をintに変換
int pin = trimed.toInt();
//指示されたピン番号をHIGHにする
digitalWrite(pin, HIGH);
}
//先頭4文字が「/off」かチェック
if (recv.substring(0, 4) == "<off")
{
//6文字目以降の文字を抜き出してtrimedに入れる
String trimed = recv.substring(5);
//trimedで抜き出した文字としての数字をintに変換
int pin = trimed.toInt();
//指示されたピン番号をLOWにする
digitalWrite(pin, LOW);
}
//recvの中身を読み終えたので、リセットする
recv = "";
}
}
}
Arduinoでデータを送信する
- Serial.print() もしくは Serial.println()
- 数字は自動的に変換してくれる
今度は逆に、Arduinoから文字データを送信するプログラムを書いてみます。Arduinoから1バイトのデータを送る時はSerial.write()という命令を使っていましたが、文字を送る際にはprint()もしくはprintln()という関数を使います。
3つのアナログ入力した値を出力しますが、この辺りは加速度センサを使った時にも使ったコードに近いですね。シリアル周りを重点的に解説していきます。
//====================以下Arduinoスケッチ===========================
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print(analogRead(0));//アナログで読み取った値をシリアルでプリント
Serial.print("\t");//3つの値をタブ文字で分割する
Serial.print(analogRead(1));
Serial.print("\t");
Serial.print(analogRead(2));
Serial.print("\n");
//最後に改行文字'\n'を入れる
//最後はSerial.println(analogRead(2));としてもOK。
//printlnとすると最後の\nは勝手に追加してくれます
}
表記できない文字の表記
- 「\(バックスラッシュ)」の後にアルファベットで、タブや改行などを表現
- 「エスケープ文字」と言って特殊な文字を表記する時に使う
Serial.print("message")という感じで、文字を送る場合にはダブルクォーテーションで囲みます。ここではシリアルプロッタのフォーマットにならって3つの値をタブ文字で区切りました。0~1023までの値がプリントされています。
タブや改行のようにコード上では書けない記号を表記する場合、バックスラッシュを後ろに付けてエスケープ文字という形で表記する場合があります。
max/mspで受け取る
maxではbangを送ると受信したデータが流し込まれるので、prependオブジェクトでスタックした数字のデータを、改行のタイミング('\n'がバイナリの10)でitoaオブジェクトに流し込むことで文字のデータとして拾い上げています。
touchdesignerで受け取る
touchDesignerでは前回同様SerialDATを使い、Row/Callback Formatをone per lineに設定すると改行ごとにデータを区切ってくれます。
これを更に別々の数値として扱うにはsplitメソッドを使い、配列として分割した後にConstant ChOPなど使いやすいオブジェクトに入れるなどをします。
Processingで受け取る
- Arduinoに近い書き方
Processingで受信する処理は、Arduinoがシリアルを受信する処理とかなり近く、Stringを連結していって改行のタイミングでブロックを処理してあげるという処理を施します。
import processing.serial.*;
int data[] = new int[3];
String recv;
Serial serial;
void setup()
{
size(1023, 300);
serial = new Serial(this, "COM3", 9600);
}
void draw()
{
background(0);
fill(255);
rect(0, 0, data[0], 50);
rect(0, 100, data[1], 50);
rect(0, 200, data[2], 50);
}
void serialEvent(Serial s)
{
char dat = char(serial.read());
if (dat != '\n') recv += dat;
if (dat == '\n')
{
String[] list = split(recv, '\t');
data[0] = int(list[0]);
data[1] = int(list[1]);
data[2] = int(list[2]);
recv = "";
}
}
19年度前期課題
前期課題
【締め切り】
7/18必着とします
19日のお昼までと言ってましたが訂正します! 19日の講義で資料を参照しながらディスカッションしていきたいと思うので、7/18日までに下記Googleフォームから提出してください。こちらからの提出をもって単位認定とします。
提出フォーム(準備中)
【課題:デバイスを使った作品プランの提出】
Arduinoを使ったセンサーやアクチュエータを用いた作品のプランを考えてみてください。後期では実際に制作してもらいたいので、規模や予算であまり現実的ではないものは避けてください。
【課題の条件】
テーマ「1in - 1out」
「何か一つの入力と、何か一つの出力を使う」 というテーマを設けます。空間で起きている現象や人の動作、操作をセンサーとして、それに反応して動作する装置という図式で考えてみてください。
参考:Arduinoの基本操作とセンサーの基礎 : アクチュエータの基礎
考えてみて欲しい点
2つの入力と出力が持つ関係性についての記述を、プランシートの中に含めてください。デジタルの世界では入出力がすべて数値に置き換わるので、全く文脈の無い接続が可能になります。文脈の無いインタラクティビティは、ただ「気持ち良い体験」になってしまうので、入力と出力の間にどういう意味が感じられるか、どういう意味が持たせられるかという事を是非考えてみてください。
PCの使用はOK
Arduinoだけで完結する事が必須ではありません。音や映像の入出力にPCを使うのはOKですが、どこかに必然性のある形でハードウェアが含まれるようにしてください。
作りたい作品があるが、テーマに則さない場合
Arduinoなどデバイスを使っていれば、既に作りたい作品プラン等がある場合その作品/研究プランの提出でもOKとします。作品であればコンセプトを、研究のための実験装置等であれば研究内容をプランシートに含めてください。
参考例
【提出形式】
記載して欲しい内容
学籍番号・名前
ドキュメント内でも、ファイル名でも構わないのでわかる場所に記載してください。
作品概要
- 何をセンシングするか
- どのようにアクチュエーションするか
- 作品の大きさなど雰囲気の分かるもの(スケッチや簡単な3Dイメージの画像があるとGOOD)
作品コンセプト・テーマについて
- 自分的に面白いと感じる部分について
- 概要で書いたセンシングとアクチュエーションによって、どんな意味合いやストーリーが生まれるか(上の「考えてみて欲しい点」はこのあたり)
データフォーマット
Web上で共有できる形式なら基本的に自由とします。Pages・Keynoteなどは独自フォーマットではなく、PDF形式などに変換してください。
- Google ドキュメント
- Google スライド
- PDF形式
- その他画像形式(PNG/JPG)
保護中: 作品メモシート
第八項 PCとの連携・シリアル通信基礎
第八項 PCとの連携・シリアル通信基礎
電子工作創作表現(2019/6/14)
スライドPDF
パソコン<->Arduinoの通信
- 色々なソフトと連携する
- Processing, oF, Max/MSP, TouchDesigner...
Arduinoとパソコンの間で通信ができると、色々なソフトウェアと連携をとることができます。
今回はいくつかの通信手段を使って、パソコンと連携する方法を解説していきます。
USBシリアル通信
- 1バイトずつデータを送る、シンプルな方法
- 親戚にRS-232CやRS-485がいる
今までArduinoでデータをやり取りしていたUSBシリアル通信は、1対1の双方向通信をするための、シンプルでポピュラーな方法です。USBでシリアル通信をするのでUSBシリアルと言って、親戚のようなものにRS-232Cや、DMXのベースとなっているRS-485という規格のものがありますが、ここでやりとりするデータも基本的には同じ内容です。
プログラム上においてで複雑なルールは無く、1バイトずつデータをやりとりします。
「バイト」とは
- 2進数の0と1を8個並べたものが「1バイト」
- 0~255までの要素を表現できる
- 16進数では0x00~0xFFのように2桁で表現できる
バイトとはコンピュータ上で扱う数字の単位で、0と1だけで数を表す「2進数」の数字8桁を1バイトとしています。
この1バイトで表せる数字は10進数の0~255までです。Arduinoでは2進数を0b00001111のように0bを付けて表現することもできます。
ちなみに16進数も0x5Aのように0xを頭に付けて記述できますが、16進数が使われるのは1バイトを表記するのに2桁で収まって見やすいという理由があります。
2/8/10/16進数の相互変換
https://hogehoge.tk/tool/number.html
バイトの列を送る
- 0-255までの数字を1個ずつ送るのがシリアル通信
- データの意味や区切りなど、自分でルール(仕様)を決めてやり取りする
この1まとまりの数字を1個ずつ送れるというのが、シリアル通信の基本的な機能です。
1バイトずつデータを送るので、この255までの数字にどういう意味を持たせるのかを決めながらやり取りしていく必要があります。
PCとの連携・基本編
- Arduino側と、PCソフト側のプログラムでルール(仕様)を決めながら書く
- max/msp oF Processing touchdesigner
シリアル通信では決まったデータのルールが特に無いため、やり取りの決め事をArduinoとソフトウェア側で決めながら実装していく必要があります。
基本的なArduinoでのシリアル通信の記述方法と、PCとの連携についていくつか具体的な方法を紹介していきます。
Arduinoシリアル送受信(基礎編)
- 指定したピン番号のアナログ値を返すサンプル
まずは、とてもシンプルなArduinoのシリアル通信サンプルを用意します。これはPCから0~5までの数字を受け取って、その番号のアナログピンの値を返すというサンプルです。
アナログの値は0~1023まで取ることができますが、返す値は1バイトだけなので、0~255までしか送れません。大きい数字を送る方法は色々あるのですが、ここはひとまずアナログの値を255までに圧縮します。
void setup() {
Serial.begin(9600);
}
void loop() {
while (Serial.available())//データが来ているか確認
{
//1バイト分のデータを変数に入れる
byte data = Serial.read();
//アナログピンの番号の入力値を返す
if (data < 6)
Serial.write(analogRead(data) / 4); //1バイトでしか送れないので、4で割る
}
}
max/mspとの連携
max/mspでシリアル通信をするときは、serialパッチを使います。1つ目の引数にポート名、2つ目には通信速度を記載します。ポート名はprintを送信すると右のアウトレットから出力されます。
数字をメッセージで送るとその数字が1バイトだけ送信されます。1バイトなので、255を超える数字を送ろうとするとオーバーフローが起きて、256で割った余りの数字になってしまいます。
ここではアナログ5番のデータが欲しいので5を一定間隔で送りました。
Arduinoからは返事が返ってきますが、返事はserialオブジェクトの中に格納されたままになっていて、bangを送る事でアウトレットから出力されます。
touchdesignerとの連携
- serialDATを使う
- TDはアスキーコードとして使うのがデフォルト(後述)
続いてTouchdesignerでの連携です。touchdesignerではSerialDATというオペレータを使って送受信をします。ポート名と通信速度を同じように設定して、データを出力するタイミングのRow/Callback FormatをOne par byte、Byte ColumnをONに設定します。byte ColumnをONにしたことで、1バイト分のデータが数字で表示されるようになります。使うデータは最新のものだけなので、Maximum Linesは1にしておきます。
execute DATからデータ送信
毎フレームアナログ番号の5を送りたいので、Execute DATのFrame StartをONにし、onFrameStart内でserial1オペレータのsendByteメソッドを呼び出します。これで毎フレーム5が送られるので、アナログ入力値がserialDATに送られます。
CHOPで受け取る
DATの値をConstant CHOPに入れ込みます。直接DATから数字をもらってきても良いですが、これで使いやすくなりました。
グラフィックのパラメータにしてみる
これをNoise TOPのZパラメータに入れてみます。
データの範囲が0~255なので、これをexpressionで調整してnullに入れておいた値をNoise TOPのtzに入れます。アナログの値をぐりぐり動かすとNoiseの模様がリニアに変化していきます。
processingとの連携
- processing.serialライブラリを使用
- serialEventメソッドが呼び出される
続いてprocessingの場合です。processing.serialからライブラリをインポートして、Serialクラスを使います。writeメソッドで1バイト分のデータを送る事ができます。Processingではデータを受信するとserialEventが呼び出されるので、そこでデータを受信することができます。
import processing.serial.*;
int data = 0;
Serial serial;
void setup()
{
serial = new Serial(this, "COM3", 9600);
}
void draw()
{
background(0);
serial.write(5);
rect(width / 2, height / 2, 10 + data, 10 + data);
}
void serialEvent(Serial s)
{
data = serial.read();
}
openFrameworksとの連携
- C++なので、Arduino側と読み書きの構造は近い
- 今日紹介した中で処理速度は最速
openFrameworksでは、ofSerialというクラスが用意されています。
C++ということもあって、書き方はArduinoとかなり近くなります。
//====header====
ofSerial serial;
int analog = 0;
//====header====
ofApp::setup()
{
serial.listDevices();
serial.setup("COM3", 9600);
}
ofApp::update()
{
seiral.writeByte(5);
while (serial.available())
{
analog = serial.readByte();
}
}
ofApp::draw()
{
ofDrawRectangle(ofGetWidth() / 2.0, ofGetHeight() / 2.0, 10 + analog, 10 + analog);
}
USBシリアルでの「決めごと」
- 通信速度(9600/38400/115200bpsなど)
- データ長・パリティビット・ストップビット
- 8ビット長・パリティ無し・1ストップビット(SERIAL_8N1)がArduinoデフォルト
最後に、USBシリアルではいくつかルールがあってこれをArduino側とソフトウェア側でそろえなくてはいけません。通信速度はノイズ等でデータの取りこぼしがあるので早すぎない程度に高い数字を使いましょう。
データ長・パリティ・ストップビットについては通信のタイミングを決めるルールですが、基本的には8ビット・パリティ無し・1ストップビットが今スタンダードなようなので、特に意識する事はないと思います。メーカー製の装置等と通信する時に違った設定があることがあるので、なぜかちゃんと通信できないという時に思い出す程度で大丈夫です。各項目の意味については検索するとたくさん情報が出てきますが、これも自分で組み込みデバイスを設計しない限りあまり使うことはないかなと思います。
補足:通信速度
- 9600だと60fpsの世界では1フレーム160バイト
- 115200で1フレーム1920バイトなので無難?
自分の感覚としてはUSBシリアルなら9600未満は遅いかなという感じで、115200当たりが無難に思います。
映像的に考えると、9600bpsで1フレームに送れるデータ量は9600÷60=160バイトなのでちょっと遅いかも、115200bpsなら1フレーム1920バイトなので十分かなという感じです。
補足の補足:クロックとの組み合わせ
- Arduinoのクロック数(UNOでは16MHz)によってエラーレートが変わる
- 高いほどエラーが起きやすいという事でもない
その後、ツイッターで情報をいただいたのですが、実はクロック数と通信速度でデータを取りこぼしやすい組み合わせがあるということでした。
https://cache.amobbs.com/bbs_upload782111/files_22/ourdev_508497.html
ここによると16MHzでエラー率が低く、速度が速いのは76800bpsだそうです。115200だと3.7でやや高く、57600は少し遅いけど位相がずれていくのでエラー率が高くなっています。
https://ja-support.renesas.com/knowledgeBase/17794551
理論上は4.375%までが許容範囲ということなので、115200はギリギリですね…16MHzのArduinoでは76800くらいが丁度良さそうです。
ここまでがシリアル通信基本形
- 生のバイナリ(数字)データでシンプルにやりとり
- 複数種類のデータや大きい数字など複雑にしづらい
以上が、各ソフトウェアで扱える簡単なシリアル通信の方法です。単純なデータのやりとりはできますが、これではちょっと機能に物足りなさを感じます。
アナログのデータも欲しいけどデジタルの入出力もしたい、さらに複雑な処理をArduinoに指示したいとなるともう少し発展的な実装をする必要があるのですが、それはまた次回!
前期課題
- デバイスを使った作品プランを提出
- 「1種類のインプットと、それに対応した1種類のアウトプットを持つもの」
- その組み合わせがどういう意味を持つか、どういう意味を持たせられるか考える
6月も半分を超えたので、ここで前期課題を出そうと思います。デバイスを使った作品プランを考えてみてください。後期は実際に作品を作ってもらいたいと考えているので、ここで少し作品についてや実現可能性についてディスカッションをできればと思っています。聴講生の提出も歓迎です。
どうしても作りたいものがある場合は、Arduinoかその他デバイスを使っていれば自由に考えてもらっても構いませんが、一応トライしてみてほしいテーマを「1種類のインプットと、それによって変化する1種類のアウトプットを持つもの」とします。
何かセンサーや入力を1種類と、それによって制御される出力を1種類決めて、その現象がどういう意味を持つか、どんな意味を持たせることができるか考えてみてください。
7/19に発表してもらいたいので、7/19のお昼12時を提出期限とします。また課題用のページを作りますが、Googleのドキュメントかスライド、PDFなどWebで共有できる形式をいくつか指定します。
第七項 通信とケーブル・はんだ付け
第七項 通信とケーブル・はんだづけ
電子工作創作表現(2019/6/7)
スライドPDF
今日使うもの
- ジャンパワイヤ4本
- MPU6050モジュール
- はんだごて
- はんだマット
今日の予定
- 6軸センサを使ったセンシング&はんだづけ体験
- 通信とケーブルについて
はんだづけ
- 基板の銅等を使った接点(ランド)に電子部品を固定する
- はんだを溶かしてくっつける。溶接ではなく「溶着」
今日はMPU6050という、傾きを検知するセンサーを使ってデモしようと思うのですが、せっかくなので部品のはんだ付けを皆さんに体験してもらおうと思います。
はんだづけとは、基板に電子部品を固定する方法の一つで、部品の金属接点と基板の接点の間にはんだという合金を溶かし入れて接着する方法です。溶接の一種とよく言われますが、溶接ではなく溶着ですので、溶接ほどの強度はありません。
はんだごてについて
- 安くて温度が低いものは難しい
- 自分用を買う時は「温度調節機能付き」がおススメ
はんだ付けは、熱したはんだごてではんだを溶かしながらつけていきます。溶かすはんだは種類によって融点が色々あり、あまり温度が低いとはんだ不良を起こすので自分用にこてが欲しくなった場合は温度調節機能付きのはんだごてがおススメです。白光やgootというメーカーのはんだごてがメジャーなので良いでしょう。
はんだの付け方
- 部品二つを温める(3~4秒)
- はんだを押し当てて溶かし、流し込む
- はんだごては1秒ほど、一息おいてからはなす
はんだの主な手順は上記のようなイメージです。部品の大きさや材質によって当然条件も変わってきますので、上手くなるためには数をこなしてみるのが一番だと思います。
良いはんだ、悪いはんだ
- つやつやした富士山型が良いはんだ
- はんだを盛りすぎる、ランドに熱が伝わってないと「いもはんだ」になる
はんだ付けがきれいにできると、三角錐の形になります。接点側に伝える熱が不十分だと表面張力で丸っこい形になり、取れやすかったり電気が通じなかったりするので、そうなってしまった所は数秒はんだを当てるなどして直してあげましょう。
ただし、あまり長く基板や部品にこてを当てていると熱が伝わりすぎてランドが剥がれたり部品が熱で壊れたりしてしまうので、いい感じと思ったらすぐ離すようにしてください。念入りに当てすぎるのはNGです。
センサモジュールGY-521(MPU6050)を使う
センサモジュールにピンソケットをはんだ付けする
今回は傾きと、回転速度をとることができるセンサーGY-521(MPU6050)にピンソケットをはんだ付けしてみます。
傾きを取得する加速度センサと、回転角速度を取得するジャイロセンサが載ったセンサで、真ん中の黒くて四角いIC部分がTDK社製のMPU6050で、それをすぐ使えるようにしたモジュール基板の名前がGY-521となっています。
配線
はんだ付けができたら、以下の通りに配線していきます。アナログの4番5番ピンは、実はI2Cという通信方式のためのピンにもなる特殊なピンになっています。
スケッチの書き込み
-
という最初から入っているライブラリを使用
- I2C(I Squared C)通信という通信方式を使って、センサーの情報を取得してくる
#include <Wire.h>
#define MPU6050_ADDR 0x68
#define MPU6050_AX 0x3B
#define MPU6050_AY 0x3D
#define MPU6050_AZ 0x3F
#define MPU6050_GX 0x43
#define MPU6050_GY 0x45
#define MPU6050_GZ 0x47
#define MPU6050_POWER 0x6B
short int acc[3];
short int gyro[3];
short int temp;
void setup()
{
Serial.begin(115200);
Serial.println("I2C Initialize.");
//I2Cの初期化
Wire.begin();
Wire.beginTransmission(MPU6050_ADDR);
//MPU6050の動作をスタート
Wire.write(MPU6050_POWER);
Wire.write(0);
Wire.endTransmission();
}
void loop()
{
//約60fpsに抑える
delay(12);
//AXから連続する14バイトのデータをリクエスト
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(MPU6050_AX);
Wire.endTransmission();
Wire.requestFrom(MPU6050_ADDR, 14);
//データの取得開始、加速度センサの値を受信(2バイトで来るため、先頭8ビットを上位にシフトして合計)
for (int i = 0;i < 3;i++) acc[i] = (Wire.read() << 8) | Wire.read();
//温度センサを受信(今回は使わないが、順番に流れてくるので受信する)
temp = (Wire.read() << 8) | Wire.read();
//ジャイロセンサの値を受信
for (int i = 0;i < 3;i++) gyro[i] = (Wire.read() << 8) | Wire.read();
//受信結果をシリアルで表示。TAB('\t')で区切ると、色分けされたグラフとして表示される
for (int i = 0;i < 3;i++) Serial.print(String(acc[i]) + '\t');
for (int i = 0;i < 3;i++) Serial.print(String(gyro[i]) + '\t');
Serial.println("");
}
モニタ/プロッタで確認
- シリアルモニタでは、タブ区切りの数字として表示される
- シリアルプロッタに展開すると、色分けされたグラフとして見える
プログラムが無事アップロードされたら、シリアルモニタでデータを確認します。
USBからそれぞれの値がタブ区切りで送信されるのが確認できると思います。
タブ区切りで見ることで簡単な時系列の値の変化を読み取りやすいというのもありますが、こうして書いておくとシリアルプロッタの画面で見た時に色別で表示されるのでとても分かりやすくなります。
通信とケーブル
- データをやり取りする方法
このようにハードウェアを扱っていくと、Arduino<->PC間やArduino<->モジュール基板間のように、2つの機器の間で情報をやり取りするということがよくあります。今回はUSBケーブルとジャンパーピンを使って通信しましたが、これもやりとりする情報の内容や、やりとりする2つの機器の距離など条件によって使うべき通信方式・ケーブルやコネクタの選択肢がいろいろと変わってきます。
このあたりは展示や実験装置を作る上でかなり重要なトピックなのですが、電子工作関連の情報の中では少ない印象があったので、このあたりまとめつつ解説していこうと思います。
通信する時に決めること
- どんな伝え方をするか (プロトコル/Protocol)
-
どんな端子を使うか (コネクタ/Connector)
- どんな線材を使うか (ケーブル/cable)
まず何らかの機器同士でやりとりする時に何を決める必要があるかという点ですが、大きく3つ「プロトコル」「コネクタ」「ケーブル」に分類することができます。
伝え方「プロトコル」
- Arduinoとパソコン:RS-232C・USBシリアル・TCP・UDP…
- Arduinoとモジュール基板(基板同士):SPI・I2C・UART
一つ目の伝え方「プロトコル」は、誰と誰が通信するか、どんな内容をどんな速度でやりとりするかによって選択肢が決まってきます。お互いがソフトウェアで対応するプロトコルを使う必要があり、今日Arduinoとモジュール間でやりとりしたのはI2Cという規格です。モジュール基板はI2CかSPIに対応していることが多いです。
つなぎ方「コネクタ」
- 必要なピンの数
- 誤挿入の防止
- 抜き差し頻度・環境に求められる耐久性
まず一つ目のコネクタは、デバイスの出入口にあたる部分です。PCだとUSB端子や有線LANポートがあったり、オーディオだとXLR端子やフォン端子、フォンにも3.5Φや6.3Φといった大きさ違いの物もあると思います。
先ほどジャンパーピンでセンサーとArduinoを接続したように、極端な話電気的に繋がってさえいれば通信はできます。しかし、頻繁に抜いたり差したりする必要が出てくると、3本も4本も毎回場所を確認しながら抜き差しするのは面倒です。そこでこういったコネクタを使うと、抜き差しの作業を効率化できる上、間違ったピンにつないでデバイスを壊したりするという事故も防止することができます。
伸ばし方「ケーブル」
-
伝送距離が延びる程、ノイズの影響を受けやすい
- ツイストペアやシールドはノイズ対策になる
- 数が増えてくると、美観的なところも気になってくる
ある程度伸ばす距離が伸びてくると、ケーブルにも気を使ったほうがよくなってきます。
これはノイズの影響を受けやすくなってくるからで、センチメートル単位であれば電子工作用の導線で事足りますが、長くなってくると信号の種類に応じて、シールドされているケーブルを選ぶ必要が出てきたりします。
簡単なケーブル作りの例
- JST XHコネクタ
- 入手性が良く、メス端子は基板に直接取り付けられる
最後に、よく使うケーブル作りの例を紹介します。
以前モーターを制御する仕事でユニットを作った際に作ったケーブルの例です。
コネクタは基板用のコネクタというのが色々ありますが、JSTというメーカーのXHコネクタを使いました。アマゾンや秋月で購入できるので入手性がよく、ユニバーサル基板にも直接はんだで取り付けられるのでよく使っています。お気に入りのコネクタをストックしておくと便利です。
ケーブル
- ミスミで購入(個人で買うならモノタロウやオヤイデ)
- AWG22の2芯・3芯を使用
ケーブルは柔らかくて取り回しの良い、PVCで被覆されたロボットケーブルを使っています。銅線の太さでよく使われるのがAWGという規格ですが、XHコネクタであればAWG22がちょうどよい太さなので、AWG22の2芯・3芯ケーブルを使っています。
私の場合事業者が使えるミスミという販売サイトを使っているのですが、学生の皆さんはモノタロウや実店舗のあるオヤイデ電気を使うのが良いでしょう。
圧着して、ケーブルを作る
- 圧着工具で、端子とケーブルをつなぐ
- ケーブルははんだ付けでは作らない
コネクタとケーブルをバラバラで買ったら、この二つを必要な長さでつないでコネクタ付きのケーブルにします。
端子の金具で、中の銅線と被覆をしっかり掴むように変形させて合体させます。引っ張ったら抜けてしまいそうにも感じますが、この手のコネクタは接続部分に力がかかるので、はんだ付け等でつけてしまうと金属疲労を起こしてすぐ切れてしまうので、しっかり力を入れて圧着した方が丈夫になります。
メス端子(レセプタクル)は、基板にはんだ付け
XHコネクタはピンの間隔が2.54mm(0.1インチ)ピッチになっているので、ユニバーサル基板にもそのままはんだ付けしてしまうことができます。はんだ付けすればあとは差し込めばカチッとしっかりロックされるので、ジャンパーピンなどを使うよりも安心感のある結線が可能になりました。
課題と今後の講義のためのアンケート
基礎的な知識の紹介をしてきたので、徐々に具体的な実例などをやっていこうと思っていますが、シラバスの内容をどのくらいの割合でやるか、課題どうするかなどをちょっとアンケートを元に計画を練っていきたいのでGoogleフォームのアンケートにお答えいただけると助かります。聴講の人も是非!
第六項 Arduino開発基礎(ライブラリとモジュール)
第六項 Arduino開発基礎(ライブラリとモジュール)
電子工作創作表現(2019/5/31)
スライドPDF
これまで紹介したのは「基本機能」
- 小規模で、シンプルな構造のセンサーやアクチュエータなら扱うことができる
- もう少し込み入ったことをするには「ライブラリ」や「モジュール基板」が有効
今回はArduinoでより複雑な機能を扱うための「ライブラリ」について紹介していきます。
これまで紹介したプログラム構文やリファレンスで紹介していたものは、Arduinoの基本的な機能を使うための情報でした。
これらの基本機能だけでも小規模でシンプルな構造のセンサーやアクチュエータを扱うことはできます。
もう少し込み入ったことも出来ないわけではありませんが、そのためには必要な知識が更にどっと増える事になりますので、まずはライブラリやモジュールとよばれる部品を使う事をおススメします。
モジュールとは
- 目的に沿った部品を組み合わせて一つの基板にまとめたもの
- コストはかかるが、電源管理やノイズ対策のようなところを気にしなくて済む
モジュールというのは、実現したい目的に合わせてあらかじめ基本的な部品が一つの基板にまとまった物です。大半が小さい基板の上にICやセンサー部品と、抵抗やトランジスタ、コンデンサといった部品をギュッと一つにまとめた形をとっています。
例えばモーターを動かしたいとなった時、一から回路を設計しようとするとノイズ対策や逆起電力対策というものが必要になってくるのですが、そういったある程度ややこしくて本筋とはあまり関係ない部分を全部ひとまとまりにやってくれているのがモジュールです。そういう意味では、Arduinoもモジュールの一種と呼べます。
モジュールの例
- モータードライバ
- 6軸センサモジュール
- Arduinoシールド
定番としてはモータードライバやセンサモジュールがあります。入力装置であるをモジュールにしたものはセンサモジュールという事が多いですが、アクチュエータを動かすためのモジュールは「ドライバ」と言う事が多いです。なのでアクチュエータを使うためのモジュールが欲しくなった場合は「モータードライバ」「LEDドライバ」などと検索すると良いでしょう。
そしてArduinoに関しては、ピンに上から差し込めるタイプのモジュールが色々開発されており、これらは「シールド」と呼ばれることもあります。写真はモータードライバのシールドです。
モジュール選び
- 同じ機能でもいろいろな仕様のモジュールがたくさんある
- Webページや、周囲で使ってる人がいるか
- ノウハウが手に入りそうな物を狙って買うべし
そんなモジュールですが、実に様々なモジュールがあります。同じモータードライバだけでもたくさんあり、その使い方も様々です。
シンプルなセンサーと違いデータシートというものは無く簡単な説明書があるだけだったりします。(載っているチップのメーカーからデータシートを手に入れてね!というスタンスも)
「このモジュールを買ったんですが動かなくて…」という相談をされる事があったりするのですが、見たこと無いモジュールですぐには問題が特定できなかったり答えられないことがあったりします。
何かモジュールを買ってみる時にはできるだけメジャーそうなもの、Arduinoで使ったWebの記事を見つけたり、回りで使ってそうな人に相談するなどして、ノウハウがゲットできそうなモジュールを買うようにしてみてください。
ライブラリとは
- 基本以外の機能を実現する
- 「ライブラリ」はコーディングで共通のワード
- モジュールを利用するための専用ライブラリも
そして今日もう一つ紹介するのが「ライブラリ」です。ライブラリというのはモジュールのプログラム版のようなイメージで、ArduinoIDEで使える機能を増やせる追加のプログラムです。
一般的にプログラミング言語で用意されている機能というのは基本的なものだけで、必要に応じてこのライブラリを使って拡張するという考え方で設計されています。
Arduinoの開発についても同じで、外部との通信や複雑な制御をひとまとめにしたり、先のモジュールを開発したメーカーが、モジュールをコントロールするためのライブラリを自分達で作って提供していたりもします。
ライブラリマネージャを使う
ArduinoIDEではライブラリマネージャから主要なライブラリを追加することができます。
スケッチ>ライブラリをインクルード>ライブラリを管理 もしくはCtrl(⌘)+Shift+Iでライブラリマネージャを開きます。
検索・インストール
このライブラリマネージャは、個人でもGithubというところで申請をして登録してもらうことができるので、メーカーから個人まで様々なライブラリが検索できるようになっています。
右上のテキストボックスで検索をします。今回はNeoPixelというライブラリをインストールしてみます。「neopixel」と検索するといくつかのライブラリに絞り込まれるので、「Adafruit NeoPixel by Adafruit」をマウスオーバーして「インストール」ボタンを押します。これでライブラリのインストールは完了です。
ライブラリのサンプル
インストールが完了すると、スケッチ例の項目にインストールしたライブラリのサンプルが追加されます。「More info」でソースコードが公開されているページにジャンプしてドキュメントを読む事も可能です。二つを合わせて読むとそのライブラリについて必要な情報が得られます。
複数のLEDを簡単に制御できる「NeoPixel」
- Adafruitが開発しているLEDモジュール
- 電源・信号線・GNDの3本線だけで複数のLEDを制御できる
NeoPixelはAdafruitという会社が作っているLEDモジュールのシリーズ名で、テープ状や板状のモジュールとして売っています。国内でもスイッチサイエンス等で取り扱いがあり、手っ取り早くLEDをたくさん光らせたい時には大変便利なモジュールになっています。電圧やノイズ等環境によっても変化しますが、数十粒単位で光らせることが可能です。
https://sekailab.com/wp/2017/01/20/neopixel-like-rgbleds/
#includeでライブラリを読み込む
- #include <xxx.h>でライブラリをスケッチと接続する
先ほどライブラリをインストールしましたが、これはArduinoIDEにインストールされただけなので、自分のスケッチでライブラリを使う宣言をしなくてはいけません。
スケッチでライブラリを使いたい時には"#include<xxx.h>"という記述を最初に書きます。これによってライブラリのプログラムをスケッチが接続されて、自分のスケッチの中で使えるようになります。カッコの中に入るファイル名はライブラリによって違ってくるので、サンプルやドキュメントを参考にします。
ソースコード:
https://www.tinkercad.com/things/hbvNF04It4l
ライブラリで使う機能は”クラス”になっていることが多い
- 変数のような形でインスタンスが定義される。
- インスタンス名.関数名()という形式をとる
pixels.showという新しい記述方法が出てきました。これはC++の「オブジェクト指向」に基づく書き方になります。
Adafruit_NeoPixelの部分は”クラス”と定義されますが、ライブラリはこのようなクラスの形式を持つものが多く、変数のような形で使うクラスを宣言した後、クラス.(ドット)関数名という書き方でクラスが持っている機能を使うことができます。
基本的にはサンプルを参照しながら使う事になりますし、Arduinoをやるためにオブジェクト指向を学ぶというのは少しハードルが高くもあるので、ここでは省略します(もう少し進んでからやるかも)
演習
06-exerciseを改良する形で作ってみてください。
- 梅:光が移動するたびに赤・緑・赤・緑…と変化する
- 竹:赤・緑・青の三色が並んで動く
- 松:光が往復して光り、跳ね返るところで色が変わる
https://www.tinkercad.com/things/hbvNF04It4l
梅:光が移動するたびに赤・緑・赤・緑…と変化する
ヒント:counterの値が偶数か奇数かを、前回やったIf文で判定することで実現できます。
#include <Adafruit_NeoPixel.h>
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 8 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int counter = 0;
void setup()
{
pinMode(PIN, OUTPUT);
pixels.begin();
}
void loop()
{
counter++;
pixels.clear();
if (counter % 2 == 0)
{
pixels.setPixelColor(counter % NUMPIXELS, pixels.Color(255, 0, 0));
}
else {
pixels.setPixelColor(counter % NUMPIXELS, pixels.Color(0, 255, 0));
}
pixels.show();
delay(500);
}
竹:赤・緑・青の三色が並んで動く
ヒント:setPixelColor命令を増やせば、同時に光らせるLEDを増やせます。counterの数字を工夫してみましょう。
#include <Adafruit_NeoPixel.h>
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 8 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int counter = 0;
void setup()
{
pinMode(PIN, OUTPUT);
pixels.begin();
}
void loop()
{
counter++;
pixels.clear();
//counter - 1 / conter - 2としてあげることで1個ずつずれる
pixels.setPixelColor(counter % NUMPIXELS, pixels.Color(255, 0, 0));
pixels.setPixelColor((counter - 1) % NUMPIXELS, pixels.Color(0, 255, 0));
pixels.setPixelColor((counter - 2) % NUMPIXELS, pixels.Color(0, 0, 255));
pixels.show();
delay(500);
}
松:光が往復して光り、跳ね返るところで色が変わる
ヒント:行き8個、帰り8個で16個周期という考え方で、行きかえりの動きを定義してあげることで実現できます。
#include <Adafruit_NeoPixel.h>
#define PIN 6 // On Trinket or Gemma, suggest changing this to 1
#define NUMPIXELS 8 // Popular NeoPixel ring size
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
int counter = 0;
int hue = 0;
void setup()
{
pinMode(PIN, OUTPUT);
pixels.begin();
}
void loop()
{
counter++;
pixels.clear();
if (counter % 16 < 8) //16個周期初めの8個
{
pixels.setPixelColor(counter % NUMPIXELS, pixels.ColorHSV(hue, 255, 255));
}
else //後半8個は戻るので、7からカウンタの余りを引いた数で定義する
{
pixels.setPixelColor(7 - (counter % NUMPIXELS), pixels.ColorHSV(hue, 255, 255));
}
if (counter % 8 == 0)
{
//ランダムで色相を変更
hue = random(0, 65536);
}
pixels.show();
delay(200);
}
第五項 Arduino開発基礎(if構文・ループ構文)
第五項 Arduino開発基礎(if構文・ループ構文)
電子工作創作表現(2019/5/24)
スライドPDF
今日の内容
- if / for構文
- 配列変数
プログラムの基本は「上から下」
- 前回までやったルール「上から下」と「ジャンプ」
- 決まり切った動作しかできない
- 複雑な動作を実現するための「制御構造」(control structure)
前回まで、Arduinoのプログラムは基本的に上から下に動き、setup()関数は1度、loop()関数は繰り返し行われるという話をしました。
ですが、これだけだと決まりきった動作をさせることしかできません。もう少し複雑な動作を実現するための「制御構造」というのがいくつか用意されており、それを紹介していきたいと思います。
代表的な制御構造
-
if構文(If statement)
- for構文
- while構文
代表的な制御構造を解説していきます。「XX構文」という言い方をします。Arduinoのリファレンスページにもありますが、これらはArduino開発のベースになっているC/C++で定義されている構文になります。
if構文
- 特定の条件を満たした時だけ動作する
- もしも...だったら~をする
まず一つ目はif構文です。これは特定の条件を満たしたときにだけ実行したい処理を定義するための構文です。
書き方は
if (条件式) {
/* 条件を満たしたときの処理 */
}
という書き方をします。かっこで囲まれた条件式を満たす時だけ、この中に書いた処理が実行されるので、条件が満たされない場合は処理がスキップされます。
条件式 (conditional expression)
- 等号・不等号(== < >)で値を検証する
- 等しい(==) 以下・以上(>= <=) 等しくない(!=) など、少し書き方が特殊
- 結果はtrue(真)かfalse(偽)
条件式は、特定の変数や値が等しいか等しくないか、大きいか小さいかで定義します。イコールは等号が二つ、以上・以下は>=, <=など、ここでも数学とは少し違う書き方をします。
条件の書き方
if (digitalRead(13) == HIGH) //13番ピンがHIGH
if (analogRead(0) < 300) //アナログ0番が300「未満」
if (analogRead(0) <= 300) //アナログ0番が300「以下」
if (Serial.read() != 'a') //シリアル通信で来た文字がa以外
具体的には、このような書き方をします。Arduinoの場合はピンから入力した値を比較したり、シリアル通信で文字のやりとりを検証したりすることに使うことが多いくあります。
条件を満たさない時は「else」も使う
- 条件を満たさなかった時に実行する処理
そしてこのif文には後ろにelseという構文を追加することができます。これは逆に「条件を満たさなかった場合」の処理を記述するブロックで「もし~ならこう、そうでなければこう」という場合分けを書く際便利な書き方です。
if (条件式){
条件を満たした時の処理
}else{
条件を満たさなかった時の処理
}
if文を使った例
- if文を使うと複雑な動作も条件付けで実現できる
- 0.5秒周期と、0.3秒周期で点滅するLEDを同時に制御するには?
if文を使うとどういったことができるようになるか。単純に考えると「インタラクティブな要素が作れる」というのが素直な結論ですが、それだけではなく色々な表現の幅が生まれます。
例えば0.5秒ごとに点滅するLEDをBlinkのサンプルに沿って作るとdelay関数を使いますが、ここへ0.3秒ごとに点滅するLEDも付けたいとなるとどうなるでしょうか。もう1個Arduinoを用意すればできますが、それは無駄なのでIf文を組み合わせてこんな感じに実現することができます。
void loop()
{
if (millis() % 1000 < 500) digitalWrite(10, HIGH);
else digitalWrite(10, LOW);
if (millis() % 600 < 300) digitalWrite(9, HIGH);
else digitalWrite(9, LOW);
}
for構文
- 命令を少しずつ変えながら繰り返したい時に便利な構文
- 繰り返す回数や条件を指定することができる
二つ目はfor構文です。これは繰り返しをするための構文です。loop()関数は電源が入っている限り繰り返されるようになっていますが、このfor構文は有限回繰り返したい時に便利な構文です。
for (int i = 0;i < 10;i++)
{
/* 繰り返す処理 */
}
for文は初期化・繰り返し条件・ループ処理の3つをセミコロンで区切ってカッコの中に定義します。上の例ではiという変数を用意して、iが10未満の間繰り返すという条件になっています。ループの終わりまで到達すると「i++」が呼ばれてiの値が1増えます。
for文の書き方
for (初期化; 繰り返す条件式; カウント式)
for (int i = 0; i < 回数; i++) で覚えておくのが便利
カッコの中を日本語で書くとこのようになります。初期値を0以外にしたりカウントを++ではなく--にしたり、5ずつ追加するなど色々な応用が効きますが、はじめのうちはシンプルに特定の回数繰り返すものとして、int iから始まる書き方を覚えておくのが良いでしょう。i, j, K などを使うのが通例になっていますが、変数の名前は基本的に自由です。
初期値を工夫した用法
初期値を工夫して、変数iを有効活用する
for (int i = 9;i < 12;i++)
{
analogWrite(i, millis() % 255);
}
変数はループのカウンタとして使いますが、初期値や条件を工夫することで中の処理に活用することができます。
ただの繰り返しではなく、連続して変化する処理を複数行いたい時にはこのような書き方が便利です。
配列変数(array variables)
- for文と組み合わせてよく使う「配列変数」
- 複数の値を格納することが可能
そしてこのfor構文とセットで覚えたい「配列変数」を紹介します。
配列変数は同じ種類の変数をいくつも扱う時に便利なもので、変数名とインデックスの2つの要素があります。
変数の定義
- float value; //通常の変数は一つの値を保持する
- float arrValue[5]; //10個の数値を持てる配列変数
- float arrValue[] = {10, 12, 15, 31, 35}; //こういう書き方も
通常の変数は宣言(変数を用意)するときに変数の種類と名前を定義しますが、配列変数の場合名前の後ろに[10](角括弧、bracket)という形で作りたい変数の数を記述します。最初に入れておきたい数が決まっている時は、3つ目のような書き方も可能です。
便利な「添え字」
- 0から始まって、連続する
- 角括弧の中の添え字は数字なので、変数などを使って呼び出せる
この角括弧の中の数字は「添え字」や「インデックス」という呼び方をして、変数XXの何番目の数かということを表しています。
少し注意したいのは、添え字は0から始まるということです。なので、arrValue[5]と定義した場合添え字に指定できる数は0~4までということになります。
使い方
- 複数の数値をひとまとまりとして扱う
- 配列の添え字とForループを組み合わせて使う
どういう使い方が便利なのかというのを実践を交えて解説していきます。
複数の数値をひとまとまりとして扱うときには便利です。例えばフルカラーLEDは赤・青・緑の三色を組み合わせて色を表現しますが、こういうものをコントロールするときは赤用の変数、青用の変数…と別々に用意するよりも、色用の配列変数として定義するのが効率的です。
int led_pin[] = {9, 10, 11};
int led_color[3];
void setup()
{
for (int i = 0;i < 3;i++) pinMode(led_pin[i], OUTPUT);
}
void loop()
{
delay(12);
for (int i = 0;i < 3;i++)
{
led_color[i] = (sin(millis() / ((i+2) * 100.0)) + 1) * 128.0;
analogWrite(led_pin[i], led_color[i]);
}
}
演習
- 回路:https://www.tinkercad.com/things/5l5dPsFDRzz
- 梅:ダイヤルを途中まで上げると赤が光り、さらに上げるとオレンジが光る
- 竹:ダイヤルを上げていくと順番に光っていく(数が多いのでFor文を使おう)
- 松:ボタンを押したときに、LEDが順番にフラッシュする
梅の回答
ヒント:赤のLEDと橙のLEDをそれぞれIF文でコントロールしましょう。
analogRead(0)で値が取れるので、数値を使って光り方を変えていきます。
void setup()
{
for (int i = 2;i <= 9;i++)
{
pinMode(i, OUTPUT);
}
}
void loop()
{
delay(12);
//赤色が点灯する
if (analogRead(0) > 300) {
digitalWrite(2, HIGH);
}
else{
digitalWrite(2, LOW);
}
//橙色が点灯する
if (analogRead(0) > 1000) {
digitalWrite(3, HIGH);
}
else{
digitalWrite(3, LOW);
}
}
竹の回答
ヒント:基本的な方向性は梅と一緒ですが、8個別々に記述していくと大変です。
できるっちゃできますが、For構文を使うとよりきれいに書くことができます。
void setup()
{
for (int i = 2;i <= 9;i++)
{
pinMode(i, OUTPUT);
}
}
void loop()
{
delay(12);
for (int i = 2;i <= 9;i++)
{
//光る境目の値(スレッショルド)をiを使って定義する
if (analogRead(0) > i * 100) {
digitalWrite(i, HIGH);
}
else{
digitalWrite(i, LOW);
}
}
}
松の回答
ヒント:光るタイミングがスイッチに依存していて、かつそれぞれ独立しているので、LEDの状態を持っておくための変数を配列で持っておくと作りやすくなります。
int led_stat[8];
void setup()
{
for (int i = 2;i <= 9;i++)
{
pinMode(i, OUTPUT);
}
pinMode(10, INPUT_PULLUP);
}
void loop()
{
delay(12);
//ボタンが押された時
if (digitalRead(10) == LOW)
{
for (int i = 0;i < 8;i++)
led_stat[i] = 0;
}
for (int i = 0;i < 8;i++)
{
//led_statは常にカウントし続ける
led_stat[i]++;
//led_statの値が特定の範囲になった時、LEDを光らせる
if ((led_stat[i] > (i + 1) * 4) && (led_stat[i] < (i + 1) * 10))
{
digitalWrite(i + 2, HIGH);
}
else
{
digitalWrite(i + 2, LOW);
}
}
}
第四項 Arduino開発基礎
第四項 Arduino開発基礎
電子工作創作表現(2019/5/17)
スライドPDF
Messengerグループを作ります
比嘉さんの講義と同様に連絡用グループ作りました
https://m.me/join/AbZhVqUurvKr9_8u
休講のお知らせなど講義の連絡用に、FacebookのMessengerグループを作りました。
以下のURLから参加できますので、申請をお願いします。
Facebookアカウントを持っていない人でも、Messengerだけのアカウントを作ることができるので、アプリをダウンロードしてみてください。
今日の内容
- Arduinoプログラミング
- TinkerCADで開発のシミュレーション
TinkerCADで開発
- 色々試したい時配線が大変
- 簡単な部品を使ったシミュレーションができる
Arduinoの現物に色々配線をしていこうとするとそっちに時間が取られたり、回路に問題があるのかプログラムに問題があるのか分かりづらい事が多いです。
なので、ここから先Arduinoの開発に関する演習はこのTinkerCADを使ってやっていこうと思うので、ユーザー登録をしてみてください。
Arduino IDEができること
- Arduioファミリ用のプログラムを生成する
- シリアルモニタ/プロッタなどデバッグ機能
- ライブラリ(特別な機能を搭載するためのプログラム)の管理
前々回から触っているArduinoIDEについて少しおさらいです。
ArduinoIDEはArduinoの開発に関わる色々な機能をひとつにまとめたソフトで、プログラムの作成と書き込みをしたり、シリアルモニタやプロッタで情報を見たりする機能がついています。
加えて、特殊な機能を使えるようになる「ライブラリ」と呼ばれるものの管理ができたりもします。
開発手順のイメージ
1.Arduino IDEでコーディング
2.コードをコンパイルしてバイナリに変換
3.変換したバイナリをArduinoマイコンに転送
開発のイメージはこんな感じで、私たちが英語に近い文章で書いたプログラムをArduinoのマイコンが理解できるデータに変換(コンパイル)し、USBを経由して転送しています。
開発言語について
- ArduinoはC++という言語をベースにしている
- 基本的な拡張子は.inoだが、.cppや.hも使える
コーディングの際記述する文のルールをまとめて「プログラミング言語」と呼びます。今は非常に多くの言語が存在していますが、ArduinoはC++という言語をベースに作られています。そして保存したファイルを「スケッチ」と呼び(この辺りはProcessingの系譜を継いでいます)拡張子は.inoを使いますが、C++がベースになっていて.cppや.hも使うことができます。
補足:C++ベースとは?
- .inoはArduino IDEで変換されたあと、avr-g++に通される
- .cpp/.cで記述されたものはavrのコンパイラに直接流し込まれる
C++ベースとはどういうことなのかという点ですが、コンパイル段階で色々なことをしています。.inoスケッチはArduinoIDEで改変されてからAVRコンパイラへと流され、main関数を含むmain.cppは別で用意されているものが一緒にコンパイルされます。
.cpp/.cで定義されたファイルはダイレクトにAVRコンパイラへ通されるので、Arduinoの改変をされたくない場合はこの拡張子を使うということになります。
参考:https://garretlab.web.fc2.com/arduino/introduction/compile_process/index.html
前回のプログラムをおさらい
https://www.tinkercad.com/things/4danaaOlAin
ではどのようなルールに基づいてプログラムを書いて行ったら良いのか、前回書いたコードを元にとっかかりとなる基本的な構造を説明していきます。
void setup()
{
pinMode(9, OUTPUT);
}
void loop()
{
digitalWrite(9, HIGH);
delay(1000);
digitalWrite(9, LOW);
delay(1000);
}
setupとloop
- Processingから来ている基本構造
- 1回のsetupと、半永久的に繰り返されるloop
プログラムというのは、下に向かって順番にコードが解釈・実行されていくものですが、プログラムの内容によってはその読んでいくラインがあちこちにジャンプします。
Arduinoにおいては、setupとloopという二つの「関数」と呼ばれるまとまりがベースとなっています。
setupは起動して一番初めに一度だけ処理される関数で、ピンの設定のような初めに決めておきたい事を書いていくブロックで、すぐ下の大かっこ{}で囲まれた部分がsetupの中身ということになります。
同様にloopの中も大かっこで囲まれた部分ですが、ここに書かれたことは一番下まで実行されるとまた先頭に戻って、電源が落とされない限り半永久的に実行され続けます。このsetupとloopの中にプログラムを書いていくのがArduinoの基本で、これはProcessingやOpenFrameworksのようなメディアアート系の開発環境によくある構造です。古くはProcessingの前身であるDesign by numbersから来ています。
関数と変数
- 小さなプログラムのまとまりが「関数」
- 関数名(引数){ 内容 }というフォーマットで定義される
setup・loopの事を関数と呼びますが、この中に記述する最も基本的な要素が「関数」と「変数」です。
前回出たdigitalWriteやdelayも関数の一つで、Arduinoが実現できる命令が色々な関数として定義されています。
関数の情報は「リファレンス」を見る
- どんな関数があるかリファレンスで知ることができる
- 使い方を知るにはサンプルも役立つ
英語リファレンス:https://www.arduino.cc/reference/en/
日本語リファレンス:https://www.arduino.cc/reference/jp/
非公式だが充実しているリファレンスも:http://www.musashinodenpa.com/arduino/ref/
つまりArduinoで何かやろうとすると、どんな関数があるかを知らなくてはならないわけですが、公式のWebなどに細かな情報がまとめられています。こういったプログラミング言語などに定義された機能をまとめた情報のことをよく「リファレンス」と言います。
Arduinoの場合は公式で出されている英語と日本語のリファレンスがありますが、日本語は随時翻訳しているようでまだ情報が歯抜けになっているので(2019/05/17現在)英語のリファレンスを読むか、非公式のリファレンスなどを見るのが良いでしょう。
前回までに使った関数
pinMode(pin, mode)
digitalWrite(pin, value)
delay(ms)
実際に、前回までに使った関数をリファレンスで見てみましょう。
変数
- 関数と同様に重要な「変数」
- 中に数値や文字を保存しておいて、色々な計算ができる
関数と同じくらい重要な要素に変数があります。変数はデータを入れておく箱のようなイメージです。
入力したデータを一時的に保存しておくことができるので、複雑な計算や複数の項目に同じパラメータを使いたい時などに有用です。
数学と少し違う「演算子」
" + - * / "という数学の記法とは少し異なる演算子を用いる
四則演算のほかにも%(余剰) ^(累乗)やビット演算子のような特殊な演算子も
プログラムに使うのは基本的に半角英数字なので、×や÷のような記号が無く、通常の四則演算しとは少し異なる記号を使って計算を表現します。Arduinoで用いる演算子はC++に準拠していて、余りや累乗のような演算子も使うことができます。
また補足ですが、パソコンと違ってメモリや処理速度に限りがあるため、大きなプログラムになるとデータの節約が必要になります。そういった際には2進数で保持されている数字を直接操作する「ビット演算」が役に立つので、より高度な制御をやりたいと思っている人は覚えておくと良いでしょう。
演習
梅:LEDが赤・緑・青と1秒ずつ順番に点灯していって、同時に消えるのを繰り返す
竹:電位差計のメモリを操作すると、点滅する速度が変化
松:全消灯~全点灯までの8通りの組み合わせを、int型の変数pattern1つで表現する
最後に、簡単な演習を用意したので梅から順にチャレンジしてみてください。
演習の正解例と解説
以下演習の正解例と解説です。あくまで正解例であり、書いてある要件を実現する方法はほかにも色々な書き方があると思います。
梅編
お題:LEDが赤・緑・青と1秒ずつ順番に点灯していって、同時に消えるのを繰り返す
delayで時間を区切りながら、digitalWriteで光らせたいところ、消したいところを記述していきます。
最後は同時に消えてほしいので、delayを挟まず三つのdigitalWriteを続けて書けばほぼ(人には知覚できない速度で)同時に消灯されます。
void setup()
{
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
}
void loop()
{
delay(1000);
digitalWrite(9, HIGH);
delay(1000);
digitalWrite(10, HIGH);
delay(1000);
digitalWrite(11, HIGH);
delay(1000);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
}
竹編
お題:電位差計のメモリを操作すると、点滅する速度が変化
delayの値に変数を使ってみます。
analogRead(0) + 100という値を入れることで、点滅する感覚が100ミリ秒~1123ミリ秒の間で推移します。
秒数指定が中途半端で気持ち悪いと思ったら、map関数を組み合わせると簡単に範囲を変えることができます。リファレンスで調べてみましょう。
int interval;
void setup()
{
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
}
void loop()
{
interval = analogRead(0) + 100;
delay(interval);
digitalWrite(9, HIGH);
digitalWrite(10, HIGH);
digitalWrite(11, HIGH);
delay(interval);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
}
松編
お題:全消灯~全点灯までの8通りの組み合わせを、int型の変数pattern1つで表現する
複数のフラグを1バイトの変数にまとめておく手法は、マイコンなどPCを使わないデバイスではよく使われる手法です。ここでは3つのLEDの状態をint型変数の最後3ビットからそれぞれ取得することで、0~8までの数字をLED点灯パターンの組み合わせとして使っています。
int pattern = 0;
void setup()
{
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
}
void loop()
{
delay(1000);
pattern = pattern + 1;
digitalWrite(9, pattern & 0x1);
digitalWrite(10, pattern & 0x2);
digitalWrite(11, pattern & 0x4);
}