一、项目概述
本项目基于龙芯 2K0300 先锋派开发板(Loongnix-Embedded 系统)+ Lighttpd Web 服务器,搭建动态可视化监控仪表盘,通过 Chart.js 实现 CPU 使用率、CPU 温度、内存使用率、存储空间 4 项核心指标的表盘式展示,同时包含系统运行时间、内核版本等基础信息,适配嵌入式教学演示场景。
1、 核心特性
- 4 个核心监控指标以动态表盘形式单行排列,视觉直观;
- 5 秒自动刷新数据,实时展示网关运行状态;
- CPU 温度超过 50 摄氏度时自动标红告警,适配工业场景需求;
- 仪表盘与基础信息框宽度统一,排版规整。
2、 环境准备
|-------------|-------------------------------------|
| 硬件 / 软件 | 规格 / 版本 |
| 硬件平台 | 龙芯 2K0300 先锋派开发板 |
| 系统版本 | Loongnix-Embedded(Buildroot 内核) |
| Web 服务器 | Lighttpd |
| 架构 | loongarch64 |
| 网络要求 | 开发板可访问外网(加载 Chart.js)或本地部署 Chart.js |
二、安装Lighttpd
1、安装 Lighttpd
sudo apt install lighttpd

2、测试Lighttpd
启动lighttpd 服务器。
/etc/init.d/lighttpd start
在浏览器输入开发板的 IP,可看到lighttpd 默认的静态页面。
http://你的板子IP/status.sh

3、配置 Lighttpd 支持脚本运行
vim /etc/lighttpd/lighttpd.conf
添加或确保启用以下模块
server.modules += ( "mod_cgi" )
cgi.assign = ( ".sh" => "/bin/bash" )
确保目录正确
server.document-root = "/var/www/html"
参考后面章节,完成 status.h 功能脚本,把脚本放到网页目录
cp /var/www/status.sh /var/www/html/
重启 Lighttpd
/etc/init.d/lighttpd stop
/etc/init.d/lighttpd start
三、监控仪表盘
1、 编写动态监控脚本(status.sh)
创建 / 覆盖脚本文件
cd /var/www/html
sudo vim status.sh
粘贴完整脚本内容
bash
#!/bin/bash
echo "Content-type: text/html"
echo ""
# Loongson 2K0300 Industrial Gateway - System Status with Dynamic Gauges
# Basic monitoring items
ip=$(ifconfig eth0 | grep 'inet ' | awk '{print $2}')
kernel=$(uname -r)
os=$(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)
# CPU Temperature (raw value for calculation, formatted for display)
temp_raw=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null || echo 0)
temp_value=$((temp_raw / 1000)) # Numeric value for gauge (e.g., 47 from 47982)
temp_display="${temp_value}.${((temp_raw % 1000)/100)}"
if [ "$temp_raw" = "0" ]; then
temp_value=0
temp_display="Unknown"
fi
# CPU Usage (numeric value for gauge)
cpu_usage_raw=$(top -bn1 | grep 'Cpu(s)' | awk '{print 100-$8}' 2>/dev/null || echo 0)
cpu_usage=$(printf "%.1f" "$cpu_usage_raw") # Format to 1 decimal place
if [ "$cpu_usage" = "0.0" ] && [ "$cpu_usage_raw" = "" ]; then
cpu_usage=0
cpu_usage_display="Unknown"
else
cpu_usage_display="${cpu_usage}%"
fi
# Memory Usage (extract numeric percentage for gauge)
mem_total=$(free -b | grep Mem | awk '{print $2}')
mem_used=$(free -b | grep Mem | awk '{print $3}')
mem_usage=$(( (mem_used * 100) / mem_total )) # Numeric percentage
mem_display="Used: $(free -h | grep Mem | awk '{print $3}') / Total: $(free -h | grep Mem | awk '{print $2}')"
# Storage Usage (extract numeric percentage for gauge)
disk_total=$(df -B1 / | grep / | awk '{print $2}')
disk_used=$(df -B1 / | grep / | awk '{print $3}')
disk_usage=$(( (disk_used * 100) / disk_total )) # Numeric percentage
disk_display="Used: $(df -h / | grep / | awk '{print $3}') / Total: $(df -h / | grep / | awk '{print $2}')"
# Extended monitoring items
uptime=$(uptime -p 2>/dev/null || cat /proc/uptime | awk '{print "Up " int($1/3600) "h " int(($1%3600)/60) "m"}')
rx_bytes=$(cat /sys/class/net/eth0/statistics/rx_bytes 2>/dev/null || echo 0)
tx_bytes=$(cat /sys/class/net/eth0/statistics/tx_bytes 2>/dev/null || echo 0)
rx_mb=$((rx_bytes / 1024 / 1024))
tx_mb=$((tx_bytes / 1024 / 1024))
network="Receive: ${rx_mb} MB | Transmit: ${tx_mb} MB"
# Output Web Page (degC + Unified Width)
cat <<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="5">
<title>Loongson 2K0300 Gateway - Dynamic Dashboard</title>
<!-- Load Chart.js from CDN (no local installation needed) -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
/* Global Style */
body {
background: #0c1015;
color: #00ff99;
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
h1 {
color: #ffaa00;
text-align: center;
margin-bottom: 30px;
}
/* Gauge Container: 4 gauges in 1 row (unified width with status box) */
.gauge-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 15px;
margin: 0 auto 30px auto;
width: 90%;
max-width: 950px; /* Match status box width */
padding: 10px;
}
.gauge-card {
background: #151a23;
border: 2px solid #00ff99;
border-radius: 10px;
padding: 10px;
text-align: center;
width: 200px; /* Fixed width for 4-in-1-row */
}
.gauge-canvas {
width: 180px !important; /* Smaller gauge size */
height: 180px !important;
margin: 0 auto;
}
.gauge-card h2 {
font-size: 18px;
margin: 5px 0;
color: #ffaa00;
}
.gauge-card p {
font-size: 14px;
margin: 5px 0;
}
/* Status Details: Same width as gauge container */
.status-box {
border: 2px solid #00ff99;
background: #151a23;
margin: 0 auto;
padding: 30px;
width: 90%; /* Match gauge container width */
max-width: 950px; /* Exact match with gauge container */
border-radius: 10px;
text-align: left;
box-sizing: border-box; /* Include padding/border in width calculation */
}
.status-item {
font-size: 20px;
margin: 10px 0;
}
.label {
color: #ffffff;
display: inline-block;
width: 180px;
text-align: right;
margin-right: 15px;
}
.temp-alert {
color: #ff0000;
font-weight: bold;
}
.demo-note {
text-align: center;
margin-top: 25px;
color: #cccccc;
font-size: 18px;
}
/* Responsive fallback (1 row on small screens) */
@media (max-width: 900px) {
.gauge-container, .status-box {
width: 95%;
max-width: 300px;
}
.gauge-container {
flex-direction: column;
align-items: center;
}
.gauge-card {
width: 250px;
}
}
</style>
</head>
<body>
<h1>Loongson 2K0300 Industrial IoT Gateway - Dynamic Dashboard</h1>
<!-- Dynamic Gauge Section (4 in 1 Row) -->
<div class="gauge-container">
<!-- CPU Usage Gauge -->
<div class="gauge-card">
<h2>CPU Usage</h2>
<canvas id="cpuUsageGauge" class="gauge-canvas"></canvas>
<p id="cpuUsageText">$cpu_usage_display</p>
</div>
<!-- CPU Temperature Gauge (degC instead of °C) -->
<div class="gauge-card">
<h2>CPU Temperature</h2>
<canvas id="cpuTempGauge" class="gauge-canvas"></canvas>
<p id="cpuTempText" class="$([ $temp_value -gt 50 ] && echo 'temp-alert')">$temp_display degC</p>
</div>
<!-- Memory Usage Gauge -->
<div class="gauge-card">
<h2>Memory Usage</h2>
<canvas id="memUsageGauge" class="gauge-canvas"></canvas>
<p id="memUsageText">$mem_display</p>
</div>
<!-- Storage Usage Gauge -->
<div class="gauge-card">
<h2>Storage Usage</h2>
<canvas id="diskUsageGauge" class="gauge-canvas"></canvas>
<p id="diskUsageText">$disk_display</p>
</div>
</div>
<!-- Additional Status Details (Reordered + Unified Width) -->
<div class="status-box">
<div class="status-item"><span class="label">System Uptime:</span>$uptime</div>
<div class="status-item"><span class="label">Kernel Version:</span>$kernel</div>
<div class="status-item"><span class="label">System Version:</span>$os</div>
<div class="status-item"><span class="label">IP Address:</span>$ip</div>
<div class="status-item"><span class="label">Network Traffic:</span>$network</div>
</div>
<div class="demo-note">Education Demo Auto-refresh every 5s | Temperature Alert: >50 degC (Red)</div>
<!-- Gauge Initialization Script (degC for temp gauge) -->
<script>
// Gauge Configuration (Reusable Function)
function createGauge(canvasId, value, max, title, color) {
const ctx = document.getElementById(canvasId).getContext('2d');
return new Chart(ctx, {
type: 'doughnut',
data: {
datasets: [{
data: [value, max - value],
backgroundColor: [color, '#2a3440'],
borderWidth: 0
}],
labels: [title, 'Free']
},
options: {
responsive: true,
cutout: '70%',
plugins: {
legend: { display: false },
tooltip: { enabled: false },
title: {
display: true,
text: value + (canvasId.includes('Temp') ? ' degC' : '%'), // Replace °C with degC
color: '#00ff99',
font: { size: 18, weight: 'bold' }
}
},
animation: {
animateRotate: true,
animateScale: true,
duration: 1000
}
}
});
}
// Initialize All Gauges
// CPU Usage (Max: 100%)
createGauge('cpuUsageGauge', $cpu_usage, 100, 'CPU Usage', '#00ff99');
// CPU Temperature (Max: 80 degC - Industrial safe limit)
createGauge('cpuTempGauge', $temp_value, 80, 'CPU Temp', $temp_value > 50 ? '#ff0000' : '#00ff99');
// Memory Usage (Max: 100%)
createGauge('memUsageGauge', $mem_usage, 100, 'Memory', '#00ff99');
// Storage Usage (Max: 100%)
createGauge('diskUsageGauge', $disk_usage, 100, 'Storage', '#00ff99');
</script>
</body>
</html>
赋予脚本执行权限
sudo chmod +x /var/www/html/status.sh
2、 离线环境适配(可选)
若开发板无法访问外网,需本地部署 Chart.js:
# 下载Chart.js到Web根目录
wget https://cdn.jsdelivr.net/npm/chart.js -O /var/www/html/chart.js
# 修改脚本中Chart.js加载路径(替换CDN链接)
# 将 <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
# 改为 <script src="/chart.js"></script>

3、配置 Lighttpd 支持脚本运行
vim /etc/lighttpd/lighttpd.conf
添加或确保启用以下模块
server.modules += ( "mod_cgi" )
cgi.assign = ( ".sh" => "/bin/bash" )
增加对cgi可执行的支持:
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi",".sh" )
确保目录正确
server.document-root = "/var/www/html"
参考后面章节,完成 status.h 功能脚本,把脚本放到网页目录
cp /var/www/status.sh /var/www/html/
重启 Lighttpd
/etc/init.d/lighttpd stop
/etc/init.d/lighttpd start
4、访问监控仪表盘
-
查看开发板 IP 地址:bash运行
ifconfig eth0 | grep 'inet '
-
在 PC 浏览器中输入:
http://[开发板IP]/status.sh

四、核心功能
1、动态表盘实现原理
- 基于 Chart.js 的 doughnut(环形图)实现表盘效果,通过
cutout: '70%'设置内径比例; - 后端 Shell 脚本采集数值型数据(如 CPU 使用率百分比、温度整数值),传递给前端 JS;
- 前端通过
createGauge函数初始化表盘,动画效果提升教学演示的视觉体验。
2、乱码问题解决
- 将易乱码的
°C替换为 ASCII 兼容的degC(摄氏度国际通用缩写); - 所有字符输出均基于 UTF-8 编码(
<meta charset="utf-8">),适配嵌入式系统字符集。
3、 宽度统一实现
- 表盘容器(
.gauge-container)和基础信息框(.status-box)设置相同的width: 90%和max-width: 950px; - 信息框添加
box-sizing: border-box,确保内边距和边框不影响宽度计算,保证两者完全对齐。
4、温度告警逻辑
- 后端:通过
if [ "$temp_value" -gt 50 ]判断温度是否超过阈值,设置告警样式类temp-alert; - 前端:
temp-alert类定义红色加粗样式,同时表盘颜色自动切换为红色($temp_value > 50 ? '#ff0000' : '#00ff99')。
5、自动刷新机制
- 通过 HTML 原生标签
<meta http-equiv="refresh" content="5">实现 5 秒页面重载,无需后端定时任务; - 刷新后 Shell 脚本重新采集数据,表盘自动渲染新值并播放动画。
五、常见问题
|--------------|----------------|--------------------------------------------------|
| 问题现象 | 原因分析 | 解决方案 |
| 表盘不显示 | 未加载 Chart.js | 检查开发板网络(CDN 方式)或本地 Chart.js 文件路径(离线方式) |
| 温度显示 Unknown | 温度文件读取失败 | 检查/sys/class/thermal/thermal_zone0/temp 文件是否存在 |
| 宽度不一致 | 未设置 box-sizing | 确认.status-box 包含box-sizing: border-box 样式 |
| 脚本执行报权限错误 | 脚本无执行权限 | 执行chmod 777 /var/www/html/status.sh |
| 小屏下表盘排版错乱 | 响应式样式未生效 | 确认 CSS 中@media (max-width: 900px) 区块完整 |
六、拓展方向
- 多指标告警:给 CPU 使用率(>80%)、内存使用率(>90%)添加告警样式,逻辑同温度告警;
- 数据精度优化:网络流量按实际大小自动切换 KB/MB/GB 单位;
- 历史数据展示:结合 localStorage 存储短周期历史数据,添加折线图展示趋势;
- 自定义阈值:通过前端输入框修改温度告警阈值,实时生效;
- 工业风格定制:替换表盘颜色、背景为企业级工业监控样式。