1. Arduino側
1.1 シリアル通信
センサデータを記録するためには、いくつかの方法がありますが、今回はシリアル通信を使ってPCにデータを保存してみましょう。
基本的な回路構成などは 文化情報工学基礎演習#1 を参考にしてください。
まずは、わかりやすくするために、サーミスタに関係する計測をreadThermoValue()
、シリアル通信関係をsendSerial()
でまとめておきましょう。
//アナログ入力ピンの設定
const int analogPin = 0; //A0を使用
//定数の定義
const float B = 3950.0; //サーミスタのB定数
const float R0 = 10000.0; //サーミスタの25度での抵抗値(カタログ値)
const float Rd = 10000.0; //検知抵抗の抵抗値
const float Tk = 273.15; //0度=273.15ケルビン
int test = 0;
float Tdeg = 0;
void setup()
{
//シリアルモニタ用
Serial.begin(9600);
}
void loop()
{
readThermoValue();
sendSerial();
//0.2秒待機
delay(200);
}
void readThermoValue()
{
//アナログ値を読む
float readValue = analogRead(analogPin);
//Rtを計算する
float Rt = Rd * readValue / (1023 - readValue);
//Tを計算する(単位はケルビン)
float Tbar = 1/B * log(Rt/R0) + 1/(Tk + 25);
float T = 1/Tbar;
//ケルビンから度に変換
Tdeg = T - Tk;
}
void sendSerial()
{
//シリアルモニターに表示する
//サーミスタを使う場合はこちら
//--------
// Serial.println(Tdeg);
//--------
//とりあえず動くか確認したい時はこちら
//--------
Serial.println(test);
if(test < 100)
test+=1;
else
test = 0;
//--------
}
1.2 時刻情報の表示
まずは、電源ONと同時に、時刻をスタートしてみます。millis()
関数を使用します。
Arduinoでのmillis()
関数は、プログラムが開始されてからの経過時間をミリ秒単位で返す関数です。
この関数は、プログラムが開始してから現在までの時間を符号なしのlong型(32ビットの整数)で返します。
//アナログ入力ピンの設定
const int analogPin = 0; //A0を使用
//定数の定義
const float B = 3950.0; //サーミスタのB定数
const float R0 = 10000.0; //サーミスタの25度での抵抗値(カタログ値)
const float Rd = 10000.0; //検知抵抗の抵抗値
const float Tk = 273.15; //0度=273.15ケルビン
int test = 0;
float Tdeg = 0;
unsigned long startMillis = 0;
void setup()
{
//シリアルモニタ用
Serial.begin(9600);
startMillis = millis(); //プログラム開始時の時刻
}
void loop()
{
readThermoValue();
sendSerial();
//0.2秒待機
delay(200);
}
void readThermoValue()
{
//アナログ値を読む
float readValue = analogRead(analogPin);
//Rtを計算する
float Rt = Rd * readValue / (1023 - readValue);
//Tを計算する(単位はケルビン)
float Tbar = 1/B * log(Rt/R0) + 1/(Tk + 25);
float T = 1/Tbar;
//ケルビンから度に変換
Tdeg = T - Tk;
}
void sendSerial()
{
//シリアルモニターに表示する
Serial.print(millis() - startMillis); //現在時刻を送信
Serial.print(",");
//サーミスタを使う場合はこちら
//--------
// Serial.println(Tdeg);
//--------
//とりあえず動くか確認したい時はこちら
//--------
Serial.println(test);
if(test < 100)
test+=1;
else
test = 0;
//--------
}
実行すると以下のように{時刻, センサ値}がSerial Monitorで確認できます。時刻の単位はミリ秒です。
2. Processing側
2.1 Arduinoから送られてきたデータの受信
次に、Processing側でデータを受け取って表示しましょう。
Processingをインストールしていない方はこちらから
ProcessingでArduinoから送信されるデータを受け取るためには、シリアル通信を介してデータを読み込む必要があります。ArduinoのsendSerial
関数で送られる時刻データとtest
値を受け取るためには、以下の手順でProcessingのコードを書くことができます。
- ProcessingにSerialライブラリをインポートします。
- (利用可能なシリアルポートをリストアップし、適切なポートを選択します。)
- シリアルポートを開いて、Arduinoからデータを読み取るための設定を行います。
draw()
関数内や別の関数を使用して、受信データを読み取り、処理します。
以下が、Processingのコードです。serialEvent()
関数は、新しいデータがシリアルポートに到着するたびに自動的に呼び出され、データを読み取り、画面に表示しています。
import processing.serial.*;
Serial myPort; // シリアルポート
String dataLine = ""; // 受信データを保持する変数
void setup() {
size(400, 300); // ウィンドウサイズの設定
//シリアルポートがわからない場合はこれを実行してコンソールを確認
//printArray(Serial.list()); // 利用可能なシリアルポートのリストを表示
//myPort = new Serial(this, Serial.list()[0], 9600); // シリアルポートを開く
myPort = new Serial(this, "/dev/tty.usbserial-110", 9600);
myPort.bufferUntil('\n'); // 改行コードが来るまでバッファリング
}
void draw() {
background(255); // 背景を白でクリア
fill(0); // テキストの色を黒に設定
text("Received: " + dataLine, 10, 50); // 受信データを表示
}
void serialEvent(Serial p) {
dataLine = p.readStringUntil('\n'); // 改行までの文字列を読み取る
if (dataLine != null) {
dataLine = trim(dataLine); // 文字列の前後の不要な空白や改行を削除
println(dataLine); // コンソールに出力
}
}
そうすると以下のように、Processing上でArduinoのデータが受け取れることが確認できましたか?
現状だと、文字列として{時刻, センサ値}を受け取っているだけです。
2.2 受け取ったデータをファイルに書き込み
次にファイルに書き込んでいきます。コードを以下のように変更します。
import processing.serial.*;
Serial myPort; // シリアルポート
String dataLine = ""; // 受信データを保持する変数
PrintWriter output = null;
void setup() {
size(400, 300); // ウィンドウサイズの設定
myPort = new Serial(this, "/dev/tty.usbserial-110", 9600);
myPort.bufferUntil('\n'); // 改行コードが来るまでバッファリング
output = createWriter("data/save.txt"); //書き出しファイルの作成
}
void draw() {
background(255); // 背景を白でクリア
fill(0); // テキストの色を黒に設定
text("Received: " + dataLine, 10, 50); // 受信データを表示
}
void serialEvent(Serial p) {
dataLine = p.readStringUntil('\n'); // 改行までの文字列を読み取る
if (dataLine != null) {
dataLine = trim(dataLine); // 文字列の前後の不要な空白や改行を削除
output.println(dataLine); // CSVファイルにそのまま書き込む
output.flush(); // バッファに残っているデータを強制的に書き出し
println(dataLine); // コンソールに出力
}
}
void stop() {
output.flush(); // バッファに残っているデータを強制的に書き出し
output.close(); // ファイルを閉じる
super.stop();
}
変更されたコードには、以下の主な変更点があります:
- ファイル書き出しの追加:
PrintWriter output = null;
という行が追加され、シリアルデータを保存するためのPrintWriter
オブジェクトが導入されています。これにより、受信したデータをテキストファイルに書き込むことが可能になります。
- ファイルの作成:
setup()
関数内でoutput = createWriter("data/save.txt");
という行が追加され、”data/save.txt”という名前のテキストファイルを作成するための処理が追加されています。これにより、プログラムが起動するときにファイルが開かれ、データの保存準備が整います。
- データの書き込み:
serialEvent(Serial p)
関数内で受信データをoutput.println(dataLine);
を使用してCSVファイルに書き込む処理が追加されています。また、output.flush();
が追加され、これによりバッファに残っているすべてのデータがファイルに即時に書き出されるようになります。
- プログラム終了時の処理の追加:
stop()
関数が追加されており、プログラム終了時にファイルを適切に閉じるための処理が含まれています。output.flush();
で最後のデータを書き出し、output.close();
でファイルを閉じることが確実に行われます。これにより、データが失われることなく安全にファイルが保存されます。
※たまに以下のようなエラーが出ます。Arduinoをリセットしたり、Processingを何度か立ち上げ直してください。
Error, disabling serialEvent() for /dev/tty.usbserial-110
null
コードを実行するとProcessingのフォルダの/Users/{ユーザ名}/Documents/Processing/{プログラム名}/data
の中にsave.txt
が作成できること確認できているかと思います。
適宜データを整形し、ファイル名をsave.csv
などに変更すればPytho等で解析可能かと思います。