今回は秋月の3軸加速度センサモジュールをArduino (Akiduino)につないで試してみました。接続といっても、ブレットボードに載せて、配線するだけです。
回路図を書く程でもなかったので、配線イメージ図っていう感じで
はじめはArduinoのスケッチで、起動時数回取得したAD値をアベレージして、オフセット値として、その後、毎回数回繰り返し取得したAD値の平均からオフセットを引いいて、数値をシリアルで画面に出して・・・ってやってみたんですが、数値がパラパラ変わるのは分かるんですが、早すぎて、何が起こってるんだかよくわからない・・・。
そこで、オフセットとか平均はとりあえずおいておいて、取得したAD値をシリアルに垂れ流して、パソコン側でグラフ表示して、何が起こってるか見てみようと思いました。
で、簡略化したスケッチが
#include "Arduino.h"
// 端子の割当。
const int PIN_ACC_X = 0;
const int PIN_ACC_Y = 1;
const int PIN_ACC_Z = 2;
// 変数宣言。
int adX = 0, adY = 0, adZ = 0;
byte sendData[8];
void setup() {
// シリアルポートの初期化。
Serial.begin(9600);
}
void loop() {
// AD値取得。
adX = analogRead(PIN_ACC_X);
adY = analogRead(PIN_ACC_Y);
adZ = analogRead(PIN_ACC_Z);
// 送信データ構成。
sendData[0] = 0x02;
sendData[1] = (byte)((adX & 0xff00) >> 8);
sendData[2] = (byte)(adX & 0x00ff);
sendData[3] = (byte)((adY & 0xff00) >> 8);
sendData[4] = (byte)(adY & 0x00ff);
sendData[5] = (byte)((adZ & 0xff00) >> 8);
sendData[6] = (byte)(adZ & 0x00ff);
sendData[7] = 0x03;
// 送信。
for (int i = 0; i < 8; i++) {
Serial.write(sendData[i]);
}
delay(200);
}
これで、シリアルに吐き出されたデータをPCに側で受信して、グラフ表示するプログラムを作って、表示させたら、こんな感じになりました。
赤・青・緑がそれぞれの軸のAD値で、加速度センサのブレッドボードを加速度センサの3軸方向にそれぞれ平行に振ってみたものです。3つの黄色の枠でそれぞれの方向に反応しているのがわかります。赤だけオフセットが大きいですが、赤はZ軸というか重力方向の軸です。重力加速度の影響かな?すみません、加速度センサのことをあまり勉強してないのでわかりません。でも、ためしにブレッドボード(Z軸の方向)をひっくり返したらオフセットが下側に行ったので、おそらく重力によるものでしょう。
ちなみにグラフ表示プログラムは Visual Studio 2010 のグラフ表示コントロールを使っています。
ソースはこんな感じ。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Management;
using System.Windows.Forms.DataVisualization.Charting;
using System.IO.Ports;
namespace SerialGraph
{
class acceleration
{
private int valueX = 0;
private int valueY = 0;
private int valueZ = 0;
public acceleration(int x, int y, int z)
{
this.valueX = x;
this.valueY = y;
this.valueZ = z;
}
public int getValue(int no)
{
if (no == 0)
{
return this.valueX;
}
else if (no == 1)
{
return this.valueY;
}
else
{
return this.valueZ;
}
}
}
public partial class frmMain : Form
{
// キーと値をセットで保存。「COM1」が「標準シリアルポート1」など。
Dictionary<string, string> ComPortTable = new Dictionary<string, string>();
// 取得データの履歴を保存。
const int MAX_DATA_COUNT = 40;
Queue<acceleration> dataHistory = new Queue<acceleration>();
// グラフ構成用。
Series[] series = new Series[3];
public frmMain()
{
InitializeComponent();
// シリアルポートの実装状況を取得。
ManagementClass mcW32SerialPort = new ManagementClass("Win32_SerialPort");
foreach (ManagementObject aSerialPort in mcW32SerialPort.GetInstances())
{
ComPortTable.Add(
(string)aSerialPort.GetPropertyValue("Caption"),
(string)aSerialPort.GetPropertyValue("DeviceID"));
}
string[] ports = SerialPort.GetPortNames();
foreach (string port in ports)
{
if (!ComPortTable.ContainsValue(port)) {
ComPortTable.Add(port, port);
}
}
}
private void drawChart(Chart chart)
{
try
{
chart.Series.Clear();
for (int i = 0; i < 3; i++)
{
series[i].Points.Clear();
foreach(acceleration acc in dataHistory)
{
series[i].Points.Add(new DataPoint(0, acc.getValue(i)));
}
}
for (int i = 0; i < 3; i++ )
{
chart.Series.Add(series[i]);
}
}
catch (Exception ex)
{
string errMsg = "@showChart(): ";
errMsg += ex.Message;
Console.WriteLine(errMsg);
MessageBox.Show(errMsg);
}
}
private void frmMain_Load(object sender, EventArgs e)
{
int i;
// 画面の構成。
// シリアルポートのコンボボックス構成。
cmbPortNo.Items.Clear();
foreach (string aPortCaption in ComPortTable.Keys)
{
cmbPortNo.Items.Add(aPortCaption);
}
if (ComPortTable.Count > 0)
{
cmbPortNo.SelectedIndex = 0;
}
else
{
MessageBox.Show("シリアルポートが存在しません。");
}
// シリアルポート「接続」「切断」ボタン。
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
// グラフ表示領域の構成。
chart.ChartAreas[0].AxisX.MinorGrid.Enabled = true;
chart.ChartAreas[0].AxisY.Minimum = 0;
chart.ChartAreas[0].AxisY.Maximum = 1023;
// グラフ要素の初期化。
for (i = 0; i < 3; i++)
{
series[i] = new Series();
series[i].ChartType = SeriesChartType.FastLine;
series[i].IsVisibleInLegend = false;
}
// グラフ線色設定。
series[0].Color = Color.Red;
series[1].Color = Color.Green;
series[2].Color = Color.Blue;
// チャート表示用データのクリア。
while (dataHistory.Count <= MAX_DATA_COUNT)
{
dataHistory.Enqueue(new acceleration(0, 0, 0));
}
// タイマーを1秒(1000ms)周期に初期化。
timer.Interval = 1000;
timer.Enabled = true;
}
// 接続ボタンのハンドラ。
private void btnConnect_Click(object sender, EventArgs e)
{
try {
string comPort = ComPortTable[cmbPortNo.SelectedItem.ToString()];
if (comPort != null)
{
// COMポート接続。
serialPort.PortName = comPort;
if (serialPort.IsOpen) {
MessageBox.Show("既に" + comPort +"はオープンされています。");
} else {
serialPort.BaudRate = 9600;
serialPort.Parity = System.IO.Ports.Parity.None;
serialPort.StopBits = System.IO.Ports.StopBits.One;
serialPort.DataBits = 8;
serialPort.Open();
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
}
}
} catch (Exception ex) {
if (serialPort.IsOpen) serialPort.Close();
string errMsg = "@btnConnect_Click(): ";
errMsg += ex.Message;
Console.WriteLine(errMsg);
MessageBox.Show(errMsg);
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
}
}
// 切断ボタンのハンドラ。
private void btnDisconnect_Click(object sender, EventArgs e)
{
try
{
if (serialPort.IsOpen)
{
serialPort.Close();
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
}
} catch (Exception ex) {
string errMsg = ex.Message;
Console.WriteLine(errMsg);
MessageBox.Show(errMsg);
}
}
// シリアルポート受信ハンドラ。
private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
byte[] rcvData = new byte[8];
ushort adX, adY, adZ;
//int readCount;
try
{
//serialPort.Read(rcvData, 0, 1);
rcvData[0] = (byte)serialPort.ReadByte();
if (rcvData[0] == 0x02)
{
//readCount = serialPort.Read(rcvData, 1, 7);
// ↑ 7byte取得に設定しても5~6byteのみ取得する。受信が間に合っていない?
// ↓ 1byteずつ同期受信に変更。
for (int i = 1; i < 8; i++)
{
rcvData[i] = (byte)serialPort.ReadByte();
}
if (rcvData[7] == 0x03)
{
// コマンド解析。
adX = (ushort)((((ushort)rcvData[1]) << 8) + (ushort)rcvData[2]);
adY = (ushort)((((ushort)rcvData[3]) << 8) + (ushort)rcvData[4]);
adZ = (ushort)((((ushort)rcvData[5]) << 8) + (ushort)rcvData[6]);
// キューに登録。
dataHistory.Enqueue(new acceleration((int)adX, (int)adY, (int)adZ));
}
}
}
catch (Exception ex)
{
string errMsg = ex.Message;
Console.WriteLine(errMsg);
MessageBox.Show(errMsg);
}
}
// タイマのハンドラ。
private void timer_Tick(object sender, EventArgs e)
{
// キューの最大値を超えたら、古いものから削除。
while (dataHistory.Count > MAX_DATA_COUNT)
{
dataHistory.Dequeue();
}
// グラフの再描画。
drawChart(chart);
}
}
}
相変わらず、使い道とか考えてないですが、まあ、こういうことをやること自体が楽しいっていうことで。