ArduinoとRaspberry PiでUSBケーブルを使った通信を行う

Linux

はじめに

ArduinoとRaspberry PiでUSBケーブルを介した通信を行いたかったのでテストしたときのメモ。
このプログラムは、両方のデバイスがUSBデバイスがシリアルポートであることが条件です。
通信形式は、テキスト、またはバイナリのシリアルデータとなります。

構成

- Arduino UNO Rev.4
    - Arduino IDE 2.3.6

- Raspberry Pi 3
    - Raspberry Pi OS with desktop 64-bit(Release date: May 13th 2025)
    - Python 3.11.2

Raspberry PiのOS

下記からダウンロードし、Rufusで書き込みます。

Raspberry Pi OS downloads ??? Raspberry Pi
Raspberry Pi OS (previously called Raspbian) is our official, supported operating system.

Rufusは下記からダウンロードします。

Rufus - 起動可能なUSBドライブを簡単に作成できます
Rufus: Create bootable USB drives the easy way

Pythonの hid モジュールのインストール

HIDデバイスと簡易に通信できます。

sudo apt install libhidapi-dev libusb-1.0-0-dev build-essential python3-dev

Arduino UNO Rev.4の送信プログラム

「while (!Serial);」があるため、シリアル通信が有効にならないとプログラムが進みません。
そのため、プログラムを書き込んだら、以下を行います。

・Arduino IDEのシリアルモニターを開いてプログラムを進ませる。
・シリアル通信の重複を避けるため、Arduino IDEを終了する。

数字を送るプログラムです。

#include <FspTimer.h>

FspTimer timer;

// サンプリングレート (Hz)
// 1Hzの場合、1秒ごとに1回実行する。
const float sampleRate = 1.0;

int cnt = 0;

void timerCallback(timer_callback_args_t *) {
  // シリアル通信を使ったデータの送信
  Serial.print("Count: ");
  Serial.println(cnt);
  cnt++;
}

void setup() {
  // シリアル通信の設定
  Serial.begin(115200);
  while (!Serial);

  uint8_t timer_type;
  // 未使用のタイマーのチャネルと種別を取得する
  int8_t timer_ch = FspTimer::get_available_timer(timer_type);
  if (timer_ch >= 0) {
    // タイマーのパラメータを設定する
    // TIMER_MODE_PERIODIC: 周期タイマーを指定
    // timer_type: GitHubのFspTimer.cppを見るとGPT_TIMER(32ビット幅のカウンタ)の優先度が高く、AGT_TIMER(8ビットまたは16ビット幅のカウンタ)は低い
    // sampleRate: タイマー割込みの間隔(Hz)   割り込みごとにデータ1個を出力するので、サイン波の周波数と同義
    // timerCallback: タイマー割り込み発生時に呼ばれるコールバック関数
    //
    // FspTimer.cpp
    // https://github.com/arduino/ArduinoCore-renesas/blob/main/cores/arduino/FspTimer.cpp
    if (timer.begin(TIMER_MODE_PERIODIC, timer_type, timer_ch, sampleRate, 0.0f, timerCallback, nullptr)) {
      // タイマオーバーフロー割込みを有効にする
      timer.setup_overflow_irq();
      // タイマーのチャネルを有効にする
      timer.open();
      // タイマーを開始する
      timer.start();
    }
  }
}

void loop() {
  // メインループでは何もしない - タイマー割り込みで処理
}

ArduinoとRaspberry PiをUSBケーブルで接続したときのdmesgのメッセージ

Raspberry Piでは、以下のように認識されました。

・Arduinoのベンダー番号:2341
・プロダクト番号:0069
・シリアルポート:ttyACM0

 [10066.910281] hwmon hwmon1: Undervoltage detected!
 [10067.098257] usb 1-1.4: new full-speed USB device number 14 using dwc_otg
 [10067.190494] usb 1-1.4: New USB device found, idVendor=2341, idProduct=0069, bcdDevice= 1.00
 [10067.190543] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
 [10067.190562] usb 1-1.4: Product: UNO R4 Minima
 [10067.190575] usb 1-1.4: Manufacturer: Arduino
 [10067.190589] usb 1-1.4: SerialNumber: 370E1F0759333032D98433324B572E63
 [10067.293035] cdc_acm 1-1.4:1.0: ttyACM0: USB ACM device
 [10067.293484] usbcore: registered new interface driver cdc_acm
 [10067.293520] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
 [10072.958271] hwmon hwmon1: Voltage normalised

Raspberry Piの受信プログラム

今回は受信のみなので、送信部はコメントしています。
受信した数字をコンソールに表示します。

input_port変数には、ArduinoとUSBケーブルで接続後にdmesgコマンドで確認したポート名「ttyACM0」を指定します。

import serial
import time

# USBポート(受信)
input_port = '/dev/ttyACM0'

# USB機器2台(送信)
#output_ports = ['/dev/ttyUSB1', '/dev/ttyUSB2']
#baudrate = 9600

# 初期化
try:
    ser_in = serial.Serial(input_port, baudrate, timeout=1)
    #ser_outs = [serial.Serial(p, baudrate, timeout=1) for p in output_ports]
except serial.SerialException as e:
    print(f"シリアルポートの初期化に失敗: {e}")
    exit(1)

print("USBデータミラーリング開始")

try:
    while True:
        if ser_in.in_waiting:
            data = ser_in.readline()
            print(f"[受信]: {data.decode(errors='ignore').strip()}")
            '''
            for out in ser_outs:
                try:
                    out.write(data)
                except serial.SerialException as e:
                    print(f"[送信エラー]: {e}")
            '''
        time.sleep(0.01)
except KeyboardInterrupt:
    print("\n終了")
finally:
    ser_in.close()
    for out in ser_outs:
        out.close()

Comments