Androidアプリ開発

USBシリアル通信をAndroidで実装する

この記事は約28分で読めます。
記事内に広告が含まれています。
スポンサーリンク

この記事は Android スマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Java での開発経験、XML 構文規則、Android のアプリ開発経験がある方を対象としています。
Android のアプリ開発でお役にたててれば、嬉しいです。
(これから Android のアプリ開発や Java での開発を始めたい方への案内は、
記事の最後で紹介します)

この記事のテーマ


 USBシリアル通信をusb-serial-for-androidで実装する

Android3.1以降でUSBホストモードがサポートされました。
この機能を使用すれば、接続したUSBデバイスとデータ受送信などシリアル通信の実装が可能です。

USBホストとアクセサリの概要

センサー自体はRS-232Cなどのシリアル通信で行い、通信を中継するUSBシリアル変換をもつUSBデバイスが存在します。この場合、通信相手はセンサーモジュールではなく、USB変換モジュールです。

USB変換モジュールには、FTDI、CP2102、CH340、PL2303などが存在します。
それぞれのモジュールにドライバーが存在し、アプリで個々に対応するのかなり面倒です。
usb-serial-for-androidは、モジュールの種類を意識することなく透過的にシリアル通信を可能とするライブラリです。

usb-serial-for-androidは、Androidで使用できるオープンソフトウェアライブラリです。

USB変換モジュールは、PL2303GC(Prolific)です。

usb-serial-for-androidを使用するための準備

usb-serial-for-androidを使用するには、
プロジェクトおよび、モジュールのbuild.gradleファイル、settings.gradleに定義の追加が必要です。

◎build.gradle(プロジェクト)

:
allprojects {
    repositories {
        :
        maven { url 'https://jitpack.io' }
    }
}
:

◎build.gradle(モジュール)
2024年10月現在の最新バージョンは 3.8.0 です。

dependencies {
    :
    implementation 'com.github.mik3y:usb-serial-for-android:3.8.0'
}

◎settings.gradle

:
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
     :
        maven { url 'https://jitpack.io' }
    }
}
:

デバイス接続でアプリを起動したい場合、device_filter.xmlの作成とAndroidManifest.xmlに定義を追加します。詳細はこちら↓↓↓

https://github.com/mik3y/usb-serial-for-android

◎ライセンス表記
usb-serial-for-androidは、MIT Licenseです。
アプリで使用する場合、ライセンス表記が必要です。

ライセンス条文の記載がある公式サイトのリンクをアプリに実装しています

USBシリアル通信の実装

USBデバイスの接続とシリアル通信に分けて、USBシリアル通信を実装します。
USBデバイスの接続はUSBホストAPI、シリアル通信はusb-serial-for-androidを使用します。
サンプルは、サービスで実装しています。

USBホストの概要

USBデバイスの接続

USBデバイスの接続は、USBホストAPIで実装します。
UsbManagerを使用して、USBに接続しているデバイスを取得し、権限チェックを行います。
権限がない場合は、ユーザ承認リクエストを発行します。
権限がある、ユーザ承認リクエストでOKの場合は、シリアル通信の接続を生成します。

import com.hoho.android.usbserial.driver.FtdiSerialDriver;
import com.hoho.android.usbserial.driver.ProbeTable;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.hoho.android.usbserial.util.SerialInputOutputManager;
:
public class USBConnectService extends Service {
    private static final String ACTION_USB_PERMISSION = "com.jiseifirm.mls.service.LocationService.USB_PERMISSION";
    private UsbManager  usbManager;
    private UsbDevice   device;
    private Connect     connect;
    private int         PORT = 0;
  :
    @Override
    public void onCreate() {
        super.onCreate();
        // マルチポートのUSBに接続している場合、シリアル通信のポート(PORT)を指定
        :
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        :
        usbConnect();
        return START_NOT_STICKY;
    }

    private void usbConnect() {
	    usbManager = (UsbManager) context.getSystemService(Service.USB_SERVICE);
        // 接続するUSBデバイスのベンダーID(vender)とプロダクトID(product)
        device = attachedDevice(vender, product);
        if (device != null) {
            if (usbManager.hasPermission(device)) {
                // シリアル通信の接続を生成       
                //noinspection InstantiatingAThreadWithDefaultRunMethod
                connect = new Connect(device);
            } else {
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION).setPackage(getPackageName()), PendingIntent.FLAG_MUTABLE);
                IntentFilter intentFilter = new IntentFilter(ACTION_USB_PERMISSION);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                    registerReceiver(usbReceiver, intentFilter, RECEIVER_EXPORTED);
                } else {
                    registerReceiver(usbReceiver, intentFilter);
                }
                usbManager.requestPermission(device, pendingIntent);
            }
        } else {
            // 接続できるデバイスがない場合の処理
            :
        }
        :

    // USBデバイスの取得
    private UsbDevice attachedDevice(int vender, int product) {
        try {
            for (UsbDevice usbDevice : usbManager.getDeviceList().values()) {
                if (usbDevice.getVendorId() == vender & usbDevice.getProductId() == product) {
                    return usbDevice;
                }
            }
        } catch (Exception e) {
            // エラー処理
            :
        }
        return null;
    }
    :

サンプルでは、接続するUSBデバイスのベンダーIDとプロダクトIDが一致するデバイスを取得しています。
取得したUSBデバイスの権限チェックは、UsbManagerhasPermissionで行います。
マルチポートのUSBに接続している場合、シリアル通信のポート(PORT)を指定します。

◎承認リクエストの結果確認

承認リクエストの結果確認はBroadcastReceiverで行います。

    // 承認リクエストの結果確認
    private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
                synchronized (this) {
                    unregisterReceiver(usbReceiver);
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        // OKの場合の処理
                        :
                    } else {
                        // キャンセルの場合の処理
                        :
                    }
                }
            }
        }
    }
    :


ユーザ承認リクエストの発行はPendingIntentを行いますが、PendingIntent生成時にFLAG_MUTABLEを指定しないと、Intentに結果がセットされないので、注意が必要です。
詳しくは、USBデバイスの権限チェックとユーザ承認リクエストを参照ください。