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:
- まず、現在のWSLのバージョンとディストリビューションを確認します。以下のコマンドを実行します:
wsl --list --verbose
- 次に、変更したいディストリビューションを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環境で利用することができ、無事に通信プログラムがテストできました。めでたし(^^)/