龙芯 2K0300-- 实现工业网关监控仪表盘项目

一、项目概述

本项目基于龙芯 2K0300 先锋派开发板(Loongnix-Embedded 系统)+ Lighttpd Web 服务器,搭建动态可视化监控仪表盘,通过 Chart.js 实现 CPU 使用率、CPU 温度、内存使用率、存储空间 4 项核心指标的表盘式展示,同时包含系统运行时间、内核版本等基础信息,适配嵌入式教学演示场景。

1、 核心特性

  1. 4 个核心监控指标以动态表盘形式单行排列,视觉直观;
  2. 5 秒自动刷新数据,实时展示网关运行状态;
  3. CPU 温度超过 50 摄氏度时自动标红告警,适配工业场景需求;
  4. 仪表盘与基础信息框宽度统一,排版规整。

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、访问监控仪表盘

  1. 查看开发板 IP 地址:bash运行

    ifconfig eth0 | grep 'inet '

  2. 在 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) 区块完整 |

六、拓展方向

  1. 多指标告警:给 CPU 使用率(>80%)、内存使用率(>90%)添加告警样式,逻辑同温度告警;
  2. 数据精度优化:网络流量按实际大小自动切换 KB/MB/GB 单位;
  3. 历史数据展示:结合 localStorage 存储短周期历史数据,添加折线图展示趋势;
  4. 自定义阈值:通过前端输入框修改温度告警阈值,实时生效;
  5. 工业风格定制:替换表盘颜色、背景为企业级工业监控样式。
相关推荐
项目題供诗2 小时前
STM32-OLED显示屏(六)
stm32·单片机·嵌入式硬件
jllllyuz2 小时前
STM32F10x MQ-2烟雾传感器驱动程序
stm32·单片机·嵌入式硬件
bubiyoushang8884 小时前
STM32F030 多路ADC采样实现
stm32·单片机·嵌入式硬件
三佛科技-187366133975 小时前
LP8841SC+LP35118N (72W SiC双电源方案),全电压认证,体积直降 20%
单片机·嵌入式硬件
metaRTC5 小时前
metaRTC8 成功适配 RTOS:开启 MCU/嵌入式实时音视频新时代
单片机·嵌入式硬件·webrtc·实时音视频·rtos
MetrixAeroCore6 小时前
无人机跨境作业通信方案:国际物联卡保障低空设备稳定飞行
物联网
d111111111d6 小时前
UAER问题+修复小bug
前端·javascript·笔记·stm32·单片机·嵌入式硬件·学习
逍遥德6 小时前
MQTT教程详解-03. 高级知识点
java·物联网·中间件·信息与通信·iot·iotdb
嵌入式的飞鱼7 小时前
SD NAND vs eMMC:嵌入式存储方案怎么选?
嵌入式硬件·mcu·sd nand