鸿蒙能耗监测仪表盘开发指南 原创

进修的泡芙
发布于 2025-6-20 14:35
浏览
0收藏

鸿蒙能耗监测仪表盘开发指南

一、系统架构设计

基于HarmonyOS的能耗监测仪表盘,利用Hi3516开发板采集用电数据,通过分布式能力实现多设备协同:
数据采集:Hi3516实时采集电压、电流等参数

智能分析:计算功率、能耗等关键指标

跨设备同步:多设备共享监测数据

可视化展示:多维度展示能耗数据

!https://example.com/harmony-energy-monitor-arch.png

二、核心代码实现
数据采集服务

// DataCollectorService.ets
import driver from ‘@ohos.driver’;
import distributedData from ‘@ohos.distributedData’;

class DataCollectorService {
private static instance: DataCollectorService = null;
private hi3516Driver: driver.Driver;
private dataManager: distributedData.DataManager;
private collectionListeners: CollectionListener[] = [];

private constructor() {
this.initDriver();
this.initDataManager();
public static getInstance(): DataCollectorService {

if (!DataCollectorService.instance) {
  DataCollectorService.instance = new DataCollectorService();

return DataCollectorService.instance;

private initDriver(): void {

const context = getContext() as common.Context;
this.hi3516Driver = driver.createDriver('hi3516', context);

// 配置采样参数
this.hi3516Driver.setConfig({
  samplingRate: 1000, // 1秒采样一次
  channels: ['voltage', 'current', 'power']
});

private initDataManager(): void {

this.dataManager = distributedData.createDataManager({
  bundleName: 'com.example.energymonitor',
  area: distributedData.Area.GLOBAL,
  isEncrypted: true
});

this.dataManager.registerDataListener('energy_sync', (data) => {
  this.handleSyncData(data);
});

public async startCollection(): Promise<void> {

try {
  await this.hi3516Driver.start();
  
  // 定时采集数据
  setInterval(async () => {
    const data = await this.collectData();
    this.notifyListeners(data);
    this.syncData(data);
  }, 1000);

catch (err) {

  console.error('启动数据采集失败:', JSON.stringify(err));

}

private async collectData(): Promise<EnergyData> {
const result: EnergyData = {
timestamp: Date.now(),
voltage: 0,
current: 0,
power: 0,
energy: 0,
deviceId: this.dataManager.getDeviceId()
};

try {
  // 从Hi3516读取传感器数据
  const voltage = await this.hi3516Driver.read('voltage');
  const current = await this.hi3516Driver.read('current');
  
  result.voltage = parseFloat(voltage.toFixed(2));
  result.current = parseFloat(current.toFixed(3));
  result.power = result.voltage * result.current;
  result.energy = result.power * (1 / 3600); // 1秒的能耗(kWh)
  
  return result;

catch (err) {

  console.error('采集数据失败:', JSON.stringify(err));
  return result;

}

private notifyListeners(data: EnergyData): void {
this.collectionListeners.forEach(listener => {
listener.onDataUpdate(data);
});
private syncData(data: EnergyData): void {

this.dataManager.syncData('energy_sync', {
  type: 'energy_data',
  data: data,
  timestamp: Date.now()
});

private handleSyncData(data: any): void {

if (!data || data.type !== 'energy_data') return;

this.collectionListeners.forEach(listener => {
  listener.onDataUpdate(data.data);
});

public addListener(listener: CollectionListener): void {

if (!this.collectionListeners.includes(listener)) {
  this.collectionListeners.push(listener);

}

public removeListener(listener: CollectionListener): void {
this.collectionListeners = this.collectionListeners.filter(l => l !== listener);
}

interface CollectionListener {
onDataUpdate(data: EnergyData): void;
interface EnergyData {

timestamp: number;
voltage: number; // 电压(V)
current: number; // 电流(A)
power: number; // 功率(W)
energy: number; // 能耗(kWh)
deviceId: string;
export const dataService = DataCollectorService.getInstance();

数据分析服务

// AnalysisService.ets
import { dataService } from ‘./DataCollectorService’;

class AnalysisService {
private static instance: AnalysisService = null;
private analysisListeners: AnalysisListener[] = [];
private historyData: EnergyData[] = [];

private constructor() {
this.setupDataListener();
public static getInstance(): AnalysisService {

if (!AnalysisService.instance) {
  AnalysisService.instance = new AnalysisService();

return AnalysisService.instance;

private setupDataListener(): void {

dataService.addListener({
  onDataUpdate: (data) => {
    this.processData(data);

});

private processData(data: EnergyData): void {

// 保存历史数据
this.historyData.push(data);
if (this.historyData.length > 3600) { // 保留1小时数据
  this.historyData.shift();

// 计算统计数据

const stats = this.calculateStats();

// 通知监听器
this.notifyListeners(data, stats);

private calculateStats(): EnergyStats {

if (this.historyData.length === 0) {
  return {
    avgPower: 0,
    maxPower: 0,
    minPower: 0,
    totalEnergy: 0
  };

let totalPower = 0;

let maxPower = 0;
let minPower = Infinity;
let totalEnergy = 0;

this.historyData.forEach(data => {
  totalPower += data.power;
  maxPower = Math.max(maxPower, data.power);
  minPower = Math.min(minPower, data.power);
  totalEnergy += data.energy;
});

return {
  avgPower: totalPower / this.historyData.length,
  maxPower: maxPower,
  minPower: minPower,
  totalEnergy: totalEnergy
};

private notifyListeners(data: EnergyData, stats: EnergyStats): void {

this.analysisListeners.forEach(listener => {
  listener.onAnalysisUpdate(data, stats);
});

public addListener(listener: AnalysisListener): void {

if (!this.analysisListeners.includes(listener)) {
  this.analysisListeners.push(listener);

}

public removeListener(listener: AnalysisListener): void {
this.analysisListeners = this.analysisListeners.filter(l => l !== listener);
public getHistoryData(): EnergyData[] {

return [...this.historyData];

}

interface AnalysisListener {
onAnalysisUpdate(data: EnergyData, stats: EnergyStats): void;
interface EnergyStats {

avgPower: number;
maxPower: number;
minPower: number;
totalEnergy: number;
export const analysisService = AnalysisService.getInstance();

仪表盘主界面

// DashboardScreen.ets
import { dataService } from ‘./DataCollectorService’;
import { analysisService } from ‘./AnalysisService’;

@Component
export struct DashboardScreen {
@State currentData: EnergyData | null = null;
@State stats: EnergyStats = {
avgPower: 0,
maxPower: 0,
minPower: 0,
totalEnergy: 0
};
@State connectedDevices: string[] = [];
@State isCollecting: boolean = false;

build() {
Column() {
// 标题栏
Row() {
Text(‘能耗监测仪表盘’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.layoutWeight(1)

    Button(this.isCollecting ? '停止监测' : '开始监测')
      .width(120)
      .onClick(() => {
        this.toggleCollection();
      })

.padding(10)

  .width('100%')
  
  // 实时数据显示
  if (this.currentData) {
    Grid() {
      GridItem() {
        EnergyCard({
          title: '电压',
          value: this.currentData.voltage,
          unit: 'V',
          icon: $r('app.media.ic_voltage')
        })

GridItem() {

        EnergyCard({
          title: '电流',
          value: this.currentData.current,
          unit: 'A',
          icon: $r('app.media.ic_current')
        })

GridItem() {

        EnergyCard({
          title: '功率',
          value: this.currentData.power,
          unit: 'W',
          icon: $r('app.media.ic_power')
        })

GridItem() {

        EnergyCard({
          title: '能耗',
          value: this.currentData.energy * 1000, // 转换为Wh
          unit: 'Wh',
          icon: $r('app.media.ic_energy')
        })

}

    .columnsTemplate('1fr 1fr')
    .rowsTemplate('1fr 1fr')
    .columnsGap(10)
    .rowsGap(10)
    .height(300)
    .margin({ bottom: 20 })

// 统计数据显示

  Column() {
    Text('统计信息')
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 10 })
    
    Row() {
      Column() {
        Text('平均功率')
          .fontSize(14)
          .fontColor('#666666')
        Text(${this.stats.avgPower.toFixed(2)} W)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)

.layoutWeight(1)

      Column() {
        Text('最大功率')
          .fontSize(14)
          .fontColor('#666666')
        Text(${this.stats.maxPower.toFixed(2)} W)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)

.layoutWeight(1)

      Column() {
        Text('最小功率')
          .fontSize(14)
          .fontColor('#666666')
        Text(${this.stats.minPower.toFixed(2)} W)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)

.layoutWeight(1)

      Column() {
        Text('总能耗')
          .fontSize(14)
          .fontColor('#666666')
        Text(${this.stats.totalEnergy.toFixed(3)} kWh)
          .fontSize(16)
          .fontWeight(FontWeight.Bold)

.layoutWeight(1)

.padding(10)

    .width('100%')

.padding(15)

  .width('90%')
  .backgroundColor('#F5F5F5')
  .borderRadius(8)
  .margin({ bottom: 20 })
  
  // 设备连接状态
  if (this.connectedDevices.length > 0) {
    Column() {
      Text('已连接设备')
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 8 })
      
      ForEach(this.connectedDevices, (device) => {
        Text(device)
          .fontSize(14)
          .margin({ bottom: 4 })
      })

.padding(10)

    .width('90%')
    .backgroundColor(Color.White)
    .borderRadius(8)
    .margin({ bottom: 20 })

// 历史数据图表

  EnergyChart({
    data: analysisService.getHistoryData(),
    width: '100%',
    height: 200
  })
  .margin({ bottom: 20 })
  
  // 控制按钮
  Button('协同监测')
    .width('80%')
    .height(50)
    .onClick(() => {
      this.showDeviceSelector();
    })

.width(‘100%’)

.height('100%')
.padding(20)
.onAppear(() => {
  dataService.addListener({
    onDataUpdate: (data) => {
      this.currentData = data;

});

  analysisService.addListener({
    onAnalysisUpdate: (data, stats) => {
      this.stats = stats;

});

})
.onDisappear(() => {
  dataService.removeListener({
    onDataUpdate: () => {}
  });
  
  analysisService.removeListener({
    onAnalysisUpdate: () => {}
  });
})

private toggleCollection(): void {

if (this.isCollecting) {
  dataService.stopCollection();

else {

  dataService.startCollection();

this.isCollecting = !this.isCollecting;

private showDeviceSelector(): void {

const dialog = new AlertDialog({
  title: '选择协同设备',
  items: ['设备1', '设备2', '设备3'], // 实际应从设备管理服务获取
  onSelect: (index) => {
    this.connectToDevice(index);

});

dialog.show();

private connectToDevice(deviceId: string): void {

if (!this.connectedDevices.includes(deviceId)) {
  this.connectedDevices.push(deviceId);
  prompt.showToast({ message: 已连接设备 ${deviceId} });

}

@Component

struct EnergyCard {
private title: string;
private value: number;
private unit: string;
private icon: Resource;

build() {
Column() {
Row() {
Image(this.icon)
.width(24)
.height(24)
.margin({ right: 8 })

    Text(this.title)
      .fontSize(16)

.margin({ bottom: 8 })

  Row(Align.Center) {
    Text(this.value.toFixed(this.unit === 'A' ? 3 : 1))
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
      .margin({ right: 4 })
    
    Text(this.unit)
      .fontSize(16)
      .fontColor('#666666')

}

.padding(15)
.width('100%')
.backgroundColor(Color.White)
.borderRadius(8)

}

@Component
struct EnergyChart {
private data: EnergyData[];
private width: string | number;
private height: string | number;

build() {
Canvas()
.width(this.width)
.height(this.height)
.onReady((context: CanvasRenderingContext2D) => {
this.drawChart(context);
})
private drawChart(ctx: CanvasRenderingContext2D): void {

if (this.data.length === 0) return;

const width = ctx.width;
const height = ctx.height;
const padding = 20;
const chartWidth = width - padding * 2;
const chartHeight = height - padding * 2;

// 清空画布
ctx.clearRect(0, 0, width, height);

// 绘制坐标轴
ctx.beginPath();
ctx.strokeStyle = '#CCCCCC';
ctx.lineWidth = 1;

// X轴
ctx.moveTo(padding, height - padding);
ctx.lineTo(width - padding, height - padding);

// Y轴
ctx.moveTo(padding, height - padding);
ctx.lineTo(padding, padding);

// Y轴刻度
const maxPower = Math.max(...this.data.map(d => d.power), 1000);
const yStep = chartHeight / 5;

for (let i = 0; i <= 5; i++) {
  const y = height - padding - i * yStep;
  ctx.moveTo(padding, y);
  ctx.lineTo(padding - 5, y);
  
  ctx.textAlign = 'right';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = '#666666';
  ctx.font = '12px sans-serif';
  ctx.fillText((maxPower * i / 5).toFixed(0), padding - 10, y);

ctx.stroke();

// 绘制功率曲线
ctx.beginPath();
ctx.strokeStyle = '#4285F4';
ctx.lineWidth = 2;

const xStep = chartWidth / (this.data.length - 1);
const startX = padding;
const startY = height - padding - (this.data[0].power / maxPower) * chartHeight;

ctx.moveTo(startX, startY);

for (let i = 1; i < this.data.length; i++) {
  const x = padding + i * xStep;
  const y = height - padding - (this.data[i].power / maxPower) * chartHeight;
  ctx.lineTo(x, y);

ctx.stroke();

// 绘制数据点
ctx.fillStyle = '#4285F4';

for (let i = 0; i < this.data.length; i += Math.floor(this.data.length / 10)) {
  const x = padding + i * xStep;
  const y = height - padding - (this.data[i].power / maxPower) * chartHeight;
  
  ctx.beginPath();
  ctx.arc(x, y, 3, 0, Math.PI * 2);
  ctx.fill();

}

三、项目配置与权限
权限配置

// module.json5
“module”: {

"requestPermissions": [

“name”: “ohos.permission.ACCESS_DRIVER”,

    "reason": "访问Hi3516驱动"
  },

“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,

    "reason": "同步能耗数据"
  },

“name”: “ohos.permission.INTERNET”,

    "reason": "加载AI模型"
  },

“name”: “ohos.permission.ACCESS_DISTRIBUTED_DEVICE_MANAGER”,

    "reason": "发现和连接其他设备"
  },

“name”: “ohos.permission.MEDIA_LOCATION”,

    "reason": "访问媒体位置信息"

],

"abilities": [

“name”: “MainAbility”,

    "type": "page",
    "visible": true
  },

“name”: “DeviceAbility”,

    "type": "page",
    "visible": true
  },

“name”: “DataCollectionAbility”,

    "type": "service",
    "backgroundModes": ["dataTransfer"]

]

}

四、总结与扩展

本能耗监测仪表盘实现了以下核心功能:
实时采集:通过Hi3516精确采集用电数据

智能分析:计算功率、能耗等关键指标

多设备协同:多设备实时共享监测数据

可视化展示:多维度展示能耗数据

扩展方向:
能耗预警:设置阈值触发警报

节能建议:基于数据分析提供节能建议

报表导出:生成日/周/月能耗报表

设备管理:对接智能电表管理系统

云同步:安全地上传数据到云端

通过HarmonyOS的分布式能力和Hi3516的硬件能力,我们构建了一个高效、可靠的能源监测系统,帮助用户更好地管理和优化能源使用。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐