PythonからArduinoをシリアル通信で制御しようとしたとき、PySerialで「PermissionError: [Errno 13] Permission denied」(アクセスが拒否されました)というエラーに悩まされた経験はありませんか?

この記事では、Windows環境でPySerialを使ってArduinoと通信する際に発生するPermissionErrorの主な原因と具体的な解決手順をまとめています。完全に同じ状況でなくても、参考になる情報がきっと見つかるはずです。

私はArduino(アルドゥイーノ)が好きで、ロボット制御や簡単なIoTシステムを個人で構築しています。「PythonからArduinoを制御できる」と聞いてチャレンジしたところ、最初のLチカ(LEDチカチカ)でいきなりつまずきました笑。

色々と調べた結果たどり着いた解決策を、同じエラーで困っている方のために公開します。あくまで一つの解決策ですが、お役に立てれば幸いです。


実行環境

まず、この記事で使用した環境を紹介します。バージョンが多少異なっても同様の手順で解決できる場合がほとんどです。

  • OS:Windows 10 / Windows 11
  • Arduino IDE:2.1.0
  • Arduino本体:UNO R3 および UNO R4 MINIMA
  • Python:3.9
  • PySerial:3.5

Arduino本体はUNO R3とUNO R4 MINIMAの両方で確認しましたが、どちらでも同様の手順で対応できました。


エラーの概要:PySerialでPermissionErrorが発生する状況

以下のようなPythonコードでArduinoに接続しようとしたときに、PermissionErrorが発生しました。

import serial
import time

# COMポート番号は環境に合わせて変更してください
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2)  # Arduino起動待ち

ser.write(b'H')  # LEDをONにする信号を送信
print("送信完了")
ser.close()

このコードを実行すると、次のようなエラーが表示されます。

PermissionError: [Errno 13] Permission denied: 'COM3'

日本語環境では「アクセスが拒否されました」と表示されることもあります。同じCOMポートに別のプログラムが接続している場合に起きやすいエラーです。


PermissionErrorの主な原因

PySerialでCOMポートへのアクセスが拒否される原因はいくつかあります。順番に確認していきましょう。

原因1:Arduino IDEのシリアルモニタが開いたままになっている

最もよくある原因です。Arduino IDEのシリアルモニタが起動中はCOMポートが占有されるため、Pythonから同じポートに接続しようとするとPermissionErrorが発生します。

【確認・解決手順】

  1. Arduino IDEを開き、右上の「シリアルモニタ」アイコンをクリックしてシリアルモニタウィンドウを閉じる
  2. それでも解決しない場合は、Arduino IDEを完全に終了する
  3. 再度Pythonスクリプトを実行する

私の場合、これが原因でした。シリアルモニタを開いてデバッグしたあと、閉じ忘れたままPythonを実行していたのです。

原因2:前回のPythonスクリプトがポートを解放していない

Pythonスクリプトを途中で強制終了した場合、ser.close()が実行されずにCOMポートが占有されたままになることがあります。

【解決策】
Pythonではwith文を使うことで、スクリプトが終了・例外発生時にも自動的にポートが閉じられます。

import serial
import time

# with文を使うとポートの閉じ忘れを防げる
with serial.Serial('COM3', 9600, timeout=1) as ser:
    time.sleep(2)  # Arduino起動待ち
    ser.write(b'H')
    print("送信完了")
# withブロックを抜けると自動的にser.close()が呼ばれる

根本解決にはなりませんが、PCを再起動するとポートの占有状態がリセットされることが多いです。

原因3:他のアプリケーションがCOMポートを使用中

Arduino IDE以外にも、以下のようなアプリケーションがCOMポートを占有している可能性があります。

  • Arduino IDEの旧バージョン(1.x系)が同時に起動している
  • PlatformIOなどの他の開発環境
  • Tera Termやその他のシリアル通信ソフト
  • Windowsのデバイスマネージャーが通信中

タスクマネージャーを開き、不要なプロセスが起動していないか確認してみてください。

原因4:COMポート番号の指定が間違っている

指定しているCOMポートが存在しない場合も似たようなエラーになることがあります。正しいCOMポートを確認する方法は以下の通りです。

  1. Windowsの「デバイスマネージャー」を開く(スタートボタンを右クリック→デバイスマネージャー)
  2. 「ポート(COMとLPT)」を展開する
  3. 「Arduino Uno (COM○)」のように表示されているCOM番号を確認する
  4. Pythonコードのserial.Serial('COM○', ...)の番号と一致させる

PySerialで接続可能なポート一覧を取得するには、以下のコードも便利です。

import serial.tools.list_ports

ports = serial.tools.list_ports.comports()
for port in ports:
    print(port.device, port.description)

このコードを実行すると、接続されているCOMポートの一覧が表示されます。Arduinoが認識されているか確認しましょう。


解決手順のまとめ:チェックリスト


PermissionErrorが発生したときは、以下の順番で確認してみてください。

  1. Arduino IDEのシリアルモニタを閉じる(最頻出の原因)
  2. Arduino IDEを完全に終了する
  3. 他のシリアル通信ソフトを終了する
  4. デバイスマネージャーでCOMポート番号を確認する
  5. PCを再起動してポートの占有状態をリセットする
  6. Pythonコードをwith文で書き直してポートの解放漏れを防ぐ

動作確認:Lチカのサンプルコードでテストしてみよう


PermissionErrorが解決したら、実際にLチカ(LED点滅)で動作確認をしてみましょう。Arduino側とPython側それぞれのコードを紹介します。

Arduino側のスケッチ(Lチカ制御)

// Arduino側:シリアル通信でLEDを制御するスケッチ
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);  // 内蔵LED(pin13)を出力に設定
  Serial.begin(9600);            // ボーレート9600で通信開始
}

void loop() {
  if (Serial.available() > 0) {
    char command = Serial.read();
    if (command == 'H') {
      digitalWrite(LED_BUILTIN, HIGH);  // LEDをON
      Serial.println("LED ON");
    } else if (command == 'L') {
      digitalWrite(LED_BUILTIN, LOW);   // LEDをOFF
      Serial.println("LED OFF");
    }
  }
}

Python側のコード(PySerialでLチカ)

import serial
import time

# COM番号はデバイスマネージャーで確認したものに変更してください
COM_PORT = 'COM3'
BAUD_RATE = 9600

with serial.Serial(COM_PORT, BAUD_RATE, timeout=1) as ser:
    time.sleep(2)  # Arduino起動待ち(重要:省略するとうまく動かないことがある)
    
    for i in range(5):
        ser.write(b'H')       # LED ON
        response = ser.readline().decode('utf-8').strip()
        print(f"Arduino応答: {response}")
        time.sleep(1)
        
        ser.write(b'L')       # LED OFF
        response = ser.readline().decode('utf-8').strip()
        print(f"Arduino応答: {response}")
        time.sleep(1)

print("Lチカ完了!")

このコードでLEDが5回点滅すれば、PySerialによるArduinoとの通信が正常に動作しています。


まとめ

今回はPySerialでPermissionError「アクセスが拒否されました」が発生したときの解決方法を解説しました。

最も多い原因は「Arduino IDEのシリアルモニタが開いたままになっている」ことです。シリアルモニタを閉じるだけで解決するケースが非常に多いので、まず最初に確認してみてください。

それでも解決しない場合は、本記事のチェックリストを上から順番に試してみてください。PySerialとArduinoを組み合わせることで、Pythonから物理デバイスを制御する世界が広がります。ぜひ快適な開発環境を整えてチャレンジしてみてください!

他にもPythonやArduinoに関する記事を書いていますので、よろしければあわせてご覧ください。