Raspberry Pi 4 Model BをDCファンで自動的に冷却する

## １．はじめに
#### 1.1 使用したもの
* Raspberry Pi 4 Model B 4GB (rev 1.4)
* OS：Bookworm(64-bit) Desktop Release 5.3 July 2024
* 開発環境　gcc(ver. 12.2.0), Geany(ver. 1.38)
* 電子部品（表2を参照）

　Raspberry Pi 4 Model B(以下、Pi 4B)は、国内では2019年11月にリリースされました[1]。現在、後継のRaspberry Pi 5がリリースされていますが、Pi 4Bは消費電力が少ないため、特定の用途や条件においては適している場合もあります。Pi 4Bはロングセラーモデルであり、いくつかのリビジョンがあります[2]。
　Pi 4Bのクロック周波数は可変ですが、リビジョンごとに最大値が異なります。リビジョンは `cpuinfo` コマンドまたは `pinout` コマンドで確認可能です。また、`lscpu`コマンドでは、CPUのクロック周波数の最小値と最大値を確認することができます。
　また、Pi 4BのPMIC(Power Management Integrated Circuit)周辺の実装部品の違いから、rev 1.2とrev 1.4以降を目視で区別することもできます[3]。

    $ cat /proc/cpuinfo
    $ lscpu


表１　リビジョンごとのCPUのクロック周波数の最小値と最大値
|   リビジョン   |    最小値　   |最大値|
|:--------------:|:----------:|:----------:|
| rev 1.2|600MHz|   1.5GHz    |
|rev 1.4|600MHz|1.8GHz|
|rev 1.5|600MHz|1.8GHz|


　リビジョン rev 1.2のPi 4Bでも、`/boot/firmware/config.txt`ファイルの`[all]`セクションに次の行を追加することで、1.8GHzで動作させることがRaspberry Pi財団により公式にアナウンスされています[4]。
 
    arm_freq=1800

　ただし、クロック周波数を変更すると起動しない場合があるため、あらかじめSDカードのバックアップを作成しておくことをお勧めします。
 
## ２．サーマルスロットリングとは
　Raspberry Pi（以下、ラズパイ）の高性能化に伴い、消費電力が増え、SoC(System on Chip)の発熱も増大しています。SoCとは、CPU、GPU、SDRAM、USB、I/Oなどの回路を１つの半導体チップに集積したものです[5]。発熱の主な要因は、高性能化を目的としたクロック周波数の高速化です。主な発熱の要因は、高性能化を目的としたクロック周波数の高速化です。Raspberry Pi 4 Model B（以下、Pi 4B）では、SoCとPMIC周辺の温度が上昇します（図1）[6]。

図1　発熱時のRaspberry Pi 4 Model Bのサーモグラフィ（参考文献[6]から引用）
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/cee48de6-d546-4f48-b148-9686e8def44d.jpeg">

　Pi 4BのSoCは、64bit CPUを4個内蔵したCortex-A72を採用し、クロック周波数は600MHZ～1.8GHzです。CPU使用率が低いときは600MHzですが、CPU使用率が100%のときは1.8GHzとなります。図2はList1のソースコードにおいて、`stress-ng`行をコメントアウトしてCPUに高い負荷を加えない状態で、SoCの温度とクロック周波数を７分間計測したグラフです（室温28℃）。SoCの温度は60℃前後で、クロック周波数はプログラムを実行開始したときと途中数か所で1.8 GHzが観測されましたが、主に600 MHzまたは700 MHzでした。

図2 CPU使用率が低いときのSoCの温度（約60℃）とクロック周波数（600-700MHz）
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/a9f0c4d0-2a40-cfad-3162-961fcbcea874.png">

　一般的に、CPUの温度が上昇し過ぎたときにクロック周波数を落として温度を下げる保護機能を「サーマルスロットリング」と呼びます[7]。ラズパイのSoCの温度が80℃を超えると、SoCを保護するためにサーマルスロットリングが機能してクロック周波数を段階的に600MHzまで低下させ、SoCの温度を下げます。図3は、List1のソースコードを実行してCPU使用率を100%にしてDCファンを停止した状態で、サーマルスロットリングが機能したときのSoCの温度とクロック周波数を測定したグラフです。SoCの温度が約83℃を超えたあたりで、クロック周波数が下がり始め、97秒後に600MHzまで下がりました。その後、SoCの温度が約85℃を超えないように、クロック周波数が激しく変動しながら制御されています。
 
図3 CPU使用率を100%のとき、サーマルスロットリングが動作しているSoCの温度（約83℃）とクロック周波数（600MHz-1.5GHz）
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/098d1b76-7d4b-e069-1621-4cec55b7fa0d.png">

　サーマルスロットリングが機能してSoCを過熱しないように保護していることが確認できましたが、クロック周波数が下がるとCPUのパフォーマンスも低下します。CPUに高い負荷をかけてもクロック周波数を低下させたくない場合は、DCファンなどでSoCを冷却する必要があります。常時DCファンを動作させるのはシンプルですが、ファンの風切り音が気になることもあります。Raspberry Pi OSの設定には、設定した温度でDCファンを動作させる機能があります。ただし、GPIOでは直接ファンを駆動できませんので、トランジスタなどを使用した駆動回路が必要です。


## ３．DCファンの設定と駆動回路
　ここでは、DCファンの駆動回路とその実装方法について説明します。
#### 3.1 ラズパイの設定
　ファンの設定は、以下の手順で行います。
　[Menu]⇒[設定]⇒[Raspberry Piの設定]を開き、「パフォーマンス」タブをクリックします。ファンのスイッチをオンにすると、「ファンGPIO」と「ファン温度」を設定できます。
* GPIO番号は、2～27の範囲で指定可能です。ここではGPIO19を使用します。
* ファン温度は、ファンが動作する温度です。60℃～120℃の範囲で5℃刻みで設定できます。本実験的にサーマルスロットリングが機能する直前の80℃に設定します。

図4 ファンの設定画面
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/e98a5382-6607-fa56-b4d8-7276f2249757.png">

　次に、タスクバーにSoCの温度モニタとCPU使用量モニタを表示させます（図5）。マウスポインタをタスクバーに合わせて右クリックし、表示されたダイアログボックスの[プラグインを追加/削除]をクリックします。次に、「プラグインの追加/削除」ダイアログで[CPU温度]を選び、[右側に追加]をクリックします。同様に、[CPU]を選択して、[右側に追加]をクリックします。最後に、[OK]をクリックします。これで、タスクバーの右側に、CPUの温度と使用率のプラグインが追加されます。

図5 温度モニタとCPU使用量モニタ
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/5c01f2b1-e17c-911b-fa32-18f21d6e191f.png">

#### 3.2 DCファン駆動回路の設計
　DCファンを駆動する回路を図6に示します。DCファンはGPIO19の出力信号で制御されます。
* GPIO19の出力がHIGHのとき、DCファンが動作します。
* GPIO19の出力がLOWのとき、DCファンが停止します。
 
図6 DCファンの駆動回路図
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/2c08274e-d03d-38d6-20de-19be8a9def4b.png">

　DCファン駆動回路に必要な部品を表2に示します。備考には入手情報を記載していますが、持ち合わせの部品や相当品を使用しても問題ありません。使用するDCファンの型番はYDH3007C05Fで、定格電圧は+5V、定格電流は200mAです。小信号用トランジスタとして2CS1815がよく使用されますが、コレクタ電流の最大値が150mAであるため、YDH3007C05Fの定格電流に対応できません。そのため、2CS1213を使用します。

表2 部品表
| 名　称 |   個　数     |備　考（参考：秋月電子通商）          | 
|:---------|:--------:|:---------| 
| Raspberry Pi 4B ケース    | 1    |OKdoのRapsberry Pi 4 Model B用ケース     | 
| DCファン YDH3007C05F    |1     |5V/0.2A、 販売コード:115655     | 
| 両端ロングピンヘッダ6ピン    | 1    |FANのコネクタとブレッドボードへの固定用、販売コード:109055     | 
|トランジスタ  2SC1213-D|1|コレクタ電流500mA、販売コード:113826 |
|ダイオード  1N4007|1|汎用整流用ダイオード、販売コード:100934|
|ミニブレッドボード|1|販売コード:105155|
|抵抗　1kΩ 1/4W|1||
|ジャンパーワイヤ|適量|オス-メス、オス-オス|

【参　考】
　抵抗R1を1kΩに選定した理由は、次の計算によりDCファンYDH3007C05Fを駆動するためのコレクタ電流を十分に確保するためです。次式から、トランジスタのコレクタ電流は、DCファンの定格電流IFAN(200mA)の倍近く確保できることがわかります。ベース電流からコレクタ電流を求め、コレクタ損失からトランジスタの発熱量を確認します。
 
* ベース電流(IB)の計算、VH:GPIOがHIGHのときの電圧、VBE：ベース・エミッタ電圧(max)
 ```math
I_B = \frac{(V_H - V_{BE})}{R1} = \frac{(3.3 - 0.75)}{1000} = 2.55 \text{ mA}
 ```
* コレクタ電流(IC)の計算、hFE :直流電流増幅率(min)
 ```math
I_C = h_{FE} \times I_B = 160 \times 2.55 \text{ mA} = 408 \text{ mA}
 ```
* コレクタ損失(PC)の計算、VCE :コレクタ・エミッタ飽和電圧(max)、IFAN :DCファンの定格電流
 ```math
P_C = V_{CE} \times I_{FAN} = 0.6 \times 200 \text{ mA} = 120 \text{ mW}
 ```
　2SC1213のデータシートより許容コレクタ損失(400mW)の半分以下であることが分かります[8]。

#### 3.3 駆動回路の配線とDCファンの取り付け
　図6のDCファンの駆動回路図をブレッドボードに配線します（図7）。ブレッドボードの使用方法については参考文献[9]を参照してください。
* DCファンのリード線はメスのコネクタで終端されているので、両端ロングピンヘッダを使用してブレッドボードに挿入します。リード線には極性があるため、逆に接続するとファンが回転しないので注意が必要です。
* 図8に示すように、トランジスタのリードを少し広げて型番が手前から見えるようにブレッドボードに挿入します。
* ダイオードには極性があります（図8）。カソードを示す白い帯を右側にしてリードをコの字に曲げます。ダイオードのアノードをトランジスタのコレクタ側に挿入し、カソード側をDCファンの赤いリード線側に挿入します。
* 黄色のジャンパーワイヤ(オス-オス)で、トランジスタのコレクタとDCファンの黒いリード線側に配線します。
* 赤色のジャンパーワイヤ(オス-メス)で、Pi 4Bの拡張コネクタの+5Vをファンの赤いリード線側に配線します。`pinout`コマンドで拡張コネクタのピン番号と信号名の情報を表示できます。  
* 黒色のジャンパーワイヤ(オス-メス)で、Pi 4Bの拡張コネクタのGNDをトランジスタのエミッタ側に配線します。
* 1kΩの抵抗のリードをコの字に曲げて、トランジスタのベースとブレッドボードの空いているピンに挿入します。
* 青色のジャンパーワイヤ(オス-メス)で、Pi 4Bの拡張コネクタのGPIO19を抵抗の左側のリード側に配線します。

図7 配線図
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/c333594b-bcff-6f05-93f7-ae3f4722b9b7.png">

図8 トランジスタとダイオードのピン配置
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/c38dad53-3693-dc12-0a80-ba35d253bdad.png">

　駆動回路の配線が完成したら、ブレッドボードとDCファンをPi 4Bのケースに取り付けます（図9）。ケース天板のOKdoロゴがあり、通気口として機能してSoCに近い位置にあります。DCファンの吹き出し口は商品ラベル側になるので、ラベル側をロゴ側に向けて接着剤などで固定します。ブレッドボードの底面に貼られているシールを剥がし、粘着テープで天板に固定します。

図9 DCファンと電子回路を実装した写真
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/4dff204f-8e41-a1f9-e745-504a018e474f.jpeg">

## ４．動作検証
　使用するツールのインストールと検証用のソースコードについて説明します。
#### 4.1 stress-ngとgnuplotのインストール
　検証用プログラムでは、`stress-ng`コマンドを使用してCPUの使用率を100%にして、SoCの温度を上昇させます[10]-[11]。まず、`stress-ng`をインストールします。

    sudo apt install stress-ng -y
 
　次に、ソースコードでは測定の終了後に、SoCの温度とクロック周波数のグラフを`gnuplot`で表示します[12]-[13]。`gnuplot`もインストールします。

    sudo apt install gnuplot -y


##### 4.2 検証用ソースコード
　C言語で記述した検証用ソースコードをList 1に示します。このソースコードでは、1秒ごとにSoCの温度とクロック周波数を測定し、csvファイルに記録します。csvファイル名は日時を基に生成されるため、計測ごとに独立したファイルが作成されます。計測時間はマクロ定義した`COUNT`で指定します。計測終了後には、`gnuplot`を使って結果をグラフで表示します。
　ソースコードのポイントは以下の通りです。
 * SoCの温度とクロック周波数は`vcgencmd`コマンドを利用して取得します。`vcgencmd`はRaspberry Pi OSに標準で装備されています[14]。また、`vcgencmd`で測定したデータを`egrep`を利用して変数に格納します[15]。
 * `system`関数を利用して、`stress-ng`の開始と終了を行っています[16]。
 * 測定終了後、`popen`関数で`gnuplot`を呼び出して、`gnuplot`のコマンドを`fprintf`関数で渡しています。

```c:List 1: MsrTmpFrq.c
#include <stdio.h>      //fgets,popen,printf,etc
#include <stdlib.h>     //strtol,etc
#include <unistd.h>     //sleep
#include <time.h>       //time,strftime,localtime
#define COUNT   420     //計測回数 秒単位

int main(void)
{
    FILE *csvp;         //csvファイル・ストリームのポインタ
    FILE *ppnp;         //popenパイプ・ストリームのポインタ
    FILE *gpltp;        //gnuplot用のポインタ
    int     i;
    char str[256];      //作業用文字列
    char *endptr;       //strtofとstrtolの関数で使用
    char fileCsv[32];   //csvファイル名用
    float temp;         //温度データ
    long freq;          //CPUクロック周波数
    char *cmd_temp = "vcgencmd measure_temp | egrep -o '[0-9][0-9].[0-9]*'";    //温度データ文字列
    char *cmd_freq = "vcgencmd measure_clock arm | egrep -o '[0-9]{9,10}'";     //周波数文字列9桁以上、10桁以下

    //csvファイルのオープン。gnuplotでグラフ作成用データファイルとして使用。
    //ファイル名日時形式 %Y%m%d%H%M%S.csv
    time_t now;             //時刻のデータ型
    now = time(NULL);       //システム時刻の取得
    strftime(fileCsv, sizeof(fileCsv), "%Y%m%d%H%M%S.csv",localtime(&now));
    if((csvp = fopen (fileCsv,"w"))==NULL){
        printf("file open error.\n");
        return EXIT_FAILURE;
    }

    //4個のCPUにfft計算で100%の負荷を加える
    system("stress-ng --cpu 4 --cpu-method fft &");

    for(i = 1; i <= COUNT; i++){
        ppnp = popen(cmd_temp, "r");    //CPU温度の測定
        fgets(str, sizeof(str), ppnp);  //文字をパイプ・ストリームから読み込み、strに保存する。
        temp = strtof(str, &endptr);    //文字列をfloat値に変換
        pclose(ppnp);

        ppnp = popen(cmd_freq, "r");    //CPUクロック周波数の測定
        fgets(str, sizeof(str), ppnp);
        freq= strtol(str, &endptr, 10); //文字列をlong型整数値に変換
        pclose(ppnp);

        //ターミナルに連番、温度℃、周波数MHzを表示する
        sprintf(str,"%d, %.1f, %ld",i,temp,freq/1000000);  //文字列化
        printf("%s\n",str);
        fprintf(csvp,"%s\n",str);                           //csvファイルに記録
        sleep(1);
    }

    fclose(csvp);
    system("sudo pkill stress-ng");      //プロセス名を指定して終了させる
    printf("測定終了\n");

/* グラフの作成   */
    gpltp = popen("gnuplot -persist","w");
    fprintf(gpltp, "set key right bottom \n");
    fprintf(gpltp, "set xlabel \"時間(秒)\"\n");
    fprintf(gpltp, "set ylabel \"温度(℃)\"\n");
    fprintf(gpltp, "set yrange[50:100]\n");         //50℃から100℃
    fprintf(gpltp, "set y2label \"周波数(MHz)\"\n");
    fprintf(gpltp, "set y2tics \n");
    fprintf(gpltp, "set y2range[500:2000] \n");     //500MHzから2000MHz
    fprintf(gpltp, "plot \"%s\" using 1:2 axis x1y1 with line title \"SoC温度\"\n", fileCsv);
    fprintf(gpltp, "replot \"%s\" using 1:3 axis x1y2 with line title \"CPU周波数\"\n", fileCsv);
    fprintf(gpltp,"exit\n");
    pclose(gpltp);
    return EXIT_SUCCESS;
}


```
#### 4.2 コンパイルと実行結果
　ビルドと実行の手続きは以下の通りです。
 
    gcc -Wall -o MsrTmpFrq MsrTmpFrq.c -O0
    sudo ./MsrTmpFrq

　実行結果を図10に示します。CPU使用率を100%に設定すると、SoCの温度が約80℃を超えた辺りからDCファンが作動し、SoCを冷却します。温度が約70℃まで下がるとDCファンが停止します。その後、SoCの温度が上昇して、DCファンのオンとオフが繰り返されます。その間、クロック周波数は一度も下げることもなく、1.8GHzを維持していることが確認されました。
 
図10 CPU使用率を100%のときのSoCの温度とクロック周波数
<img width="360" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631966/6148ce6d-8dff-1843-4b89-d052ab207265.png">


　測定したSoCの温度とクロック周波数はcsvファイルに保存されます。List2のプログラムで、csvファイルを指定してグラフを表示することができます。
```c:List 2: DspGnplt.c
#include <stdio.h>      //fgets,popen,printf,etc
#include <stdlib.h>     //strtol,etc

int main(void)
{
    FILE *gpltp;        //gnuplot用のポインタ
    char fileCsv[32];   //csvファイル名用
	printf("Enter Csv file name = ");
    scanf("%s",fileCsv);

/* グラフの作成   */
    gpltp = popen("gnuplot -persist","w");
    fprintf(gpltp, "set key right bottom \n");
    fprintf(gpltp, "set xlabel \"時間(秒)\"\n");
    fprintf(gpltp, "set ylabel \"温度(℃)\"\n");
    fprintf(gpltp, "set yrange[50:100]\n");         //50℃から100℃
    fprintf(gpltp, "set y2label \"周波数(MHz)\"\n");
    fprintf(gpltp, "set y2tics \n");
    fprintf(gpltp, "set y2range[500:2000] \n");     //500MHzから2000MHz
    fprintf(gpltp, "plot \"%s\" using 1:2 axis x1y1 with line title \"SoC温度\"\n", fileCsv);
    fprintf(gpltp, "replot \"%s\" using 1:3 axis x1y2 with line title \"CPU周波数\"\n", fileCsv);
    fprintf(gpltp,"exit\n");
    pclose(gpltp);
    return EXIT_SUCCESS;
}
```

## ６．おわりに
　DCファンとトランジスタ回路で、`stress-ng`コマンドでCPU使用率を100%に設定しても、クロック周波数が低下しないことを確認しました。本実験ではSoCの温度上昇の変化を確認するためにヒートシンクを取り付けていませんが、ヒートシンクを取り付けることでさらに効果的な冷却が期待できます。また、図4では実験的にDCファンが動作する温度を80℃に設定しましたが、実際には余裕をもって60℃や70℃で動作させるのが良いでしょう。
　なお、DCファンの動作を停止すると、図3に示すようにサーマルスロットリングが働くことを確認できますが、Pi 4Bに強いストレスを与えることになります。不具合の原因になる可能性がありますので、追試を行う際は注意してください。
 
## Reference
[1] Raspberry Pi 4 Tech Specs, https://www.raspberrypi.com/products/raspberry-pi-4-model-b/specifications/
[2] Product Information Portal, https://pip.raspberrypi.com/categories/560-pcn
[3] Free upgrade: Raspberry Pi 4 now with 1.8GHz instead of 1.5GHz, https://picockpit.com/raspberry-pi/free-upgrade-raspberry-pi-4-now-with-1-8ghz-instead-of-1-5ghz/
[4] Bullseye bonus: 1.8GHz Raspberry Pi 4, https://www.raspberrypi.com/news/bullseye-bonus-1-8ghz-raspberry-pi-4/  (Nov.2021)
[5] BMC2711, https://www.raspberrypi.com/documentation/computers/processors.html#bcm2711
[6] Alex Bate,“Thermal testing Raspberry Pi 4, https://www.raspberrypi.org/blog/thermal-testing-raspberry-pi-4/ (Nov.2019)
[7] CPUのサーマルスロットリングとは？, https://pcinformation.info/cpu/thermal-throttling.html (Oct.11,2019)
[8] 2SC1213 datasheet, JIANGSU CHANGJIANG ELECTRONICS TECHNOLOGY CO., LTD (Aug. 2017)
[9] 「ブレッドボード」ってどう使うの？，https://shop.sunhayato.co.jp/blogs/problem-solving/breadboard
[10] stress-ngコマンドの使い方, https://hana-shin.hatenablog.com/entry/2022/03/21/221131 (Mar.2022)
[11] stress-ng, https://manpages.ubuntu.com/manpages/oracular/en/man1/stress-ng.1.html
https://linuxhint.com/raspberry_pi_temperature_monitor/
[12] gnuplot homepage, http://www.gnuplot.info/ (May 2024)
[13] 半場滋, Gnuplot入門第0.08版, http://dsl4.eee.u-ryukyu.ac.jp/DOCS/gnuplot/gnuplot.html (Aug. 2007)
[14] vcgencmd, https://www.raspberrypi.com/documentation/computers/os.html#vcgencmd
[15] Shahriar Shovon, Raspberry Pi Temperature Monitor, https://linuxhint.com/raspberry_pi_temperature_monitor/
[16]【 pkill 】コマンド――名前を指定してプロセスを終了させる, https://atmarkit.itmedia.co.jp/ait/articles/1708/03/news014.html (Aug. 2017)
[17] popen関数でコマンドを実行, https://hooktail.org/computer/index.php?popen%B4%D8%BF%F4%A4%C7%A5%B3%A5%DE%A5%F3%A5%C9%A4%F2%BC%C2%B9%D4 (Dec. 2006)