1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-03-02

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

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

条件

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

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

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

□ 走行デモ

□ ラジコン本体
IMG_4495.JPEG


目次


JavascriptでRaspberryPiのGPIOを操作

RaspberryPiにインストールするOSは
Raspberry Pi OS 32-Bitを使用しています。
スクリーンショット 2025-02-26 231217.png


nodejsインストール

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

下記セットアップコマンドで
色々警告が出ますが、とりあえずnodejs16が入ります。

$ curl -s https://deb.nodesource.com/setup_16.x | sudo bash

16系統が入っていることを確認

$ node --version
v16.20.2

npmコマンドも合わせてバージョンが古いです。

$ npm --version
8.19.4


pigpioインストール

gpioを操作する定番ライブラリ、pigioを追加

sudo apt install pigpio

制作時点で下記のバージョンが入りました。

$ pigpiod -v
79


zxインストール

簡単なテストシェル等もTypescriptで作成してしまうので
Google性Javascriptシェルのzxを使用します。

npm install -g zx

制作時点のバージョンは下記になります。

$ zx --version
8.3.2


pigpioを操作するサービス作成

「使い回す処理は別ファイルで記述したい」派に属する人間なので
pigpioを呼び出す部分を単独でサービスとして分けます。

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

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

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

下記は今回使用したコードです。
「gpio.service.js」というファイル名で保存します。

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
}


zxでLチカ

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

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

LEDの足の長い方を16番と
短い方をGNDと繋ぐように結線

下記の試験用コードを「gpio_led_test.js」などで保存。

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))
}


Lチカ試験

ZXで保存したjsファイルを実行

zx ./gpio_led_test.js

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

実際の点灯状態の映像です。

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




UZAYA

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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?