手持ちのラズパイ3B+をNASにし、DLNAサーバーとしても運用したい。
手持ちの5インチディスプレイを使用し、監視システム+α(時計とか天気とか)の表示をさせたい。chatGPTに相談しながら作成してみる。
1.サーバー監視
・ストレージ使用率(総容量、使用量、空き、〇%超で色変化)
・ディスク状態(温度、SMART異常の有無)
・ネットワーク状況(リアルタイム転送速度、接続クライアント数)
・CPU/メモリ使用率(RPi3の負荷監視)
・稼働時間(再起動忘れ防止)
2.+α
・現在時間、カレンダー
・天気情報
・接続中ユーザー表示
・最近のファイル更新ログ
・バックアップ状態
・フォトフレーム(NAS内の写真を表示)
・音楽再生情報(再生中の曲名)
・ニュースヘッドライン
3.状態異常の強調表示
・温度〇度以上の時、ストレージ使用率〇%以上、ネットワーク切断 など
構想:
まず、1のサーバー監視を作成し、順次+αのうちいずれかを表示できるように考えたい。
→python+FlaskでシンプルWeb表示
環境構築:
OS:Debialn13 (コードネームtrixie)
ラズベリーパイ3B+
KUMAN 5インチディスプレイ(800×480)
sambaとminidlnaの導入
①HDDの接続確認
$ lsblk出力例)
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 3.6T 0 disk
┗sda1 8:1 0 3.6T 0 part /media/pi
②HDDのファイル形式確認
$ sudo blkid出力例)
/dev/sda1: LABEL="HD-ADU3" BLOCK_SIZE="512" UUID="XXXXXXXXX" TYPE="ntfs"
※UUIDは後で使います。ntfsでフォーマットされています。
③必要なソフトのインストール、まとめて実行
$ sudo apt update$ sudo apt install samba minidlna ntfs-3g exfat-fuse -y
最新バージョン(2026.5.10時点)
samba is already the newest version (2:4.22.8+dfsg-0+deb13u1).
minidlna is already the newest version (1.3.3+dfsg-1.1+b1).
ntfs-3g is already the newest version (1:2022.10.3-5+deb13u1).
exfat-fuse is already the newest version (1.4.0-3+b2).
④HDDのマウント先を作る
$ sukdo mkdir -p /mnt/hdd⑤HDDを手動でマウント
$ sudo mount -t ntfs-3g /dev/sda1 /mnt/hdd下記のエラーが出る場合
Mount is denied because the NTFS volume is already exclusively opened.
The volume may be already mounted, or another software may use it which
could be identified for example by the help of the 'fuser' command.
すでに /dev/sda1がどこかに接続されているためにおこるエラー。接続先を調べるには
$ mount | grep sda1
表示例)
/dev/sda1 on /media/pi/HD-ADU3 type ntfs3
→/media/piに接続されているから /mnt/hddには接続できないので、一旦外す。
※/media/pi は一時的な接続先となる。USBメモリの接続など。そのため、毎回必ず「/media/pi/HD-ADU3」となるとは限らない。サーバー用途などでは、「/mnt/hdd」だけではなく「/srv/storage」なども良い。
$ sudo umount /media/pi/HD-ADU3
表示例)
sda 8:0 0 3.6T 0 disk
┗sda1 8:1 0 3.6T 0 part
①の表示例、(┗sda1 8:1 0 3.6T 0 part /media/pi)と比べるとマウントポイントが消えていることが分かる。
再度手動でマウントする。
$ sudo mount -t ntfs-3g /dev/sda1 /mnt/hdd
⑥成功確認
$ ls /mnt/hddHDDの中のファイル一覧が見えれば成功
⑦自動マウント設定
UUIDを確認$ sudo blkid
表示例)
/dev/sda1: UUID="340815D408159648"
fstab編集
$ sudo nano /etc/fstab
一番下に下記の一文を追加
UUID=上記のUUID /mnt/hdd ntfs-3g defaults,uid=1000,gid=1000,umask=000 0 0
上記のUUID:このHDDを使う
/mnt/hdd:ここにHDDを接続する
ntfs-3g:NTFSを読み書きできるドライバを使う
defaults:Linux標準設定。(読み書き可能、自動マウント、実行許可、デバイス許可など)
uid=1000:ユーザー番号1000の所有物にする。ラズパイではpiがUID1000。
gid=1000:グループ番号。通常piのグループ。
umask=000:全員フルアクセスOK。セキュリティ的には自宅内限定。
0:dumpという古いバックアップ機能、現代ではほぼ使わない。
0:fsckという起動チェック設定。LinuxではNTFSは完全修復できないので0でOK。
つまり、「UUIDが〇〇のNTFS HDDを、起動時に /mnt/hddへ接続し、piユーザーが自由に読み書きできるようにする。」という意味。
⑧Samba設定(Windows共有)
設定ファイル編集$ sudo nano /etc/samba/smb.conf
一番下に下記を追加
[HDD]
path = /mnt/hdd
browseable = yes
writeable = yes
create mask = 0777
directory mask = 0777
public = yes
guest ok = yes
⑨Samba再起動
$ sudo systemctl restart smbd$ sudo systemctl enable smbd
⑩Windowsからアクセス
Windowsから\\raspberrypi(ホストネーム、IPアドレス)
を開く。
※Windowsの「ネットワークの資格情報」にてはじかれてしまう場合は、
「Raspberry Piに存在するLinuxユーザー」、「Sambaに登録されたユーザー」、「Windowsで入力するユーザー」が一致している必要がある。
RaspberryPi側で $ whoami →例)pi
Sambaユーザー登録
$ sudo smbpasswd -a pi
表示例)
New SMB password:
Retype new password:
ここで設定したパスワードが、Windowsから接続するときに使うパスワードです。
※ネットワークドライブとして割り当てると後々アクセスが楽になる。
⑪MiniDLNA設定
$ sodo nano /etc/minidlna.conf以下を探して変更
media_dir=/mnt/hdd メディアフォルダ
friendly_name=RPi3 名前
inotify=yes 自動監視ON
⑫MiniDLNA再起動
$ sudo systemctl restart minidlna$ sudo systemctl enable minidlna
⑬データベース再構築(初回)
$ sudo minidlnad -R⑭動作確認(Windows)
エクスプローラーで\\RPi3⑮トラブル時の確認コマンド
$ systemctl status smbd Samba状態表示例)
pi@RPi3:~ $ systemctl status smbd
● smbd.service - Samba SMB Daemon
Loaded: loaded (/usr/lib/systemd/system/smbd.se>
Active: active (running) since Sun 2026-05-10 2>
Invocation: 8ab259a2c8db44a2a2b87a54ba54d3b6
Docs: man:smbd(8)
man:samba(7)
man:smb.conf(5)
Main PID: 11111 (smbd)
Status: "smbd: ready to serve connections..."
Tasks: 4 (limit: 751)
CPU: 7.385s
CGroup: /system.slice/smbd.service
tq11111 /usr/sbin/smbd --foreground --n>
tq11114 "smbd: notifyd" .
tq11115 "smbd: cleanupd "
mq11583 "smbd: client [240b:13:7e1:7600>
$ systemctl status minidlna DLNA状態
表示例)
pi@RPi3:~ $ systemctl status minidlna
● minidlna.service - MiniDLNA lightweight DLNA/UPnP->
Loaded: loaded (/usr/lib/systemd/system/minidln>
Active: active (running) since Sun 2026-05-10 2>
Invocation: bea131b5f08645419921f4a3d9b183c8
Docs: man:minidlnad(1)
man:minidlna.conf(5)
Main PID: 11666 (minidlnad)
Tasks: 2 (limit: 751)
CPU: 4min 53.080s
CGroup: /system.slice/minidlna.service
tq11666 /usr/sbin/minidlnad -f /etc/min>
mq11673 /usr/sbin/minidlnad -f /etc/min>
Python+Flaskで表示画面作成
1.必要なパッケージをインストールする
$ sudo apt update$ sudo apt install -y python3-flask python3-psutil smartmontools
$ sudo apt install -y mpd mpc ※音楽表示をする場合
②作業フォルダ作成
$ mkdir -p ~/nas-monitor/templates
$ mkdir -p ~/nas-monitor/static
フォルダ構成
/home/pi/nas-monitor/
├── app.py
├── templates/
│ └── index.html
└── static/
└── style.css
③Pythonコード作成(Flask本体)
$ nano ~/nas-monitor/app.py
from flask import Flask, render_template, jsonify
import psutil
import shutil
import subprocess
import time
from datetime import datetime
app = Flask(__name__)
# ===== 設定 =====
# HDD名
DISK = "/dev/sdb"
# 使用しているネットワークIF
# Wi-Fiなら wlan0
# 有線なら eth0
NETWORK_IF = "wlan0"
# マウント先
MOUNT_POINT = "/mnt/hdd"
# ==================
# ネットワーク速度計算用
last_bytes = 0
last_time = time.time()
def get_cpu_temp():
try:
temp = subprocess.check_output(
["vcgencmd", "measure_temp"]
).decode()
return temp.replace("temp=", "").replace("'C\n", "")
except:
return "不明"
def get_disk_temp():
try:
output = subprocess.check_output(
[
"sudo",
"smartctl",
"-A",
"-d",
"sat",
DISK
]
).decode()
for line in output.splitlines():
if "Temperature_Celsius" in line:
return line.split()[-1] + "°C"
if "Airflow_Temperature_Cel" in line:
return line.split()[-1] + "°C"
return "不明"
except Exception as e:
return "取得失敗"
def get_smart_status():
try:
output = subprocess.check_output(
[
"sudo",
"smartctl",
"-H",
"-d",
"sat",
DISK
]
).decode()
if "PASSED" in output:
return "正常"
else:
return "異常"
except:
return "取得失敗"
def get_now_playing():
try:
song = subprocess.check_output(
["mpc", "current"]
).decode().strip()
if song == "":
return "なし"
return song
except:
return "なし"
def get_network_speed():
global last_bytes
global last_time
try:
counters = psutil.net_io_counters(pernic=True)
if NETWORK_IF not in counters:
return 0
net = counters[NETWORK_IF]
current_bytes = net.bytes_recv + net.bytes_sent
current_time = time.time()
speed = (
current_bytes - last_bytes
) / (
current_time - last_time
)
last_bytes = current_bytes
last_time = current_time
# MB/s
return round(speed / 1024 / 1024, 2)
except:
return 0
@app.route("/")
def index():
return render_template("index.html")
@app.route("/status")
def status():
# CPU
cpu = psutil.cpu_percent()
# RAM
ram = psutil.virtual_memory().percent
# ストレージ
total, used, free = shutil.disk_usage(MOUNT_POINT)
storage_percent = round(
(used / total) * 100,
1
)
total_tb = round(
total / (1024**4),
1
)
# 温度
cpu_temp = get_cpu_temp()
disk_temp = get_disk_temp()
# SMART
smart = get_smart_status()
# ネットワーク
network_speed = get_network_speed()
# 時刻
now = datetime.now().strftime("%H:%M:%S")
# 音楽
music = get_now_playing()
return jsonify({
"cpu": cpu,
"ram": ram,
"storage_percent": storage_percent,
"total_tb": total_tb,
"cpu_temp": cpu_temp,
"disk_temp": disk_temp,
"smart": smart,
"network_speed": network_speed,
"time": now,
"music": music
})
if __name__ == "__main__":
app.run(
host="0.0.0.0",
port=5000
)
④HTML作成
$ nano ~/nas-monitor/templates/index.html
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>NAS Monitor</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container">
<div class="top">
<div class="left">
<h2>ストレージ使用率:<span id="storage"></span>%</h2>
<p>( <span id="total"></span>TB)</p>
</div>
<div class="right">
<p>システム温度:<span id="cpu_temp"></span>°C</p>
<p>ディスク温度:<span id="disk_temp"></span></p>
<p>SMART:<span id="smart"></span></p>
</div>
</div>
<div class="middle">
<div>
<p>ネットワーク転送速度:<span id="network"></span> MB/s</p>
</div>
<div>
<p>CPU使用率:<span id="cpu"></span>%</p>
<p>RAM使用率:<span id="ram"></span>%</p>
</div>
</div>
<div class="clock">
<p>現在時間</p>
<div id="time">00:00:00</div>
</div>
<div class="music">
<p>現在再生中の音楽:<span id="music"></span></p>
</div>
</div>
<script>
async function updateStatus() {
const response = await fetch("/status");
const data = await response.json();
document.getElementById("storage").innerText =
data.storage_percent;
document.getElementById("total").innerText =
data.total_gb;
document.getElementById("cpu_temp").innerText =
data.cpu_temp;
document.getElementById("disk_temp").innerText =
data.disk_temp;
document.getElementById("smart").innerText =
data.smart;
document.getElementById("network").innerText =
data.network_speed;
document.getElementById("cpu").innerText =
data.cpu;
document.getElementById("ram").innerText =
data.ram;
document.getElementById("time").innerText =
data.time;
document.getElementById("music").innerText =
data.music;
}
setInterval(updateStatus, 1000);
updateStatus();
</script>
</body>
</html>
⑤CSS作成
$ nano ~/nas-monitor/static/style.css
background: #efefef;
font-family: sans-serif;
margin: 0;
padding: 10px;
width: 800px;
height: 480px;
overflow: hidden;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
}
.top {
display: flex;
justify-content: space-between;
}
.middle {
margin-top: 40px;
display: flex;
justify-content: space-between;
}
.clock {
margin-top: 40px;
text-align: center;
}
#time {
font-size: 80px;
letter-spacing: 10px;
}
.music {
margin-top: 30px;
font-size: 20px;
}
⑥Flask起動
$ cd ~/nas-monitor
$ python3 app.py
表示例)
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.10.131:5000
ラズパイからなら、ブラウザにhttp://127.0.0.1:5000と入力
LAN内のWindows機からはhttp://192.168.10.131:5000と入力すると表示される。
⑦sudoers設定
$ sudo visudo
最後に以下の1文を追記する。
pi ALL=(ALL) NOPASSWD: /usr/sbin/smartctl
⑧自動起動設定
サービスに登録する。
$ sudo nano /etc/systemd/system/nas-monitor.service
[Unit]
Description=NAS Monitor
After=network.target
[Service]
User=pi
WorkingDirectory=/home/pi/nas-monitor
ExecStart=/usr/bin/python3 /home/pi/nas-monitor/app.py
Restart=always
[Install]
WantedBy=multi-user.target
上記自作のサービスを有効にする。
$ sudo systemctl daemon-reload
$ sudo systemctl enable nas-monitor
$ sudo systemctl start nas-monitor
⑨フルスクリーン表示
$ sudo apt install -y chromium
$ mkdir -p ~/.config/lxsession/LXDE
$ nano ~/.config/lxsession/LXDE/autostart
@xset s off
@xset -dpms
@xset s noblank
@unclutter -idle 0
@chromium
--kiosk
--app=http://localhost:5000
--no-first-run
--disable-infobars
--disable-session-crashed-bubble
--disable-features=TranslateUI
--disable-gpu
--incognito
--noerrdialogs
$ sudo nano /etc/lightdm/lightdm.conf
autologin-user-timeout=0
user-session=LXDE