====== ラズベリーパイ ======
組立I/Fシステムをラズベリーパイ上で動かす。
===== Linuxの基礎 =====
[[https://www.atmarkit.co.jp/ait/articles/0108/07/news002.html|各ディレクトリの役割を知ろう(ルートディレクトリ編)]]
===== ラズベリーパイ環境(実機) =====
HDMIをマイクロUSBの変換アダプターを使用してモニターに繋げる。\\
モニターのINPUTボタンにて、HDMI接続をできるようにしておく。
==== 実機スペック ====
型番 B03114\\
* https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
* https://troubletoday.net/
* [[https://pc-freedom.net/today_pc_story/linux-for-raspberry-pi/|Raspberry Pi で使える Linux 。]]
$ cat /proc/cpuinfo
Raspberry Pi 4 ARMv7 Processor rev 3 メモリ 2.0GB
* armhf … ARMv7 (32bit) までのプロセッサに対応
* arm64 … ARMv8 (64bit) からのプロセッサに対応
32-bit OS と 64-bit OSの両方あるが、今回の実機はARMv7 (32bit)を使用している。\\
[[https://coconala.com/blogs/1638666/131490|Raspberry Pi 32-bit OS と 64-bit OS の違いは?]]
==== USBメモリのアクセス ====
ラズパイ上では自動で認識してマウントしてくれる。
USBメモリが認識しないのでいろいろやってしまった。\\
* [[https://www.raspberrypirulo.net/entry/usbmemory-mount|Raspberry PiでUSBメモリをマウントする]]
* [[https://tutorialcrawler.com/hardware/usb-%E3%83%89%E3%83%A9%E3%82%A4%E3%83%96%E3%81%AE%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3/|Linux で USB ドライブをフォーマットする]]
LinuxでUSBメモリをフォーマットしたらWindows上で認識しなくなった場合の対応\\
[[https://www.toomath.net/entry/2019/02/01/usb-memory-diskpart|WindowsでUSBメモリを認識しないときの対処方法]]
最終的にUSBメモリをWindows上でNTFSでフォーマットしラズパイ上でも読み書きが出来るようになった。\\
※FAT32だとラズパイ側で読めるが書き込みが出来なかった。
==== USBメモリ内のファイルを実行可能にする ====
実行時に「許可がありません」が表示される。\\
USBメモリー上のファイルに「chmod +x 」としても実行可能パーミッションがつかないし、実行できない。
=== 原因 ===
>USBスティックのファイルシステムがFAT / VFATまたはNTFSの場合、デフォルトのマウントオプションはnoexecです。
>これは、UnixセキュリティモデルをサポートしていないUSBファイルシステムからシステムに不正なプログラムが到着するのを防ぐためです。
>簡単な対応としては、フォーマット形式をext2,3 or 4にする。
>https://forums.raspberrypi.com/viewtopic.php?t=99124
=== 対応方法1 ===
[[https://shimada-k.hateblo.jp/entry/20110623/1308816263|debianでUSBメモリを実行権付きでマウントするメモ]]
usbmountの設定を書き換えます。場所は/etc/usbmount/usbmount.confです。
MOUNTOPTIONS="sync,nodev,noatime,nodiratime" ←noexecを削除
FS_MOUNTOPTIONS="-fstype=ntfs,uid=pi,gid=pi" ←「""」内を追加
=== 対応方法2 ===
USBメモリ内のファイルを実行可能にするには、一苦労する。\\
[[https://qiita.com/h_nari/items/dc3f89c8c210d1081dc1|Raspberry PiでUSBメモリ内のファイルを実行可能にする方法]]
=== 対応方法3 ===
* [[https://sekisuiseien.com/computer/11054/|【Linux】UbuntuでNTFS形式のファイルで所有者や権限が変更する方法]]
* [[http://denshihakoma.blogspot.com/2013/08/ubuntuntfsusb.html|ubuntu上でntfsフォーマットUSBメモリ上のファイルに実行権限を付加する方法]]
* [[https://askubuntu.com/questions/11840/how-do-i-use-chmod-on-an-ntfs-or-fat32-partition|How do I use 'chmod' on an NTFS (or FAT32) partition?]]
※該当USBメモリ上ファイルのぜんぶに実行権限がつくようになる
==== 固定IP接続 ====
ノートPCとラズパイは、LANケーブル(ストレートケーブルでよい)を直接つないでも繋がる。\\
※LANケーブルの爪が折れていたりすると接続不良で接続できないので、ランプが着くのを確認すること
固定IPで192.168.0.101で接続する。変更後に「↑↓」マークをクリックすると数秒点滅後に点滅が止まり点灯した状態になる。\\
[[https://www.fabshop.jp/raspberry-pi-static-ip/|Raspberry Pi OSのIPアドレスの固定方法 GUIマウス操作で簡単に設定]]
ノートPC側は固定IPで192.168.0.100とする。ファイアーウォールは一時的に無効とするか、pingの応答を返すように設定する。\\
[[https://n-archives.net/software/nwol/articles/how-to-allow-ping-response-in-windows10/#i-1|Windows10 PCへのpingが通らない]]
pi@raspberrypi:~ $ ifconfig
eth0: flags=4163 mtu 1500
inet 192.168.0.101 netmask 255.255.255.0 broadcast 192.168.0.255
inet6 fe80::e0a:ab87:e066:407f prefixlen 64 scopeid 0x20
ether dc:a6:32:f2:61:93 txqueuelen 1000 (イーサネット)
RX packets 48 bytes 3904 (3.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 32 bytes 3766 (3.6 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73 mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10
loop txqueuelen 1000 (ローカルループバック)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
pi@raspberrypi:~ $ ping 192.168.0.100
PING 192.168.0.100 (192.168.0.100) 56(84) bytes of data.
64 bytes from 192.168.0.100: icmp_seq=1 ttl=128 time=1.08 ms
64 bytes from 192.168.0.100: icmp_seq=2 ttl=128 time=0.528 ms
* [[https://db.danman.jp/access-p2p-raspberrypi/|ピアツーピアでRaspberryPiとPCを有線接続]]
* [[https://yeswego.biz/iot/%E6%A5%BD%E3%81%97%E3%81%84-iot-%E8%87%AA%E4%BD%9C/%E3%83%A9%E3%82%BA%E3%83%91%E3%82%A4%EF%BC%94%E3%82%92lan%E3%82%B1%E3%83%BC%E3%83%96%E3%83%AB%E3%81%A7p2p-%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AF%E6%B0%97%E3%82%92%E4%BB%98/|ラズパイ4をLANケーブルでP2P 接続する場合は気を付けて]]
===== ラズベリーパイ環境(仮想) =====
Windows 10上でQEMUを使用することで、Raspberry Pi OSを動かすことが出来る。\\
※QEMUは、CPUエミュレーションをするためのソフトウェア
==== マシン名:versatilepb ====
[[https://www.kkaneko.jp/tools/container/windows_qemu.html|Windows で Raspbian システムを起動(QEMU, qemu-rpi-kernel を使用)]]\\
※2020年5月に正式名称がRaspbianからRaspberry Pi OSへ変更され、PCとMac向けのRaspbianについても、Raspberry Pi Desktopへと変更
上記サイトを参考に、最新版をインストールする。
* qemu-w64-setup-20210505.exe
* 2021-05-07-raspios-buster-armhf-full.img
* kernel-qemu-4.19.50-buster
* versatile-pb-buster.dtb
qemu以外は C:\RaspberryPiフォルダに格納\\
RaspberryPiOS起動用バッチを作成する。
cd "C:\Program Files\QEMU"
qemu-system-arm.exe -M versatilepb ^
-cpu arm1176 ^
-m 256 ^
-drive format=raw,file=C:\RaspberryPi\2021-05-07-raspios-buster-armhf-full.img ^
-net nic ^
-net user,hostfwd=tcp::2222-:22 ^
-dtb C:\RaspberryPi\versatile-pb-buster.dtb ^
-kernel C:\RaspberryPi\kernel-qemu-4.19.50-buster ^
-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" ^
-no-reboot
メモリを増やそうと -m 256 → 1Gだと下記エラーになる。\\
qemu-system-arm.exe: versatilepb: memory size must not exceed 256MB
※256MBしかメモリを割り当てられないQEMUの制限に対し、後述するスワップ領域を用意することで対処し、わりと多めのメモリを必要とする処理が可能になる。
=== マウス操作 ===
* Raspberry Pi OS内にマウス操作が移るがウィンドウタイトルバーにあるように「CTRL + ALT + G」で抜けれる。
* Raspberry Pi OSのウィンドウを最大化するとマウス操作がスムーズできる。
=== 起動時設定 ===
起動時設定については、言語設定以外はスキップでよい。\\
再度、Raspberry Pi OSを起動、Raspberry Pi OS上のターミナルで実行する。
^デフォルトの管理者^^
^ユーザー名|pi|
^パスワード|raspberry|
sudo apt install -y gnome-disk-utility ;
gnome-disks ;
ディスクユーティリティ(gnome-disks)が起動しますので、先程足した 2GB を拡張します。\\
=== 空き容量増加 ===
空き容量が570MBくらいしかないので下記サイトを参考に空き容量を増やす\\
[[https://qiita.com/mugimugi/items/eaf3721a50dc618c07ac|"Ubuntu で" Raspberry Pi を動かす QEMU]]
C:\Program Files\qemu>qemu-img resize C:\RaspberryPi\2021-05-07-raspios-buster-armhf-full.img +2G
WARNING: Image format was not specified for 'C:\RaspberryPi\2021-05-07-raspios-buster-armhf-full.img' and probing guessed raw.
Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
Specify the 'raw' format explicitly to remove the restrictions.
Image resized.
=== スワップ領域増加 ===
スワップ領域も少ないのでラズパイ上で下記コマンドで修正を行います。
sudo nano /etc/dphys-swapfile ;
/etc/dphys-swapfile
#CONF_SWAPSIZE=100
CONF_SWAPSIZE=1024
保存をして閉じたら下記コマンドでスワップファイルサイズの更新を行います。
sudo dphys-swapfile install ;
sudo dphys-swapfile swapon ;
=== アップデート ===
アップデートを行います。
sudo apt update
=== SSH ===
SSHとVNCを有効にする。\\
[[https://dev.classmethod.jp/articles/raspberry-pi-4-ssh-vnc-remote/|Raspberry Pi 4にSSHとVNCで接続してみた]]
Windows TerminalでSSHに接続する。
SSH pi@127.0.0.1 -p 2222
パスワード入力 raspberry
=== その他 ===
Visual Studio Codeをインストールするが、エラー「Illegal instruction」により動作せず。
$ sudo apt install code
$ code
Illegal instruction
動作しないのは「Unsupported architecture: armv6l」のため\\
>CPUのアーキテクチャがarmv7lのRaspberry Piが今回サポートされるようになりました。つまり、Raspberry Pi 2系統とRaspberry Pi 3系統はすべて実行可能です。Raspberry Pi Zero系統はCPUのアーキテクチャがarmv6となっているので現在は実行不可能です。
>[[https://raspi.taneyats.com/entry/remote-ssh-on-raspberrypi|Raspberry PiにVSCodeのRemote SSHで接続・開発する]]
==== マシン名:raspi2 ====
イメージファイルから「kernel7.img」と「bcm2709-rpi-2-b.dtb」を抽出する。\\
Windowsではイメージファイルをマウントできないため、抽出するにはLinuxを使用する。\\
WSL2からはext4ファイルをマウント出来るが、WSL1では対応していない。\\
WSL2が無い場合、VirtualBox内にUbuntuをインストールして抽出する。ファイル共有しておけばWindows側に持ってこれる。
* qemu-w64-setup-20210505.exe
* 2021-05-07-raspios-buster-armhf.img
* kernel7.img
* bcm2709-rpi-2-b.dtb
qemu以外は C:\RaspberryPi2フォルダに格納
先にイメージサイズの変更を行う。イメージサイズは2の冪乗を指定する。
"C:\Program Files\qemu\qemu-img.exe" resize 2021-05-07-raspios-buster-armhf.img 8G
RaspberryPiOS起動用バッチを作成する。
@echo off
cd /d %~dp0
"C:\Program Files\qemu\qemu-system-arm.exe" ^
-m 1024 ^
-M raspi2 ^
-kernel kernel7.img ^
-dtb bcm2709-rpi-2-b.dtb ^
-drive format=raw,file=2021-05-07-raspios-buster-armhf.img ^
-append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4 dwc_otg.fiq_fsm_enable=0" ^
-serial stdio ^
-no-reboot ^
-device usb-kbd ^
-device usb-tablet ^
-device usb-net,netdev=net0 ^
-netdev user,id=net0,hostfwd=tcp::2222-:22
メモリは1G(1024)まで、それを超えた値を指定しても「Invalid RAM size, should be 1 GiB」エラーになる。
=== マウス操作 ===
* Raspberry Pi OS内にマウス操作が移るがウィンドウタイトルバーにあるように「CTRL + ALT + G」で抜けれる。
* Raspberry Pi OSのウィンドウを最大化するとマウス操作がスムーズできる。
=== 起動時設定 ===
起動時設定については、言語設定以外はスキップでよい。\\
再度、Raspberry Pi OSを起動、Raspberry Pi OS上のターミナルで実行する。
^デフォルトの管理者^^
^ユーザー名|pi|
^パスワード|raspberry|
=== 空き容量増加 ===
Raspberry Pi OSが起動したら、ターミナルで以下を実行し、ディスクの容量分だけ利用できるようにファイルシステムを拡張する。
$ sudo raspi-config --expand-rootfs
ここで一旦、再起動します。
=== スワップ領域増加 ===
スワップ領域も少ないのでラズパイ上で下記コマンドで修正を行います。
sudo nano /etc/dphys-swapfile ;
/etc/dphys-swapfile
#CONF_SWAPSIZE=100
CONF_SWAPSIZE=2048
保存をして閉じたら下記コマンドでスワップファイルサイズの更新を行います。
sudo dphys-swapfile install ;
sudo dphys-swapfile swapon ;
=== アップデート ===
アップデートを行います。
sudo apt update
==== マシン名:raspi3 ====
イメージファイルは「2020-08-20-raspios-buster-arm64.img」を使用する。2021年以降では動作しなかった。\\
イメージファイルから「kernel8.img」と「bcm2710-rpi-3-b.dtb」を抽出する。\\
Windowsではイメージファイルをマウントできないため、抽出するにはLinuxを使用する。\\
WSL2からはext4ファイルをマウント出来るが、WSL1では対応していない。\\
WSL2が無い場合、VirtualBox内にUbuntuをインストールして抽出する。ファイル共有しておけばWindows側に持ってこれる。
* qemu-w64-setup-20210505.exe
* 2020-08-20-raspios-buster-arm64.img
* kernel8.img
* bcm2710-rpi-3-b.dtb
qemu以外は C:\RaspberryPi3フォルダに格納
先にイメージサイズの変更を行う。イメージサイズは2の冪乗を指定する。
"C:\Program Files\qemu\qemu-img.exe" resize 2020-08-20-raspios-buster-arm64.img 8G
RaspberryPiOS起動用バッチを作成する。
@echo off
cd /d %~dp0
"C:\Program Files\qemu\qemu-system-aarch64.exe" ^
-m 1024 ^
-M raspi3 ^
-kernel kernel8.img ^
-dtb bcm2710-rpi-3-b.dtb ^
-drive format=raw,file=2020-08-20-raspios-buster-arm64.img ^
-append "console=ttyAMA0 root=/dev/mmcblk0p2 rw rootwait rootfstype=ext4 dwc_otg.fiq_fsm_enable=0" ^
-serial stdio ^
-no-reboot ^
-device usb-kbd ^
-device usb-tablet ^
-device usb-net,netdev=net0 ^
-netdev user,id=net0,hostfwd=tcp::2222-:22
メモリは1G(1024)まで、それを超えた値を指定しても「Invalid RAM size, should be 1 GiB」エラーになる。
=== マウス操作 ===
* Raspberry Pi OS内にマウス操作が移るがウィンドウタイトルバーにあるように「CTRL + ALT + G」で抜けれる。
* Raspberry Pi OSのウィンドウを最大化するとマウス操作がスムーズできる。
=== 起動時設定 ===
起動時設定については、言語設定以外はスキップでよい。\\
再度、Raspberry Pi OSを起動、Raspberry Pi OS上のターミナルで実行する。
^デフォルトの管理者^^
^ユーザー名|pi|
^パスワード|raspberry|
=== 空き容量増加 ===
Raspberry Pi OSが起動したら、ターミナルで以下を実行し、ディスクの容量分だけ利用できるようにファイルシステムを拡張する。
$ sudo raspi-config --expand-rootfs
ここで一旦、再起動します。
=== スワップ領域増加 ===
スワップ領域も少ないのでラズパイ上で下記コマンドで修正を行います。
sudo nano /etc/dphys-swapfile ;
/etc/dphys-swapfile
#CONF_SWAPSIZE=100
CONF_SWAPSIZE=2048
保存をして閉じたら下記コマンドでスワップファイルサイズの更新を行います。
sudo dphys-swapfile install ;
sudo dphys-swapfile swapon ;
=== アップデート ===
アップデートを行います。
sudo apt update
===== WSL環境での動作確認 =====
ラズベリーパイ上で動かす前に、Linux上で動くかを検証
==== WSL環境作成 ====
- Windowsの機能の有効化で「Linux用Windowsサブシステム」を有効化する。
- Windows Storeからubuntu 18.04 ltsをインストールする。
=== VSCode+WSLのC++開発 ===
Linux用のC++開発環境構築するのに、Visual Studio Code(VSCode)を使用する。\\
Windows側のVSCodeからWSL環境にアクセスし、Linux環境でのC/C++開発が可能となります。
[[https://novnote.com/vscode-wsl-cpp-env/511/|【環境構築】VSCode+WSL+Remote WSLでWindows上にC/C++開発・デバッグ環境を構築する]]
WSL Ubuntu上で行う作業
- sudo apt update
- sudo apt install build-essential -y
- sudo apt install gdb -y
VSCode拡張機能のインストール
* Remote Development(Remote WSL)
* C/C++
* C/C++ IntelliSense
現在編集中のフォルダ以下に「.vscode」フォルダ内に launch.json と tasks.json を作成する。\\
「C++(GDB/LLDB)」と「g++ アクティブファイルのビルドとデバッグ(コンパイラ /usr/bin/g++)」を選択している。
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "g++ - アクティブ ファイルのビルドとデバッグ",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "gdb の再フォーマットを有効にする",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "C/C++: g++ アクティブなファイルのビルド",
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
{
"version": "2.0.0"
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: g++ アクティブなファイルのビルド",
"command": "/usr/bin/g++",
"args": [
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "デバッガーによって生成されたタスク。"
}
]
}
==== Linux向け.NET5アプリケーションの発行 ====
Visual Studioにて.NET5アプリケーションをLinux用に発行する。
- ビルドの対象プラットフォームをx64にする。
- ターゲット ランタイムを「linux-x64」にする。
==== .NET5アプリケーションの動作確認 ====
コマンドプロンプト上で、wslコマンドを実行する。\\
「cd /mnt/c/」で、Windows上のCドライブにアクセスできる。\\
実行ファイルがあるフォルダまでCDコマンドでディレクトリを移動させる。\\
実行ファイルは、"./(実行ファイル名)"と先頭に"./"を付けることで実行できるようになる。\\
先頭に"./"を付けないと、「command not found」エラーとなる。
[[https://7iro.net/csharp-dotnetcore-linux/|Windowsで開発したアプリをLinuxで実行する【C# / .Net Core】]]
=== プログラムからの呼び出し ===
Windowsでは CreateProcessとWaitForSingleObjectを使用するが、Linuxにはないため forkとexeclとwaitpidを使用する。\\
#include
#include
#include
#include
int main()
{
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(-1);
}
else if (pid == 0) {
// 子プロセスで別プログラムを実行
execl("./Client/test", "./Client/test", "-u", "HOGE", NULL);
exit(-1);
}
// 親プロセス
int status;
//子プロセスの終了待ち
pid_t r = waitpid(pid, &status, 0);
if (r < 0) {
perror("waitpid");
exit(-1);
}
if (WIFEXITED(status)) {
// 子プロセスが正常終了の場合
printf("child exit-code=%d\n", WEXITSTATUS(status));
}
else {
printf("child status=%04x\n", status);
}
return 0;
}
===== Python =====
C#版のインターフェイスではメモリ2GByteのラズパイでは5秒かかるなど動作が遅いこともあり、Pythonで作成しPyInstallerで実行ファイルを作成する方法を試す。
ラズパイの実機は外部ネットワークに接続できないため、ラズパイ(raspi3)のエミュレーター環境で行う。
==== Python3に切り替え ====
ラズパイのPythonのバージョンがデフォルトが2系になっているで、3系をデフォルトにする。
cd /usr/bin
sudo unlink python
sudo ln -s python3 python
[[https://python-academia.com/raspberry-pi-python-version/|ラズパイ(Raspberry Pi)のpythonバージョン確認、python3への変更方法。]]
==== 仮想環境作成 ====
Python3ならデフォルトで仮想環境作成の「venv」が備わっている。
pyhton -m venv {環境名}
# 例
pyhton -m venv cnvnet
=== 仮想環境の有効化 ===
source {環境名}/bin/activate
# 例
source cnvnet/bin/activate
コマンドラインの先頭に(環境名)が追加された状態になります。
({環境名}) pi@raspberrypi:~/ $
# 例
(cnvnet) pi@raspberrypi:~/ $
==== Pyinstaller ====
=== Pyinstallerのインストール ===
仮想環境にPyinstallerをインストールする
(cnvnet) pi@raspberrypi:~/ $ pip install pyinstaller
(cnvnet) pi@raspberrypi:~/ $ pyinstaller -v
4.10
=== テスト ===
(cnvnet) pi@raspberrypi:~/ $ mkdir test
(cnvnet) pi@raspberrypi:~/ $ cd test
(cnvnet) pi@raspberrypi:~test/ $ nano test.py
print("Hello cnvnet")
(cnvnet) pi@raspberrypi:~test/ $ pyinstaller test.py
[[https://ideal-user-interface.hatenablog.com/entry/20110304/1299232964|Pythonスクリプトを実行形式のファイルにまとめる(2)]]
テストフォルダ配下にdistフォルダが作成され実行ファイルの「test」ファイルができる。\\
実行すると"Hello cnvnet"が表示される。
(cnvnet) pi@raspberrypi:~/ $ ./dist/test/test
Hello cnvnet
==== Windows側にファイル転送 ====
Windows側に「WinSCP」をインストールして、「WinSCP」を使用すればGUI上でファイル転送ができる。\\
https://forest.watch.impress.co.jp/library/software/winscp/
==== 速度検証 ====
dotNET版の半分程度の速度改善が見られた。
^種類^1回目^2回目^3回目^4回目^5回目^
^dotNET版|4440ms|3130ms|3120ms|3110ms|3130ms|
^Python版|3690ms|1670ms|1630ms|1620ms|1620ms|