ブラウザから操作可能なラジコンを作る【１】


## 【１】Raspberry pi の、GPIOをTypescriptから操作


別プロジェクトの試験をかねて、以下の条件でラジコンを作りましたので
UZAYAの宣伝を兼ねて、記事にまとめます。
<br></br>
## 条件

* ラジコン本体は、RasipberryPiで制御
* DualSenseをコントローラーに使用する
* ブラウザにDualSenseを接続し、RasipberryPiに操作信号を送る
* DualSenseのアナログトリガーで速度調整が可能
* ラジコン本体に搭載したカメラの映像を、ブラウザで確認可能
* スマホでカメラ映像を見た場合、VRで見れる

以上の条件を満たす内容で作っていきます。
1度に全部書くと長すぎるので、何回かに分けて続きます。

こんな感じの物を作成する議事録です。

□ 走行デモ
<iframe width="560" height="315" src="https://www.youtube.com/embed/bIB_23iRbfY?si=1DuYu1pCMKx-tN3t" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

□ ラジコン本体
![IMG_4495.JPEG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147738/e3711877-6f99-47e0-9fb3-a1a20b2f4459.jpeg)



<br></br>
## 目次
* [【１】Raspberry pi の、GPIOをTypescriptから操作](https://qiita.com/Yasukosan/items/1bf9480c10de7d69c2a4)
* [【２】DCモーターをPWMで速度制御](https://qiita.com/Yasukosan/items/291c76c10be135fd7595)
* [【３】サーボモーターを制御](https://qiita.com/Yasukosan/items/e81c9c9d0159b32e5893)
* [【４】DualSenseをブラウザに接続](https://qiita.com/Yasukosan/items/bf3a085145db84a48823)
* [【５】DualSenseの情報をRaspberryPiに飛ばす](https://qiita.com/Yasukosan/items/400883be94751966221a)
* [【６】DualSenseのチャタリング？問題対応](https://qiita.com/Yasukosan/items/7e52609699b4b71196ca)
* [【７-１】RaspberryPiから低遅延で映像を飛ばす](https://qiita.com/Yasukosan/items/98426f1ba4dea4c9ec5f)
* [【７-２】RaspberryPiから低遅延で映像を飛ばす](https://qiita.com/Yasukosan/items/2246c8a32602ca77ace9)
* [【８】ThreeJSでVRもどきを作成](https://qiita.com/Yasukosan/items/e15e8fd002cf49e3d04d)
* [【９-１】iPhoneの加速度から頭の向きをVRに反映](https://qiita.com/Yasukosan/items/9b4333770baea4110361)
* [【９-２】スマホVRゴーグル向けデザインに変える](https://qiita.com/Yasukosan/items/7f1bb7a9d95cb76f8117)
* [【１０】ラジコン本体の製作](https://qiita.com/Yasukosan/items/0d37c1df5b5d6c02257b)
* 【１１】パワーアップとバッテリー問題の解決



<br></br>
## JavascriptでRaspberryPiのGPIOを操作

RaspberryPiにインストールするOSは
<b>Raspberry Pi OS 32-Bit</b>を使用しています。
![スクリーンショット 2025-02-26 231217.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147738/79494bf7-6dda-48f7-9fc6-dd618265eb8d.png)





<br></br>
## nodejsインストール

gpioをTypescriptから操作するにあたり
完全に非推奨事案ですが、nodejs16が必要になります。
自力で今回使用するpigpioのラッパーを書く
という選択肢もありますが
<b>正直面倒なので</b>とりあえずこのまま行きます。

下記セットアップコマンドで
色々警告が出ますが、とりあえずnodejs16が入ります。
```powershell
$ curl -s https://deb.nodesource.com/setup_16.x | sudo bash
```
16系統が入っていることを確認
```powershell
$ node --version
v16.20.2
```
npmコマンドも合わせてバージョンが古いです。
```powershell
$ npm --version
8.19.4
```

<br></br>
## pigpioインストール
gpioを操作する定番ライブラリ、pigioを追加
```powershell
sudo apt install pigpio
```
制作時点で下記のバージョンが入りました。
```powershell
$ pigpiod -v
79
```
<br></br>
## zxインストール
簡単なテストシェル等もTypescriptで作成してしまうので
Google性Javascriptシェルのzxを使用します。

```powershell
npm install -g zx
```
制作時点のバージョンは下記になります。
```powershell
$ zx --version
8.3.2
```
<br></br>
# pigpioを操作するサービス作成
「使い回す処理は別ファイルで記述したい」派に属する人間なので
pigpioを呼び出す部分を単独でサービスとして分けます。

* npmライブラリ、pigpioの呼び出し
* 使用するポート番号の追加
* ポート番号の削除
* 初期化処理
* デジタル書き込み
* PWM書き込み
* Servo書き込み

以上の機能を持つサービスとして作成できれば
何でもいいと思います。

javascriptで書かないと、最終的に動かない問題がありますので
Typescriptで！という予定ですが
原則Javascriptで進めます。


下記は今回使用したコードです。
「gpio.service.js」というファイル名で保存します。
```javascript
const Gpio = require('pigpio').Gpio;

let GpioPorts = {}

const Ports = [
    { port: 0, mode: "out" }
]

// ポートの追加
const addPort = (port) => {
    if (Ports[0].port === 0) {
        Ports.pop()
    }
    if (Ports.find(p => p.port === port.port)) {
        console.log(`Port ${port.port} already exi sts`)
    } else{
        Ports.push(port)
    }
}

// ポートの削除
const removePort = (port) => {
    const index = Ports.findIndex(p => p.port === port)
    if (index !== -1) {
        Ports.splice(index, 1)
    }
}

// イニシャル処理
const initGpio = () => {
    console.log('INITIAL PORTS', Ports)
    if (Object.keys(GpioPorts).length > 0) {
        GpioPorts = {}
    }

    try 
    {
        for (const port of Ports) {
            console.log('SETUP ',port)
            const gpio = new Gpio(port.port, {mode: Gpio.OUTPUT})
            GpioPorts[port['port']] = gpio
        }
    } catch (error) {
        console.log('ERROR', error)
    }

}

/**
 * デジタル出力
 * @param port number
 * @param value 1 or 0
 */
const write = (
    port,
    value
) => {
    try {
        if (GpioPorts[String(port)]) {
            GpioPorts[String(port)].digitalWrite(value)
        }
        // B1.digitalWrite(1)
        return {
            status: true,
            message: `Port ${port} value set to ${value}`
        }
    } catch (error) {
        return {
            status: false,
            message: error.message
        }
    }
}

/**
 * PWMのソフトウェアエミュレーションを出力
 * @param port number
 * @param port 1~250
 */
const pwmWrite = (
    port,
    value
)  => {
    try {
        if (GpioPorts[String(port)]) {
            GpioPorts[String(port)].pwmWrite(value)
        }
        return {
            status: true,
            message: `Port ${port} value set to ${value}`
        }
    } catch (error) {
        return {
            status: false,
            message: error.message
        }
    }
}

/**
 * サーボモーター用出力
 * @param port number
 * @param value 0 ~ 1500
*/
const searvoWrite = (
    port,
    value
) => {
    try {
        if (GpioPorts[String(port)]) {
            GpioPorts[String(port)].servoWrite(value)
        }
        return {
            status: true,
            message: `Port ${port} value set to ${value}`
        }
    } catch (error) {
        return {
            status: false,
            message: error.message
        }
    }
}


module.exports = {
    addPort,
    removePort,
    initGpio,
    write,
    pwmWrite,
    searvoWrite
}
```

<br></br>
## zxでLチカ

GPIOを叩くサービスを作ったので
実際に叩きます。

GPIOの16番ポートを使用し
まずは、定番のLチカを行います。

LEDの足の長い方を16番と
短い方をGNDと繋ぐように結線
<img
    src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147738/61898d7e-8aa0-4237-ab4f-76662488c57f.png"
    width="320"
/>

下記の試験用コードを「gpio_led_test.js」などで保存。
```javascript
const gpio = require('.gpio.service')

gpio.addPort({port: 16, mode: 'out'})

await gpio.initGpio()

let push = 1

while (true) {
    console.log(gpio.write(16, push))
    push = (push === 1) ? 0 : 1
    await new Promise((resolve) => setTimeout(resolve, 1000))
}

```

<br></br>
## Lチカ試験

ZXで保存したjsファイルを実行
```powershell
zx ./gpio_led_test.js
```


1秒毎にLEDが点いたり消えたりするはず、です。


実際の点灯状態の映像です。
<iframe width="560" height="315" src="https://www.youtube.com/embed/ez9Ry9CMUfc?si=g481yHp6AyoBose-" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

これで、JavascriptからGPIOを操作する下準備が出来ましたので
次回はDCモーターを操作し、速度調整まで行います。

<br></br>
<br></br>
# UZAYA
Uzayaでは、多分仕事を求めています。
何かの役に立ちそうでしたら、是非お知らせを。