Raspberry Pi 3でソフトUARTを利用

# はじめに

ラズパイ3ではUARTが2つ用意されていますが、実際に外部接続用に利用できるのは1つしかありません。複数の機器とUARTでシリアル通信する場合にUSBアダプターを追加したりとハードウェアを増やすのも嫌なので、ソフトウェアのUARTデバイスを利用してみます。

ソフトウェアでUART通信するので、高速な通信はできないのですが接続しようとしている[GPS受信器キット](http://akizukidenshi.com/catalog/g/gK-09991/)のデフォルトボーレートが9600bpsと低速なので何とかなるだろうと。

なお、ラズパイ4ではUART1～5まで使えるようになったみたいです。ちょっと欲しいかも…

# 追記事項あり:helmet_with_cross:

- 動作が不安定なので、常時利用は難しいかも。詳細は[こちら](#追記事項)。


# Goal

- GPS受信キットからのUART送信をソフトウェアUARTで受け取れること

# 事前確認

下記設定がすんでいる前提です。

- シリアルコンソールの無効化
- UARTの有効化
- UART0とUART1(Mini UART) の入れ替え

なお、UART0とUART1の入れ替え後は下記となっている想定です。

```
pi@raspberrypi:~/project/soft_uart $ ll /dev | grep serial
lrwxrwxrwx  1 root root           7 Jul 19 14:28 serial0 -> ttyAMA0
lrwxrwxrwx  1 root root           5 Jul 19 14:28 serial1 -> ttyS0
```

# ソフトウェアUARTの設定

基本は下記Githubに書いてある通りです。
https://github.com/adrianomarto/soft_uart

```bash
git clone https://github.com/adrianomarto/soft_uart
sudo apt-get install raspberrypi-kernel-headers
cd soft_uart
make
sudo make install
sudo insmod soft_uart.ko
```

下記のように `/dev/ttySOFT0` があればOKです。

```bash:確認
pi@raspberrypi:~/project/soft_uart $ ll /dev | grep SOFT
crw-rw----  1 root dialout 240,   0 Jul 19 15:13 ttySOFT0
```

なお、再起動には使えなくなるので再度 `sudo insmod soft_uart.ko` を実行してください。継続利用する場合はOS起動時にロードできるようrcスクリプトなどに書いてください、

# ラズパイ側の接続

デフォルトでは以下の設定となっているのでGPIO17と27をワイヤで直結して試してみます。

- gpio_tx: int [default = 17]
- gpio_rx: int [default = 27]


```:配線
GPIO17(TX) --> GPIO27(RX)
```

# 確認

minicomで接続確認[^LANG]
ボーレート9600で確認してみます。

```bash
minicom -b 9600 -D /dev/ttySOFT0
```

別の端末で以下を実行します。

```bash
echo "hello" > /dev/ttySOFT0
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/912b9847-d6c3-8720-5d68-f1a35fb7c7ac.png)


へんなタブが気になりますが、ハードウェアのUART側(UART0)も同じ表示になったのでよしとします。

# GPS受信キットでの確認

ここまでくれば大丈夫でしょう。試してみます。
特に問題なしでした！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/a36bde22-38c9-509a-e261-39b93d770896.png)

![IMG_4495.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144344/031f488e-fb18-ccb1-a0b1-7685006b7887.jpeg)

# 参考サイト

下記サイトのほぼそのままになりました。情報助かりました！

- RPIで複数のUARTを使う方法
  - https://research.itplants.com/?p=2034

# 感想

- シリアル通信周り／電源回りなどを考えるとArduinoの偉大さがよくわかる
- mrubyがESP32あたりでさくっと動けばなー。今のアップロード手順だと初心者としてはTraial&Errorのたびに心が折れる…
- RasbperyPi OSは最初から英語版でいれるべきだった。
- ワイヤ断線ヤバイ…気づくのに時間がかかった…

# 追記事項

プログラムを中断＆再度実行を数回繰り返すとシリアルポートからの読み込みができなくなる現象が発生。

- 読み込みができないようでプログラムが永遠にブロックされる
- いったんモジュールを削除してから再度モジュールをロードするとうまく動く
- プログラムをずっと起動しておく分には問題なさげ

そこで以下を試すも改善せず。

- `Ctrl + C` で停止時にきちんとポートを閉じるように `SIGINT` をトラップ
    - 修正後のソースは後述
- CPU周波数の固定化（250MHzに固定）
    - `echo "core_freq=250" | sudo tee -a /boot/config.txt` 後再起動

結局以下の力技で回避した。まさに:muscle:技
`[PATH_TO_SOFT_UART_DIR]` は環境に合わせてください。

```ruby
require 'serialport'
require 'nmea_plus'

puts `sudo rmmod soft_uart.ko`
sleep 0.1
puts `cd [PATH_TO_SOFT_UART_DIR] && sudo insmod soft_uart.ko`

sp = SerialPort.new('/dev/ttySOFT0', 9600, 8, 1, 0) # see: https://rubydoc.info/gems/serialport/SerialPort#set_modem_params-instance_method

# 最低限お行儀よく振る舞う
trap 'SIGINT' do
  sp.close if sp
  exit
end

source_decorder = NMEAPlus::SourceDecoder.new(sp)
source_decorder.each_complete_message do |message|
  # see: https://github.com/ianfixes/nmea_plus/blob/master/lib/nmea_plus/message/nmea/rmc.rb
  if 'GPRMC' == message.data_type
    puts message.utc_time
    puts message.active? # false: データ無効
    puts message.latitude
    puts message.longitude
    #puts message.speed_over_ground_knots
    #puts message.track_made_good_degrees_true
    #puts message.magnetic_variation_degrees
    puts message.faa_mode # A: 単独測位(精度3m程度), D: 相対測位(精度0.4m程度) 
    puts
  end
end

```

[^LANG]: minicomが日本語ロケールだと表示がずれてしまうので、 `LANG=C minicom -s` を試したのですが英語化されず…raspi-configからの設定はなぜか効かなかったの、GUI画面の設定からロケールを変更しました。