Raspberry Pi & 10個の言語でLチカやってみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

テントの中から失礼します、CX事業本部のてんとタカハシです!

前回の記事(Rust & Raspberry Pi で温度センサーの値を AWS IoT Core に Pub してみた)を書く際に、Rust を使ってLチカを試したのですが、何だか色んな言語で Raspberry Pi を操作したい欲求に駆られてしまい、こんな素敵な記事を書いてみました。

Raspberry Pi を使った電子工作では、セットアップ不要 かつ ドキュメント豊富な Python がよく使われると思いますが、Python の経験が無い方にとっては、少々ハードルを感じてしまうかもしれません。しかし、この記事を見ればもう大丈夫。だって10個の言語でLチカを試したんだから。読者の方々が触れたことある言語、きっと1つはあるはず。皆様、是非是非安心して、電子工作の第一歩、Lチカを試して楽しんで頂ければなと思います。

各言語のプログラムは下記に置いてありますので、Lチカを試す際はリポジトリをローカルに落として、これから記載する手順に従って実行してみてください。

GitHub - iam326/l-tika-10-languages

今回Lチカで使用する言語の一覧

  • Python
  • Node.js
  • C言語
  • Golang
  • Rust
  • Shell Script
  • Java
  • PHP
  • Ruby
  • Perl

Raspberry Pi の環境

Raspberry Pi OS のバージョンは下記の通りです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:    10
Codename:   buster

Raspberry Pi 自体のモデルは、「Raspberry Pi 2 Model B」を使用して記事を書いていますが、今回使用する Pin の配置は、Raspberry Pi 3 or 4 系 と同じになります。

配線

下記の通りの配線してください。

プログラムの流れ

全ての言語共通でプログラムの流れは下記の通りになります。

  • LED と接続している GPIO23(物理ピン番号16)を出力モードに設定する
  • 無限ループで1秒おきに LED の ON/OFF を切り替えて、チカチカさせる
  • Ctrl + C で無限ループを抜ける
  • プログラムを終了する

また、プログラムが終了した後も LED が ON になり続ける状態を回避するため、必要に応じて後処理を行います。言語(というより使用するライブラリ)によっては、プログラムの終了と同時に LED を自動 OFF してくれるものもあります。

実装見ていくぞー

Python

定番の Python からやっていきます。こちらはセットアップ不要で動いちゃいます。

import RPi.GPIO as GPIO
from time import sleep

LED_PIN = 23

GPIO.setmode(GPIO.BCM)
GPIO.setup(LED_PIN, GPIO.OUT)

try:

    while True:
        GPIO.output(LED_PIN, GPIO.HIGH)
        sleep(1.0)
        GPIO.output(LED_PIN, GPIO.LOW)
        sleep(1.0)

except KeyboardInterrupt:
  GPIO.cleanup()

下記の手順でプログラムを実行します。

$ cd python/
$ python3 main.py

使用しているライブラリのドキュメントはこちら。SourceForge - raspberry-gpio-python

Raspberry Pi の公式ドキュメントでは、GPIO Zero というライブラリを使ったLチカのプログラムが紹介されています。 raspberrypi.org - GPIO in Python

GPIO Zero 知らなかった、今度使って遊んでみようと思います。

Node.js

次は Node.js でやっていきます。Node.js で Raspberry Pi を操作している記事もよく見かける気がしています。

const Gpio = require('onoff').Gpio;
const LED_PIN = new Gpio(23, 'out');

process.on('SIGINT', () => {
  LED_PIN.unexport();
  process.exit();
});

setInterval(() => {
  LED_PIN.writeSync(LED_PIN.readSync() === 0 ? 1 : 0);
}, 1000);

npm trends を見て、一番人気がありそうな onoff というライブラリを使用しました。

下記の手順でセットアップ & プログラムを実行します。

$ curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
$ sudo apt-get install -y nodejs

$ node --version
v14.6.0

$ npm --version
6.14.6

$ cd nodejs/
$ npm install
$ node main.js

C言語

組み込みといえばC言語。こちらはセットアップ不要で動いちゃいます。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>

#define LED_PIN 4

int end = 0;

void abort_handler(int signal);

int main(void) {
  wiringPiSetup();
  pinMode(LED_PIN, OUTPUT);

  if (signal(SIGINT, abort_handler) == SIG_ERR) {
    exit(1);
  }

  while (!end) {
    digitalWrite(LED_PIN, HIGH);
    delay(1000);
    digitalWrite(LED_PIN, LOW);
    delay(1000);
  }

  return 0;
}

void abort_handler(int signal) {
  end = 1;
}

WiringPi というライブラリを使用しています。

下記の手順でプログラムを実行します。

$ cd c/
$ gcc -Wall -o main main.c -lwiringPi
$ ./main

Golang

C言語と構文が似てる感じの Golang。

package main

import (
  "time"

  "gobot.io/x/gobot"
  "gobot.io/x/gobot/drivers/gpio"
  "gobot.io/x/gobot/platforms/raspi"
)

func main() {
  r := raspi.NewAdaptor()
  led := gpio.NewLedDriver(r, "16")

  work := func() {
   gobot.Every(1 * time.Second, func() {
    led.Toggle()
   })
  }

  robot := gobot.NewRobot("ltika",
    []gobot.Connection{r},
    []gobot.Device{led},
    work,
  )

  robot.Start()
}

GoBot というロボティックス・IoT向けのフレームワークを使用しています。ドキュメントに書いてあるプログラムそのままで動作しました。ちょっとこのGoBot面白そうなので、何かの機会で試してみたいなという気持ちになっています。

下記の手順でセットアップ & プログラムを実行します。

$ wget https://golang.org/dl/go1.14.6.linux-armv6l.tar.gz
$ sudo tar -C /usr/local -xzf go1.14.6.linux-armv6l.tar.gz

$ echo export 'PATH=$PATH:/usr/local/go/bin'  >> ~/.profile
$ source ~/.profile

$ go version
go version go1.14.6 linux/arm

$ cd golang/
$ go build main.go 
$ ./main

Rust

この記事を書くきっかけとなった Rust。

use std::error::Error;
use std::thread;
use std::time::Duration;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use ctrlc;
use rppal::gpio::Gpio;

const GPIO_LED: u8 = 23;

fn main() -> Result<(), Box<dyn Error>> {
    let mut pin = Gpio::new()?.get(GPIO_LED)?.into_output();
    pin.set_reset_on_drop(true);

    let running = Arc::new(AtomicBool::new(true));
    let r = running.clone();
    ctrlc::set_handler(move || {
        r.store(false, Ordering::SeqCst);
    }).expect("Error setting Ctrl-C handler");

    while running.load(Ordering::SeqCst) { 
        pin.set_high();
        thread::sleep(Duration::from_millis(1000));
        pin.set_low();
        thread::sleep(Duration::from_millis(1000));
    }

    Ok(())
}

rppal というクレートを使用しています。

下記の手順でセットアップ & プログラムを実行します。

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

$ source ~/.profile

$ cargo --version
cargo 1.45.0 (744bd1fbb 2020-06-15)

$ cd rust/ltika/
$ cargo run

Ctrl + C らへんの処理はドキュメントのまま書いて動いたのですが、何やってるかはよく分かりませんでした。勉強不足です勉強します。

Shell Script

Shell Script で手軽に Raspberry Pi を制御したいっていうケースはよくありそう。こちらはセットアップ不要で動いちゃいます。

#!/bin/bash

set -euo pipefail

LED_PIN=23

gpio export $LED_PIN out

trap 'gpio unexport $LED_PIN' SIGINT

while true
do
  gpio -g write $LED_PIN 1
  sleep 1
  gpio -g write $LED_PIN 0
  sleep 1
done

これも WiringPi を使用しています。gpioコマンドを使って制御していますね。

下記の手順でプログラムを実行します。

$ cd sh/
$ ./main.sh

Java

調べてみたのですが、Java って組み込みでもよく使われるんですね。

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class Ltika {

  public static void main(String[] args) throws InterruptedException {
    final GpioController gpio = GpioFactory.getInstance();
    final GpioPinDigitalOutput pin = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_04, "LED", PinState.LOW);
    pin.setShutdownOptions(true, PinState.LOW);

    Runtime.getRuntime().addShutdownHook(new Thread(
      () ->  gpio.shutdown()
    ));

    while (true) {
      pin.toggle();
      Thread.sleep(1000);
    }
  }

}

Pi4J というライブラリを使用しています。

下記の手順でセットアップ & プログラムを実行します。

$ sudo apt install default-jdk

$ java --version
openjdk 11.0.7 2020-04-14
OpenJDK Runtime Environment (build 11.0.7+10-post-Raspbian-3deb10u1)
OpenJDK Server VM (build 11.0.7+10-post-Raspbian-3deb10u1, mixed mode)

$ curl -sSL https://pi4j.com/install | sudo bash

$ cd java/
$ javac -classpath .:classes:/opt/pi4j/lib/'*' -d . Ltika.java
$ java -classpath .:classes:/opt/pi4j/lib/'*' Ltika

PHP

PHP でも Raspberry Pi 操作できるよってのは、電子工作のハードルをかなり下げてくれそうなお話。

<?php
require_once "vendor/autoload.php";

use PiPHP\GPIO\GPIO;
use PiPHP\GPIO\Pin\PinInterface;

declare(ticks = 1);

pcntl_signal(SIGINT, "signal_handler");

$end = false;
$gpio = new GPIO();
$led_pin = $gpio->getOutputPin(23);

while (!$end) {
  $led_pin->setValue(PinInterface::VALUE_HIGH);
  sleep(1);
  $led_pin->setValue(PinInterface::VALUE_LOW);
  sleep(1);
}

$led_pin->unexport();

function signal_handler($signal) {
  global $end;
  switch($signal) {
    case SIGINT:
      $end = true;
      break;
    default:
      break;
  }
}
?>

PiPHP/GPIO というライブラリを使用しています。

下記の手順でセットアップ & プログラムを実行します。

$ sudo apt-get install php

$ php --version
PHP 7.3.19-1~deb10u1 (cli) (built: Jul  5 2020 06:46:45) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.19, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.19-1~deb10u1, Copyright (c) 1999-2018, by Zend Technologies

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

$ composer --version
Composer version 1.10.9 2020-07-16 12:57:00

$ cd php/
$ composer install
$ sudo php main.php

PHP でLチカのやりかたを検索すると、Webページから LED の ON/OFF を制御するみたいな記事が結構出てきます。かなり面白そうなので、これもその内試してみたいなと思いました。

Ruby

Ruby でもやっていきますー。

require 'bundler/setup'
require 'pi_piper'

led_pin = PiPiper::Pin.new :pin => 23, :direction => :out

begin

  loop do
    led_pin.on
    sleep 1
    led_pin.off
    sleep 1
  end

rescue Interrupt

  PiPiper::Platform.driver.unexport_pin(23)

end

pi_piper というライブラリを使用しています。

下記の手順でセットアップ & プログラムを実行します。

$ sudo apt-get install ruby

$ ruby --version
ruby 2.5.5p157 (2019-03-15 revision 67260) [arm-linux-gnueabihf]

$ sudo apt-get install ruby2.5-dev libssl-dev
$ sudo gem install bundler

$ gem --version
2.7.6.2

$ bundler -v
Bundler version 2.1.4

$ cd ruby/
$ bundle install
$ sudo ruby main.rb

Perl

Perl は人生で初めて触りました。cpan の使い方がよく分からず結構ハマりました。

#! /usr/bin/perl

use strict;
use warnings;

use RPi::WiringPi;
use RPi::Const qw(:all);

my $end = 0;
my $pi = RPi::WiringPi->new;

my $led_pin = $pi->pin(23);
$led_pin->mode(OUTPUT);

sub sigint_handler {
  $end = 1;
}
$SIG{'INT'} = 'sigint_handler';

while (!$end) {
  $led_pin->write(OFF);
  sleep(1);
  $led_pin->write(ON);
  sleep(1);
}

$pi->cleanup();

これまた WiringPi ですね。

下記の手順でセットアップ & プログラムを実行します。

$ cpan
cpan[1]> install RPi::WiringPi
cpan[2]> install RPi::Const
cpan[3]> exit

$ cd perl/
$ perl main.pl

おわりに

今回は、Raspberry Pi & 10個の言語を使ってLチカを試してみましたが、いかがだったでしょうか。個人的には色々なバリエーションを経験することができて、とても楽しかったです。需要があるのか分かりませんが、他の言語を使った第二弾もやりてえなああ〜とか思ったりしています(触ったことないけど Elixir とか?)。

前の記事でもご紹介しましたが、もっと Raspberry Pi を使った電子工作にチャレンジしてみたいと思った方には、ブルーバックス出版の Raspberry Piで学ぶ電子工作 作る、動かす、しくみがわかる! がオススメです。こちらの本では Python を使って、様々なセンサーを動かしながら、電子工作の基礎を学ぶことができますよ。

では、今回は以上になります。最後まで読んで頂きありがとうございました!