組込Linux（など）で動く mruby peripheral interface guide 互換の gem を作りました

[しまねソフト研究開発センター](https://www.s-itoc.jp)（略称 ITOC）にいます、東です。  
これは [Ruby Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ruby) の記事です。


# Abstract

* mruby, mruby/c 共通の[マイクロコントローラペリフェラルインターフェースガイド](https://github.com/mruby/microcontroller-peripheral-interface-guide)というものがある。
* 実体は、I2C, UART 等のインターフェースの利用 API を定義したもの。
* mrubyだけじゃなくて、やっぱり CRuby でも使いたいよね。

# 結論

作りました。

* GPIO&nbsp;&nbsp;https://rubygems.org/gems/mruby-sysfs-gpio
* I2C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;https://rubygems.org/gems/mruby-linux-i2c
* SPI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;https://rubygems.org/gems/mruby-linux-spi
* UART&nbsp;&nbsp;https://rubygems.org/gems/mruby-serialport-uart


# うれしいこと

* CRuby でも、IoT ができる。Raspberry Pi をはじめとして、組込 Linux ボードで CRuby使って IoT とか、よさそうじゃない？
* もっと「小さく」「低コストに」「省電力に」したくなったら、マイコンボードに mruby か mruby/c を載せて、同じ Ruby プログラムをうごかすんだ。

そんなわけで、ガイドラインのうち、Linux のデバイスドライバレベルでサポートされている4種類、"GPIO", "I2C", "SPI", "UART" の rubygem ライブラリを作りました。
ラズベリーパイ4 と、アットマークテクノの [Armadillo-IoT G3](https://armadillo.atmark-techno.com/armadillo-iot-g3) で動作確認をしています。


# GPIO クラスについて

GPIOは、General-purpose input/output の略で、汎用の入出力です。１ビット単位の入力または出力ができます。
入力の代表的な例はプッシュスイッチのON/OFF 状態など、出力の代表的な例は、LEDの点灯などです。

ガイドライン
* [mruby_io_GPIO_ja.md](https://github.com/mruby/microcontroller-peripheral-interface-guide/blob/main/mruby_io_GPIO_ja.md)

実装
* https://rubygems.org/gems/mruby-sysfs-gpio

Linux の sysfs を使っています。GPIO の sysfs は既に使用が非推奨になっていますが、新しい仕組みが使いたいボード類で十分出そろったら、そのときに考えます。

## ElChika（えるちか）サンプル

```
require "mruby/gpio"   # or "mruby/gpio/sysfs"

led = GPIO.new(26, GPIO::OUT)  # GPIO26番ピンを出力に設定し、そのインスタンスを得る
while true
  led.write( 1 )    　　　　　　　# 出力1 -> LED点灯
  sleep 1
  led.write( 0 )    　　　　　　　# 出力0 -> LED消灯
  sleep 1
end
```

## プッシュスイッチとLED（入出力）のデモ

ソースコード
https://github.com/HirohitoHigashi/mruby-mio/blob/main/mruby-sysfs-gpio/test/led5sw1_polling.rb

実行結果
![led_anim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/1d5365cc-ac7f-131d-55d6-6641af33f659.gif)

## 未実装

プルアップ、プルダウンの機能などについては、sysfs にその機能が無いのでサポートできていません。


# I2Cクラス, SPI クラス

I2C, SPI は、両方ともシリアル入出力バスの規格で、主にセンサーや小型ディスプレイの接続などに使用されます。

ガイドライン
* [mruby_io_I2C_ja.md](https://github.com/mruby/microcontroller-peripheral-interface-guide/blob/main/mruby_io_I2C_ja.md)
* [mruby_io_SPI_ja.md](https://github.com/mruby/microcontroller-peripheral-interface-guide/blob/main/mruby_io_SPI_ja.md)

実装
* https://rubygems.org/gems/mruby-linux-i2c
* https://rubygems.org/gems/mruby-linux-spi

いずれも、Linux device driver の ioctl だけを使って PureRubyで書いています。perl ゆずり（と思われる）pack / unpack を駆使しますが、もう少しマシな方法が欲しいと思いますよね。

## 気圧センサ　LPS25H　読み取りサンプル
```
require "mruby/i2c"

# create instance
i2c = I2C.new("/dev/i2c-1")

# Write to device at address 0x5c, data 0x20, 0x90.
i2c.write( 0x5c, 0x20, 0x90 )

# Read 5 bytes from the device at address 0x5c.
# Outputs 0xa8 before reading.
s = i2c.read( 0x5c, 5, 0xa8 )
```

## 加速度センサで重力方向を検出するデモ

ソースコード
https://github.com/HirohitoHigashi/mruby-mio/blob/main/mruby-linux-spi/test/ADXL345_Accelerometer.rb

実行結果
![adxl345_demo-2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/504019/cb1c221e-0681-3cc4-8a48-8194ea7ded30.gif)

## 未実装

I2Cの 低レベルメソッドは、実装していません。


# UART クラス

UART クラスは、通常1対1のシリアル通信をサポートする規格で、RF通信モジュールをはじめとし、様々なデバイスが接続できます。

ガイドライン
* [mruby_io_UART_ja.md](https://github.com/mruby/microcontroller-peripheral-interface-guide/blob/main/mruby_io_UART_ja.md)

実装
* https://rubygems.org/gems/mruby-serialport-uart

このクラスだけは、下層に serialport gem を使用します。汎用的に作ろうとすると、どうしてもC拡張が必要になりそうだったので、よくできている gem に頼りました。

## サンプル  Echo server
```
require "mruby/uart"   # or "mruby/uart/serialport"

uart = UART.new("/dev/serial0")
while true
  s = uart.read(1)
  uart.write s
  print s
end
```


# おわりに

ADC と PWM については、マイコンボードの種類ごとにハードウェアを直接たたくコードを書くしかなさそうです。また、現在は LinuxOS のサポートだけですが、FreeBSD も I2C等のデバイスドライバが用意されているので、そちらへの対応もしたいと考えています。

それでは、Happy Holidays!