ArduinoとPythonをシリアル通信で連携させ、センサーデータをリアルタイムでグラフ表示する方法をゼロから解説します。「数値を眺めるだけじゃつまらない」——そんな気持ち、すごくわかります。せっかくハードウェアを動かしているのに、シリアルモニターの数字だけじゃ達成感が半分以下ですよね。
この記事では、ArduinoとPythonのシリアル通信の仕組みから、pyserialによるデータ受信、matplotlibを使ったリアルタイム可視化まで一気に通して解説します。IoT入門として最初の一歩にぴったりの内容です。ぜひ手を動かしながら読み進めてみてください。
必要な部品・環境
- Arduino(UnoやNanoなど何でもOK)
- 温度センサー(今回はLM35を使用)
- USBケーブル
- Python 3.x がインストールされたPC
Pythonのバージョンは3.8以上を推奨します。pyserialおよびmatplotlibはpipでインストールできます(後述)。Windowsでもmacでも同じ手順で動作確認済みです。
ArduinoとPythonがシリアル通信で繋がる仕組み
まず「なぜArduinoとPythonがシリアル通信でデータをやり取りできるのか」を簡単に押さえておきましょう。
ArduinoをUSBでPCに繋ぐと、PC側には仮想COMポート(シリアルポート)が作られます。Arduino側はこのポートにデータをテキスト形式で送信でき、Python側はpyserialというライブラリを使ってそのデータをリアルタイムに受け取れます。
イメージとしては「ArduinoがUSB経由でPCにメモを渡し続けて、Pythonがそのメモを読み続ける」感じです。これがArduino Python連携の基本となるシリアル通信の仕組みです。
通信速度はボーレート(baud rate)と呼ばれる数値で管理されており、Arduino側とPython側で必ず同じ値を設定する必要があります。今回は一般的な9600を使用します。
STEP 1:Arduinoのスケッチ(温度センサーのデータをシリアル送信)
まずArduino側のプログラムです。LM35温度センサーのアナログ値を読み取って、シリアルポートに送り続けるだけのシンプルな内容です。
// 温度センサー(LM35)のデータをシリアル送信するスケッチ
const int sensorPin = A0; // LM35をA0ピンに接続
void setup() {
Serial.begin(9600); // ボーレートを9600に設定(Python側と揃える)
}
void loop() {
int rawValue = analogRead(sensorPin); // アナログ値を0〜1023で取得
// LM35は10mV/℃の特性を持つため、電圧→温度に変換
float voltage = rawValue * (5.0 / 1023.0);
float temperature = voltage * 100.0;
// 温度をシリアルポートに1行ずつ送信
Serial.println(temperature);
delay(500); // 0.5秒ごとに送信
}
ポイント:Serial.begin(9600)のボーレートは、後述するPython側の設定と必ず一致させてください。値が違うと文字化けしたデータが届き、Pythonでの変換時にエラーが発生します。
スケッチを書き込んだら、まずArduino IDEのシリアルモニターを開いて温度の数値(例:25.34)が0.5秒おきに流れてくることを確認しましょう。この動作確認を先に済ませておくと、Python側でトラブルが起きたときの切り分けが楽になります。
STEP 2:PythonでArduinoのシリアルデータを受信する(pyserial)
次にPython側の準備です。まずpyserialをインストールします。pyserialはPythonからシリアルポートを操作するための定番ライブラリです。
pip install pyserial
インストールできたら、まずデータを受信してターミナルに表示するだけのシンプルなスクリプトを動かしてみましょう。可視化の前に「ちゃんとデータが来ているか」を確認するためのステップです。
import serial
import time
# COMポートは環境に合わせて変更してください
# Windowsなら 'COM3' など、Macなら '/dev/cu.usbmodem...' など
SERIAL_PORT = 'COM3'
Baud_RATE = 9600
try:
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
print(f"{SERIAL_PORT} に接続しました。データ受信を開始します...")
time.sleep(2) # Arduino起動待ち(重要:これを省くと最初のデータが壊れる場合がある)
while True:
line = ser.readline().decode('utf-8').strip()
if line:
temperature = float(line)
print(f"温度: {temperature:.1f} ℃")
except KeyboardInterrupt:
print("\n終了しました")
except serial.SerialException as e:
print(f"接続エラー: {e}")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
print("シリアルポートを閉じました")
time.sleep(2)はArduinoのリセット完了を待つための待機処理です。これを省略すると最初の数バイトが欠けることがあるので、必ず入れておきましょう。COMポートが分からない場合は、Arduino IDEの「ツール → ポート」から確認できます。
ターミナルに「温度: 25.3 ℃」などの数値が流れてくれば、ArduinoとPythonのシリアル通信は成功です。次のステップでこのデータをリアルタイムにグラフ化します。
STEP 3:matplotlibでセンサーデータをリアルタイムにグラフ表示する
いよいよメインの可視化パートです。matplotlibのアニメーション機能を使って、受信した温度データをリアルタイムで折れ線グラフに描画します。まずmatplotlibをインストールします。
pip install matplotlib
以下がリアルタイム可視化の完成スクリプトです。
import serial
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from collections import deque
# --- 設定 ---
SERIAL_PORT = 'COM3' # 環境に合わせて変更
BAUD_RATE = 9600
MAX_POINTS = 60 # グラフに表示するデータ点数(直近60件)
# --- シリアル接続 ---
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
time.sleep(2) # Arduino起動待ち
# --- データバッファ ---
temperatures = deque([0.0] * MAX_POINTS, maxlen=MAX_POINTS)
# --- グラフの初期設定 ---
fig, ax = plt.subplots(figsize=(10, 4))
line, = ax.plot([], [], 'r-', linewidth=1.5, label='温度 (℃)')
ax.set_xlim(0, MAX_POINTS)
ax.set_ylim(0, 50) # 表示温度範囲:0〜50℃(必要に応じて変更)
ax.set_xlabel('経過時間(サンプル数)')
ax.set_ylabel('温度 (℃)')
ax.set_title('Arduino LM35 リアルタイム温度モニタ(Python / matplotlib)')
ax.legend(loc='upper right')
ax.grid(True, linestyle='--', alpha=0.5)
# --- アニメーション更新関数 ---
def update(frame):
"""FuncAnimationから呼び出される更新関数"""
if ser.in_waiting > 0:
try:
raw = ser.readline().decode('utf-8').strip()
if raw:
temp = float(raw)
temperatures.append(temp)
except (ValueError, UnicodeDecodeError):
pass # 変換失敗時はスキップ
line.set_data(range(MAX_POINTS), list(temperatures))
# 最新値をタイトルにも表示
ax.set_title(
f'Arduino LM35 リアルタイム温度モニタ '
f'現在: {temperatures[-1]:.1f} ℃'
)
return line,
# --- アニメーション開始 ---
try:
ani = animation.FuncAnimation(
fig, update,
interval=500, # 500ms ごとに update() を呼び出す
blit=True,
cache_frame_data=False
)
plt.tight_layout()
plt.show()
finally:
ser.close()
print("シリアルポートを閉じました")
コードの要点を整理します。
- deque(両端キュー):
maxlen=MAX_POINTSを指定することで、古いデータを自動的に捨てながら最新N件だけを保持します。リストより効率的です。 - FuncAnimation:指定した間隔(ここでは500ms)で
update()関数を繰り返し呼び出し、グラフを再描画します。matplotlibでリアルタイム表示を行う標準的な方法です。 - blit=True:変化した部分だけを再描画することで、CPU負荷を下げてグラフをスムーズに更新します。
- 例外処理(ValueError / UnicodeDecodeError):シリアル通信では稀にデータが乱れることがあります。try/exceptで囲んでスキップすることで、スクリプトが途中で落ちるのを防ぎます。
よくあるエラーと対処法
SerialException: could not open port
COMポート名が間違っているか、Arduino IDEやシリアルモニターが同じポートを占有しています。Arduino IDEのシリアルモニターを閉じてから実行してください。Windowsのデバイスマネージャー(またはmacのls /dev/cu.*)でポート名を確認しましょう。
ValueError: could not convert string to float
Arduinoが起動直後に送信する不完全なデータが原因であることがほとんどです。time.sleep(2)を忘れずに入れてください。また、Arduino側のボーレートとPython側のBAUD_RATEが一致しているかも確認してください。
グラフウィンドウが固まる / 更新されない
Jupyter Notebookから実行している場合、%matplotlib notebookマジックコマンドが必要です。通常のPythonスクリプト(.py)としてターミナルから実行するのが最も安定します。
応用:データをCSVに保存しながら可視化する
リアルタイム表示と並行して、データをCSVに保存しておくと後の分析に便利です。update()関数内に以下の処理を追加するだけで対応できます。
import csv
from datetime import datetime
# ファイルをオープン(追記モード)
csv_file = open('temperature_log.csv', 'a', newline='', encoding='utf-8')
writer = csv.writer(csv_file)
writer.writerow(['timestamp', 'temperature']) # ヘッダー(初回のみ手動で書く場合)
# update()関数内、temp取得後に追加
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
writer.writerow([timestamp, temp])
csv_file.flush() # バッファを即時書き出し
保存したCSVはpandasで読み込んで分析したり、Excelで開いてグラフ化したりできます。IoTデータ活用の次の一歩として、ぜひ試してみてください。
まとめ:Arduino Python シリアル通信 + リアルタイム可視化の全体像
この記事でやったことを整理します。
- ✅ LM35温度センサーの値をArduinoで読み取り、シリアルポートに送信
- ✅ Pythonのpyserialでシリアルデータを受信・デコード
- ✅ matplotlibのFuncAnimationでリアルタイムグラフを描画
- ✅ エラー処理・CSV保存など実用的なポイントも網羅
「Arduinoからデータを取る → Pythonで受け取る → 可視化する」というこの流れは、IoTプロジェクトの基本パターンです。センサーを湿度センサー(DHT11)や照度センサー(フォトレジスタ)に変えても、ほぼ同じコードで動かせます。まずは今回のコードをそのままコピーして、動く状態を手元に作ることから始めてみてください。
次のステップとして、取得したデータをWebブラウザ上に表示したい方は「PythonとFlaskでIoTダッシュボードを作る方法」も参考にしてみてください。
よくある質問(FAQ)
Q. Arduino UnoとNanoで動作の違いはありますか?
A. スケッチのコードは同じで動きます。COMポート番号がArduino IDEで変わるだけです。
Q. LM35以外のセンサー(DHT11など)でも使えますか?
A. はい。Arduino側のスケッチをセンサーに合わせて変更し、Serial.println()で数値を送信する部分は同じです。Python側はほぼ変更不要です。
Q. Raspberry Piでも同じ方法は使えますか?
A. 使えます。Raspberry PiにPythonとpyserialをインストールし、シリアルポート名を/dev/ttyUSB0などに変更するだけで動作します。
