ラズパイ Zero W の本体温度を調べる【vcgencmd のインストール @ RaspberryPi OS Lite】

> RaspberryPi ZeroW で本体の測定温度を知りたい。しかし、RaspberryPi OS Lite (Buster) には **`vcgencmd` コマンドがない**。

[「`view temperature raspberry pi`」でググって](https://www.google.com/search?q=view+temperature+raspberry+pi&tbs=qdr:y)も「`vcgencmd` を使え」という記事ばかりで、`vcgencmd` コマンドを実行しようにも「コマンドが見つかりません」エラーが出ます。しかも、`apt search vcgencmd` して検索しても出てこないので、自分のググラビリティとして。

## TL; DR <sub><sup>（今北産業）</sup></sub>

1. RaspberryPi OS <ruby><strong>Lite</strong><rt>・・・・</rt></ruby> には、`vcgencmd` コマンドは含まれていない。
1. `libraspberrypi-bin` と `libraspberrypi0` を再インストールすると入る。
1. マスター、BIN ごとくれ。

    ```bash
    sudo apt-get --reinstall install libraspberrypi-bin
    sudo apt-get --reinstall install libraspberrypi0
    ```

- 本体温度を取得したいだけで、RPi Zero なので容量などの問題から `apt` で色々と余分なパッケージをインストールしたくない人は、[簡単な自作アプリをビルドして取得してみる](#%E5%B0%82%E7%94%A8%E3%82%A2%E3%83%97%E3%83%AA%E3%82%92%E3%83%93%E3%83%AB%E3%83%89%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)のもアリだと思います。アプリは 70KB 程度のサイズです。
- 併せて読みたい
    - [ラズパイ3B+をUSBブートしたい](https://qiita.com/DozyouMetoro/items/c711c92d937e23885c34) @ Qiita<br>ラズパイを Ubuntu で使うも、カーネルに含まれていない（ユーザーランドの）ラズパイ専用ドライバ類が必要な場合の参考に。

## TS; DR <sub><sup>（kwsk）</sup></sub>

基本的に `vcgencmd` コマンドは `libraspberrypi-bin` パッケージに含まれています。しかも、`libraspberrypi-bin` パッケージはデフォルトで Raspberry Pi OS（旧 Raspbian）にインストールされています。

ところが、[`vcgencmd` コマンドはラズパイの VideoCore GPU の情報を取得するためのコマンド](https://www.raspberrypi.com/documentation/computers/os.html#vcgencmd)であるため、<font color="darkorange">**GUI なしの Lite 版には肝心の `vcgencmd` コマンドはインストールされません**</font>。

では、`libraspberrypi-bin` パッケージを入れ直せば良いかというとダメで、`vcgencmd` は入るものの、コマンドがハードウェアにアクセスするのに必要な ARM 用のドライバが足りません。つまり、**ARM チップに対応した `libvchiq_arm.so`もさらに必要**です。

Raspberry Pi Zero 用の `libvchiq_arm.so` ファイルは `libraspberrypi0` パッケージに含まれているので、`libraspberrypi0` パッケージも再インストールします。

これにより `/opt/vc` 下に必要なモジュール（`.so` ファイル）一式が入ります。

:::note info
`libvchiq_arm.so` のソースコードは、以下のリポジトリから参照できます。

- https://github.com/raspberrypi/firmware/tree/master | Firmware | RaspberryPi @ GitHub

Raspberry Pi 用のカーネル、モジュール、Userland ライブラリ、ブートローダー、GPU ドライバ（ファームウェア）などもあります。

とはいえ、`libraspberrypi-bin` と `libraspberrypi0` で、ラズパイ用のハードウェア関連の大半のコマンドとドライバがインストールされます。

:::

---

問題を見つけるまでのログ（手順）はこちら。

```shell-session:コマンドが見つかりません
$ vcgencmd measure_temp
-bash: vcgencmd: コマンドが見つかりません

$ /opt/vc/bin/vcgencmd measure_temp
-bash: /opt/vc/bin/vcgencmd: そのようなファイルやディレクトリはありません

$ /usr/bin/vcgencmd
-bash: /usr/bin/vcgencmd: そのようなファイルやディレクトリはありません
```
```shell-session:libraspberrypi-binパッケージは入っている
$ apt search libraspberrypi-bin
ソート中... 完了
全文検索... 完了  
libraspberrypi-bin/oldstable,now 1:1.20220308~buster-1 armhf [インストール済み]
  Miscellaneous Raspberry Pi utilities

libraspberrypi-bin-dbgsym/oldstable 2+git20211125~155417+14b90ff-1~buster armhf
  debug symbols for libraspberrypi-bin
```

```shell-session:libraspberrypi-binパッケージを再インストールするも.soがないそうです
$ sudo apt-get --reinstall install libraspberrypi-bin
** snip **
取得:1 http://archive.raspberrypi.org/debian buster/main armhf libraspberrypi-bin armhf 1:1.20220308~buster-1 [342 kB]
342 kB を 2秒 で取得しました (189 kB/s)      
(データベースを読み込んでいます ... 現在 50400 個のファイルとディレクトリがインストールされています。)
.../libraspberrypi-bin_1%3a1.20220308~buster-1_armhf.deb を展開する準備をしています ...
libraspberrypi-bin (1:1.20220308~buster-1) で (1:1.20220308~buster-1 に) 上書き展開しています ...
libraspberrypi-bin (1:1.20220308~buster-1) を設定しています ...

$ vcgencmd measure_temp
vcgencmd: error while loading shared libraries: libvchiq_arm.so: cannot open shared object file: No such file or directory
```

`libvchiq_arm.so` は、何なのか調べると、以下の公式リポジトリがヒットしました。

https://github.com/raspberrypi/firmware/blob/master/opt/vc/lib/libvchiq_arm.so

[リポジトリを見ると](https://github.com/raspberrypi/firmware)、Raspberry Pi OS のファームウェアのうち、[userland](https://en.wikipedia.org/wiki/User_space_and_kernel_space) で動作するモジュール（カーネルにデフォルトで同梱されない `.so` ファイル）で、VideoCore（GPU）に関連するもの（`/opt/vc/` に設置されるもの）のようです。

では、どのパッケージに、これらモジュール（`/opt/vc`, `libvchiq_arm.so`）が含まれているのか探してみました。すると、ラズパイの公式フォーラムに別件で「`libvchiq_arm.so` をインストールしたい」というケースを見つけました。

- [libvchiq_arm.so /opt/vc problem](https://forums.raspberrypi.com/viewtopic.php?t=304833#p1824986) @ orums.raspberrypi.com

どうやら、外付け SSD を USB ブートする際にも `libvchiq_arm.so` を求められるようで、`libraspberrypi0` のパッケージに含まれているっぽい。情報をみると、確かにそれっぽい。

```shell-session:libraspberrypi0のパッケージ情報
$ sudo apt info libraspberrypi0
Package: libraspberrypi0
Version: 1:2+git20230322~143557+9d5250f-1
Priority: optional
Section: libs
Source: raspberrypi-userland
Maintainer: Serge Schneider <serge@raspberrypi.org>
Installed-Size: 542 kB
Depends: raspberrypi-bootloader, libc6 (>= 2.17)
Homepage: https://github.com/raspberrypi/userland
Download-Size: 167 kB
APT-Sources: http://archive.raspberrypi.org/debian bullseye/main armhf Packages
Description: Libraries for the Raspberry Pi's VideoCore IV
 This package contains MMAL and other libraries for the Raspberry Pi's
 VideoCore IV multimedia processor.
```


```shell-session:libraspberrypi0パッケージを入れると動きました
$ sudo apt-get --reinstall install libraspberrypi0
** snip **
取得:1 http://archive.raspberrypi.org/debian buster/main armhf libraspberrypi0 armhf 1:1.20220308~buster-1 [847 kB]
847 kB を 4秒 で取得しました (230 kB/s)                               
(データベースを読み込んでいます ... 現在 50400 個のファイルとディレクトリがインストールされています。)
.../libraspberrypi0_1%3a1.20220308~buster-1_armhf.deb を展開する準備をしています ...
libraspberrypi0 (1:1.20220308~buster-1) で (1:1.20220308~buster-1 に) 上書き展開しています ...
libraspberrypi0 (1:1.20220308~buster-1) を設定しています ...

$ vcgencmd measure_temp
temp=51.4'C
```

## 専用アプリをビルドしてみる

本体の CPU 温度を取得したいだけなのに、パッケージを使ってインストールすると、余分なコマンドなどもインストールされてしまいます。

そんな時には、[オリジナルのソースコード](https://github.com/raspberrypi/utils/blob/master/vcgencmd/vcgencmd.c)を参考に、`measure_temp` の機能だけに特化したコマンドを作ると便利です。

`gcc` がインストールされていない場合は、別途インストールが必要ですが、以下のコードを `get_temp.c` にテキスト保存し、`gcc` でコンパイルすれば CPU 温度の取得に特化したアプリが作成できます。

```c:get_temp.c
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the copyright holder nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdlib.h>

#define DEVICE_FILE_NAME "/dev/vcio"
#define MAJOR_NUM 100
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
#define MAX_STRING 1024
#define GET_GENCMD_RESULT 0x00030080

static int mbox_property(int file_desc, void *buf) {
    int ret_val = ioctl(file_desc, IOCTL_MBOX_PROPERTY, buf);
    if (ret_val < 0) {
        perror("ioctl mbox_property failed");
    }
    return ret_val;
}

static int mbox_open() {
    int file_desc = open(DEVICE_FILE_NAME, 0);
    if (file_desc < 0) {
        perror("Cannot open device file");
        exit(EXIT_FAILURE);
    }
    return file_desc;
}

static void mbox_close(int file_desc) {
    close(file_desc);
}

unsigned gencmd(int file_desc, const char *command, char *result, int result_len) {
    unsigned p[(MAX_STRING >> 2) + 7];
    int i = 0;
    int len = strlen(command);

    if (len + 1 >= MAX_STRING) {
        fprintf(stderr, "gencmd length too long: %d\n", len);
        return -1;
    }

    // Prepare the buffer for the mailbox request
    p[i++] = 0; // size (will be filled later)
    p[i++] = 0x00000000; // process request

    p[i++] = GET_GENCMD_RESULT; // the tag id
    p[i++] = MAX_STRING; // buffer size
    p[i++] = 0; // request length (initially zero)
    p[i++] = 0; // error response (initially zero)

    memcpy(p + i, command, len + 1); // copy the command
    i += (MAX_STRING >> 2); // adjust index

    p[i++] = 0x00000000; // end tag
    p[0] = i * sizeof(*p); // actual size of the message

    // Send the request to the VideoCore and get the response
    mbox_property(file_desc, p);

    // Copy the result from the response
    result[0] = '\0';
    strncat(result, (const char *)(p + 6), result_len);

    return p[5]; // Return the result status (0 on success)
}

int main() {
    int mb = mbox_open();
    char result[MAX_STRING] = {};

    // Send the "measure_temp" command to the VideoCore
    int ret = gencmd(mb, "measure_temp", result, sizeof(result));
    if (ret) {
        printf("Error retrieving temperature: %d\n", ret);
    }

    mbox_close(mb);

    // Output the result
    printf("Temperature: %s\n", result);

    return ret;
}
```

```shell-session:get_temp アプリのコンパイルと実行例
$ gcc -o get_temp get_temp.c

$ ./get_temp
Temperature: temp=42.9'C
```

### 動作環境

```shell-session:いんふぉ
$ # モデル名
$ more /proc/device-tree/model
Raspberry Pi Zero W Rev 1.1

$ # OS バージョン
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
```

## 参考文献

- [vcgencmd](https://www.raspberrypi.com/documentation/computers/os.html#vcgencmd) | Useful Utilities | Raspberry Pi OS @ Raspberry Pi Documentation
- [libvchiq_arm.so /opt/vc problem](https://forums.raspberrypi.com/viewtopic.php?t=304833) | Raspberry Pi Forums @ forums.raspberrypi.com
- [[SOLVED]vcgencmd not working](https://forums.raspberrypi.com/viewtopic.php?p=1066341&sid=d39e9b337637717945d180d57121b6fd#p1066341) | Raspberry Pi Forums @ forums.raspberrypi.com