Ubuntu環境向けに制御ソフトウェアを作ることになったので、その一環としてシリアル通信のCプログラムをデバッグしようとしてたら、結構つまずいたのでその克服ナレッジを記録。

[環境]

  • 開発環境OS:Windows 11
  • IDE: Visual Studio Community 2022(Linux向けCMakeプロジェクト)
  • プログラミング言語: C/C++
  • ターゲット Ubuntu 20.04@WSL

このような環境で、シリアル通信プログラムのデバッグをWSL上のUbuntuでやろうとしておおよそ以下のようなプログラムを作った。(Windows上のCOM1 を使うので ttyS1 が対象デバイスとなるつもりで・・・)

    //シリアルポートを開く
    int serial_port = open("/dev/ttyS1", O_RDWR);

    //シリアルポートの設定
    struct termios tty;
    if(tcgetattr(serial_port, &tty) != 0)
    {   //設定データ取得失敗
        printf("Error %i from tcgetattr: %s\n", errno, strerror(errno));
        return 1;
    }

    tty.c_cflag &= ~PARENB; // パリティビットをクリアし、パリティなしに設定
    tty.c_cflag &= ~CSTOPB; // ストップビットをクリアし、1つのストップビットに設定
    tty.c_cflag |= CS8;     // 8ビットの文字サイズを設定
    tty.c_cflag &= ~CRTSCTS;// ハードウェアフロー制御を無効化
    tty.c_cflag |= CREAD | CLOCAL; // 受信を有効化し、モデム制御を無視

    tty.c_lflag &= ~ICANON; // カノニカルモードを無効化
    tty.c_lflag &= ~ECHO;   // エコーを無効化
    tty.c_lflag &= ~ECHONL; // 改行エコーを無効化
    tty.c_lflag &= ~ISIG;   // シグナル文字を無効化

    tty.c_iflag &= ~(IXON | IXOFF | IXANY); // ソフトウェアフロー制御を無効化
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL); // 特殊入力処理を無効化

    tty.c_oflag &= ~OPOST; // 特殊出力処理を無効化
    tty.c_oflag &= ~ONLCR; // 改行変換を無効化

    // ボーレートの設定
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    // シリアルポートの設定を保存
    if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
        printf("Error %i from tcsetattr: %s\n", errno, strerror(errno));
        return 1;
    }
    
    //とりあえず送信
    char* message="Hello\r";
    int result = write(serial_port,message,sizeof(message));
    :
    :
    //シリアルポートを閉じる
    close(serial_port)

これを実行すると、open(“/dev/ttyS1”,O_RDWR)でのファイルディスクリプタは正常に取れている様だが、その後どーしてもtcgetattr(serial_port,&tty)のとこで失敗する。errnoは5、Device I/O Error とのことだ。

デバイス名をttyS0に変えてみたりとか色々試すけど一向にうまくいかないのでcopilotに訊いてみる。

申し訳ありませんが、現在のところWSL2ではオンボードのシリアルデバイスを直接扱うことはできません1234。これは、WSL2が仮想マシン内で動作しているため、Win32側のデバイスに直接アクセスすることができないからです1234

しかし、一部のUSBデバイスについては、usbipd-winというツールを使用することで、WSL2からWin32側に接続しているUSBデバイスを利用することが可能です1234。このツールは、USBデバイスをWindowsホスト側で使うかWSL2側で使うかを切り替えることができます1234

それでも問題が解決しない場合は、さらに詳しい情報を提供していただけますか?それにより、より具体的な解決策を提供することができます。1

copilotより回答

つまり、オンボードのシリアルポートはWSL2では使えないと言うのだ。これをどうしても使いたい場合はWSL2をWSL1にバージョンダウンする必要があるという訳である。

WSLのバージョンを変更する方法もCopilotに訊いてみた

はい、WSL2をWSL1に変更することは可能です。以下の手順で行うことができます123

  1. まず、現在のWSLのバージョンとディストリビューションを確認します。以下のコマンドを実行します:
wsl --list --verbose
  1. 次に、変更したいディストリビューションをWSL1に設定します。以下のコマンドを実行します(<ディストリビューション名>は確認したディストリビューションの名前に置き換えてください):
wsl --set-version <ディストリビューション名> 1

これらの手順を試してみてください。それでも問題が解決しない場合は、さらに詳しい情報を提供していただけますか?それにより、より具体的な解決策を提供することができます。4

Copilotより回答

で、実際やってみると・・・

> wsl --list --verbose
NAME STATE VERSION
* Ubuntu Stopped 2
Ubuntu-20.04 Stopped 2

はい現在2つのUbuntuがWSL上にインストールされ、いずれもWSLバージョンは「2」となっています。今回はターゲットがUbuntu-20.04なので、これをWSL1へ変更してみます

> wsl --set-version Ubuntu-20.04 1
変換中です。これには数分かかる場合があります。
この操作を正しく終了しました。
> wsl --list --verbose
NAME STATE VERSION
* Ubuntu Stopped 2
Ubuntu-20.04 Stopped 1

うまく行きました。で、この操作がうまく行かない環境もあるみたいで、その場合は、

コントロールパネル→プログラム→Windowsの機能の有効化または無効化

↑Linux用Windowsサブシステム の所にチェックを入れてインストール→PC再起動すると大抵解決するはずですが、何せ未確認w

私の環境ではWSL1の環境にすることで、オンボードのシリアルポートがWSL環境で利用することができ、無事に通信プログラムがテストできました。めでたし(^^)/