
鸿蒙智慧农业大棚监测系统开发指南 原创
鸿蒙智慧农业大棚监测系统开发指南
一、系统架构设计
基于HarmonyOS的分布式能力和物联网技术,我们设计了一套农业大棚监测系统,主要功能包括:
土壤墒情预测:基于历史数据和环境因素预测土壤湿度变化
多节点组网:优化大棚内传感器节点的网络拓扑
天气预警:集成气象数据提供异常天气预警
跨设备协同:多终端实时同步监测数据
智能灌溉:根据预测模型自动调节灌溉系统
!https://example.com/harmony-greenhouse-arch.png
二、核心代码实现
土壤墒情预测服务
// SoilMoistureService.ets
import neuralNetwork from ‘@ohos.ai.neuralNetwork’;
import sensor from ‘@ohos.sensor’;
class SoilMoistureService {
private static instance: SoilMoistureService;
private model: neuralNetwork.Model | null = null;
private history: MoistureData[] = [];
private predictionInterval: number = 3600000; // 1小时预测一次
private constructor() {
this.initModel();
this.startPredictionLoop();
private async initModel(): Promise<void> {
try {
// 加载预训练的LSTM土壤墒情预测模型
this.model = await neuralNetwork.loadModel({
modelPath: 'models/soil_moisture_lstm.nn',
quantization: true
});
// 加载历史数据
this.loadHistory();
catch (error) {
console.error('Failed to load prediction model:', error);
}
private async loadHistory(): Promise<void> {
const history = await storage.get(‘moistureHistory’);
if (history) {
this.history = JSON.parse(history);
}
public static getInstance(): SoilMoistureService {
if (!SoilMoistureService.instance) {
SoilMoistureService.instance = new SoilMoistureService();
return SoilMoistureService.instance;
private startPredictionLoop(): void {
setInterval(() => {
this.predictMoisture();
}, this.predictionInterval);
public async recordCurrentMoisture(data: MoistureData): Promise<void> {
this.history.push(data);
// 保留最近7天数据
if (this.history.length > 7 * 24) {
this.history.shift();
// 保存数据
await storage.set('moistureHistory', JSON.stringify(this.history));
private async predictMoisture(): Promise<void> {
if (!this.model || this.history.length < 24) return;
try {
// 准备输入数据
const input = this.prepareInput();
// 运行预测
const output = await this.model.run(input);
// 解析预测结果
const predictions = this.parseOutput(output);
// 触发预测事件
EventBus.emit('moisturePredicted', predictions);
catch (error) {
console.error('Moisture prediction failed:', error);
}
private prepareInput(): neuralNetwork.Tensor {
// 使用最近24小时数据预测未来6小时
const recentData = this.history.slice(-24);
return {
data: this.normalizeData(recentData),
shape: [1, 24, 5] // [批次, 时间步, 特征]
};
private normalizeData(data: MoistureData[]): number[] {
// 归一化处理
const maxMoisture = Math.max(...data.map(d => d.moisture));
const maxTemp = Math.max(...data.map(d => d.temperature));
return data.flatMap(d => [
d.moisture / maxMoisture,
d.temperature / maxTemp,
d.humidity / 100,
d.lightIntensity / 10000,
d.watered ? 1 : 0
]);
private parseOutput(output: neuralNetwork.Tensor): MoisturePrediction[] {
const data = output.data as number[];
const now = Date.now();
return [
hours: 1, moisture: data[0] * 100 }, // 1小时后
hours: 3, moisture: data[1] * 100 }, // 3小时后
hours: 6, moisture: data[2] * 100 } // 6小时后
];
public getHistory(): MoistureData[] {
return [...this.history];
public getLatestPrediction(): MoisturePrediction | null {
const predictions = EventBus.getLastEvent('moisturePredicted');
return predictions ? predictions[0] : null;
}
export const moistureService = SoilMoistureService.getInstance();
多节点组网服务
// MeshNetworkService.ets
import net from ‘@ohos.net’;
import deviceManager from ‘@ohos.distributedHardware.deviceManager’;
class MeshNetworkService {
private static instance: MeshNetworkService;
private nodes: SensorNode[] = [];
private networkTopology: ‘star’ ‘mesh’
‘hybrid’ = ‘hybrid’;
private updateInterval: number = 300000; // 5分钟优化一次
private constructor() {
this.initNetwork();
this.startOptimizationLoop();
private initNetwork(): void {
this.discoverNodes();
this.establishConnections();
private startOptimizationLoop(): void {
setInterval(() => {
this.optimizeNetwork();
}, this.updateInterval);
public static getInstance(): MeshNetworkService {
if (!MeshNetworkService.instance) {
MeshNetworkService.instance = new MeshNetworkService();
return MeshNetworkService.instance;
private async discoverNodes(): Promise<void> {
const devices = await deviceManager.getTrustedDeviceList();
this.nodes = devices.map(device => ({
id: device.deviceId,
type: 'sensor',
rssi: this.getRssi(device.deviceId),
battery: 100, // 初始值
lastSeen: Date.now()
}));
private getRssi(deviceId: string): number {
// 模拟获取信号强度
return Math.floor(Math.random() * 100) - 50; // -50到50dBm
private establishConnections(): void {
// 根据初始拓扑建立连接
switch (this.networkTopology) {
case 'star':
this.setupStarNetwork();
break;
case 'mesh':
this.setupMeshNetwork();
break;
default:
this.setupHybridNetwork();
}
private setupStarNetwork(): void {
// 找到中心节点(信号最强)
const centerNode = this.nodes.reduce((prev, current) =>
(prev.rssi > current.rssi) ? prev : current
);
// 其他节点连接到中心节点
this.nodes.forEach(node => {
if (node.id !== centerNode.id) {
net.connect({
target: centerNode.id,
type: 'BLE'
});
});
private setupMeshNetwork(): void {
// 每个节点连接到最近的3个节点
this.nodes.forEach(node => {
const neighbors = this.getClosestNodes(node.id, 3);
neighbors.forEach(neighbor => {
net.connect({
target: neighbor.id,
type: 'BLE'
});
});
});
private setupHybridNetwork(): void {
// 混合拓扑: 区域星型+全局网状
const zones = this.groupNodesByZone();
zones.forEach(zone => {
const center = zone.reduce((prev, current) =>
(prev.rssi > current.rssi) ? prev : current
);
zone.forEach(node => {
if (node.id !== center.id) {
net.connect({
target: center.id,
type: 'BLE'
});
});
});
// 连接各区域中心
const centers = zones.map(zone =>
zone.reduce((prev, current) =>
(prev.rssi > current.rssi) ? prev : current
)
);
for (let i = 0; i < centers.length - 1; i++) {
net.connect({
target: centers[i + 1].id,
type: 'BLE'
});
}
private groupNodesByZone(): SensorNode[][] {
// 简单按位置分组(实际项目应使用GPS或信号强度)
const zones: SensorNode[][] = [];
let currentZone: SensorNode[] = [];
this.nodes.forEach((node, index) => {
currentZone.push(node);
if (currentZone.length >= 5 || index === this.nodes.length - 1) {
zones.push([...currentZone]);
currentZone = [];
});
return zones;
private getClosestNodes(nodeId: string, count: number): SensorNode[] {
const node = this.nodes.find(n => n.id === nodeId);
if (!node) return [];
return [...this.nodes]
.filter(n => n.id !== nodeId)
.sort((a, b) => Math.abs(b.rssi) - Math.abs(a.rssi))
.slice(0, count);
private optimizeNetwork(): void {
// 检查节点状态
this.updateNodeStatus();
// 重新评估拓扑
const weakNodes = this.nodes.filter(n => n.rssi < -70);
if (weakNodes.length > this.nodes.length / 3) {
// 超过1/3节点信号弱,切换到网状拓扑
this.networkTopology = 'mesh';
else if (weakNodes.length > 0) {
// 少量弱节点,使用混合拓扑
this.networkTopology = 'hybrid';
else {
// 所有节点信号良好,使用星型拓扑
this.networkTopology = 'star';
// 重新建立连接
this.establishConnections();
private updateNodeStatus(): void {
this.nodes.forEach(node => {
node.rssi = this.getRssi(node.id);
node.lastSeen = Date.now();
// 模拟电量消耗
node.battery = Math.max(0, node.battery - 0.5);
});
// 移除离线节点
this.nodes = this.nodes.filter(n =>
Date.now() - n.lastSeen < 600000 // 10分钟
);
public getNetworkTopology(): string {
return this.networkTopology;
public getNodeCount(): number {
return this.nodes.length;
public getNodeStatus(nodeId: string): SensorNode | null {
return this.nodes.find(n => n.id === nodeId) || null;
public getAllNodes(): SensorNode[] {
return [...this.nodes];
}
export const meshNetwork = MeshNetworkService.getInstance();
天气预警服务
// WeatherAlertService.ets
import http from ‘@ohos.net.http’;
import geolocation from ‘@ohos.geolocation’;
class WeatherAlertService {
private static instance: WeatherAlertService;
private apiKey: string = ‘YOUR_API_KEY’;
private location: Location | null = null;
private weatherData: WeatherData | null = null;
private lastUpdate: number = 0;
private updateInterval: number = 3600000; // 1小时更新一次
private constructor() {
this.initLocation();
this.startUpdateLoop();
private initLocation(): void {
geolocation.getCurrentLocation((err, location) => {
if (!err && location) {
this.location = {
latitude: location.latitude,
longitude: location.longitude
};
this.fetchWeatherData();
});
private startUpdateLoop(): void {
setInterval(() => {
this.fetchWeatherData();
}, this.updateInterval);
public static getInstance(): WeatherAlertService {
if (!WeatherAlertService.instance) {
WeatherAlertService.instance = new WeatherAlertService();
return WeatherAlertService.instance;
private async fetchWeatherData(): Promise<void> {
if (!this.location) return;
try {
const httpRequest = http.createHttp();
const url = https://api.openweathermap.org/data/2.5/forecast?lat={this.location.latitude}&lon={this.location.longitude}&appid=${this.apiKey};
const response = await httpRequest.request(url);
this.weatherData = JSON.parse(response.result);
this.lastUpdate = Date.now();
this.checkAlerts();
catch (error) {
console.error('Failed to fetch weather data:', error);
}
private checkAlerts(): void {
if (!this.weatherData) return;
const alerts: WeatherAlert[] = [];
// 检查异常天气
this.weatherData.list.forEach(forecast => {
const weather = forecast.weather[0];
const windSpeed = forecast.wind.speed;
const rain = forecast.rain?.['1h'] || 0;
// 大风预警
if (windSpeed > 10) { // 10m/s
alerts.push({
type: 'wind',
severity: 'warning',
startTime: forecast.dt * 1000,
endTime: (forecast.dt + 3600) * 1000,
value: windSpeed
});
// 暴雨预警
if (rain > 20) { // 20mm/h
alerts.push({
type: 'rain',
severity: 'warning',
startTime: forecast.dt * 1000,
endTime: (forecast.dt + 3600) * 1000,
value: rain
});
// 低温预警
if (forecast.main.temp < 5) { // 5°C
alerts.push({
type: 'cold',
severity: 'warning',
startTime: forecast.dt * 1000,
endTime: (forecast.dt + 3600) * 1000,
value: forecast.main.temp
});
});
if (alerts.length > 0) {
EventBus.emit('weatherAlerts', alerts);
}
public getCurrentWeather(): WeatherData | null {
return this.weatherData;
public getAlerts(): WeatherAlert[] {
const alerts = EventBus.getLastEvent('weatherAlerts');
return alerts || [];
public getLastUpdateTime(): number {
return this.lastUpdate;
public setUpdateInterval(interval: number): void {
this.updateInterval = interval;
}
export const weatherService = WeatherAlertService.getInstance();
跨设备同步服务
// SyncService.ets
import distributedData from ‘@ohos.data.distributedData’;
import deviceManager from ‘@ohos.distributedHardware.deviceManager’;
class SyncService {
private static instance: SyncService;
private kvManager: distributedData.KVManager;
private kvStore: distributedData.KVStore;
private connectedDevices: string[] = [];
private constructor() {
this.initKVStore();
this.initDeviceListener();
private async initKVStore(): Promise<void> {
const config = {
bundleName: 'com.example.greenhouse',
userInfo: { userId: 'default' }
};
this.kvManager = distributedData.createKVManager(config);
this.kvStore = await this.kvManager.getKVStore('greenhouse_sync', {
createIfMissing: true,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
});
this.kvStore.on('dataChange', (data) => {
this.handleRemoteChanges(data);
});
private initDeviceListener(): void {
deviceManager.on('deviceStateChange', (data) => {
this.handleDeviceStateChange(data);
});
this.updateConnectedDevices();
private async updateConnectedDevices(): Promise<void> {
const devices = await deviceManager.getTrustedDeviceList();
this.connectedDevices = devices.map(d => d.deviceId);
private handleDeviceStateChange(data: deviceManager.DeviceStateChangeData): void {
if (data.deviceState === deviceManager.DeviceState.ONLINE) {
if (!this.connectedDevices.includes(data.deviceId)) {
this.connectedDevices.push(data.deviceId);
} else if (data.deviceState === deviceManager.DeviceState.OFFLINE) {
this.connectedDevices = this.connectedDevices.filter(id => id !== data.deviceId);
}
public static getInstance(): SyncService {
if (!SyncService.instance) {
SyncService.instance = new SyncService();
return SyncService.instance;
public async syncSensorData(data: SensorData): Promise<void> {
const syncData: SyncSensorData = {
...data,
deviceId: deviceManager.getLocalDevice().id,
timestamp: Date.now()
};
await this.kvStore.put(sensor_{data.nodeId}_{Date.now()}, JSON.stringify(syncData));
public async syncMoisturePrediction(prediction: MoisturePrediction): Promise<void> {
const syncData: SyncPrediction = {
...prediction,
deviceId: deviceManager.getLocalDevice().id,
timestamp: Date.now()
};
await this.kvStore.put(prediction_${Date.now()}, JSON.stringify(syncData));
public async syncWeatherAlert(alert: WeatherAlert): Promise<void> {
const syncData: SyncAlert = {
...alert,
deviceId: deviceManager.getLocalDevice().id,
timestamp: Date.now()
};
await this.kvStore.put(alert_{alert.type}_{Date.now()}, JSON.stringify(syncData));
private handleRemoteChanges(data: distributedData.ChangeInfo): void {
if (data.deviceId === deviceManager.getLocalDevice().id) return;
try {
const parsed = JSON.parse(data.value);
if (data.key.startsWith('sensor_')) {
EventBus.emit('remoteSensorData', parsed);
else if (data.key.startsWith(‘prediction_’)) {
EventBus.emit('remotePrediction', parsed);
else if (data.key.startsWith(‘alert_’)) {
EventBus.emit('remoteWeatherAlert', parsed);
} catch (error) {
console.error('Failed to parse sync data:', error);
}
public async getConnectedDevices(): Promise<string[]> {
await this.updateConnectedDevices();
return […this.connectedDevices];
public async broadcastCommand(command: GreenhouseCommand): Promise<void> {
const syncCommand: SyncCommand = {
type: 'command',
command,
timestamp: Date.now(),
deviceId: deviceManager.getLocalDevice().id
};
await this.kvStore.put(command_${Date.now()}, JSON.stringify(syncCommand));
}
export const syncService = SyncService.getInstance();
三、主界面实现
大棚监测主界面
// GreenhouseView.ets
@Component
struct GreenhouseView {
@State sensorData: SensorData[] = [];
@State predictions: MoisturePrediction[] = [];
@State alerts: WeatherAlert[] = [];
@State connectedNodes: number = 0;
@State topology: string = ‘hybrid’;
aboutToAppear() {
this.initEventListeners();
this.loadInitialData();
build() {
Column() {
// 状态栏
StatusBar({
nodeCount: this.connectedNodes,
topology: this.topology
})
.margin({ top: 16, bottom: 16 })
// 土壤墒情图表
MoistureChart({
history: this.sensorData,
predictions: this.predictions
})
.height(200)
.margin({ bottom: 16 })
// 天气预警
if (this.alerts.length > 0) {
WeatherAlerts({ alerts: this.alerts })
.margin({ bottom: 16 })
// 传感器节点列表
NodeList({ nodes: this.sensorData })
.layoutWeight(1)
.padding(16)
private initEventListeners(): void {
EventBus.on('remoteSensorData', (data) => {
this.updateSensorData(data);
});
EventBus.on('remotePrediction', (prediction) => {
this.updatePrediction(prediction);
});
EventBus.on('remoteWeatherAlert', (alert) => {
this.addAlert(alert);
});
EventBus.on('networkTopologyChanged', (topology) => {
this.topology = topology;
});
private async loadInitialData(): Promise<void> {
this.sensorData = await sensorService.getLatestData();
this.predictions = await moistureService.getLatestPredictions();
this.alerts = weatherService.getAlerts();
this.connectedNodes = meshNetwork.getNodeCount();
this.topology = meshNetwork.getNetworkTopology();
private updateSensorData(data: SensorData): void {
const index = this.sensorData.findIndex(d => d.nodeId === data.nodeId);
if (index >= 0) {
this.sensorData[index] = data;
else {
this.sensorData.push(data);
this.sensorData = […this.sensorData];
private updatePrediction(prediction: MoisturePrediction): void {
const index = this.predictions.findIndex(p =>
p.hours === prediction.hours
);
if (index >= 0) {
this.predictions[index] = prediction;
else {
this.predictions.push(prediction);
this.predictions = […this.predictions];
private addAlert(alert: WeatherAlert): void {
this.alerts = [alert, ...this.alerts];
}
@Component
struct StatusBar {
private nodeCount: number;
private topology: string;
build() {
Row() {
Column() {
Text(${this.nodeCount})
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text(‘节点’)
.fontSize(12)
.margin({ right: 16 })
Column() {
Text(this.getTopologyName())
.fontSize(18)
.fontWeight(FontWeight.Bold)
Text('拓扑')
.fontSize(12)
}
private getTopologyName(): string {
switch (this.topology) {
case 'star': return '星型';
case 'mesh': return '网状';
default: return '混合';
}
@Component
struct MoistureChart {
private history: SensorData[];
private predictions: MoisturePrediction[];
build() {
Canvas({ context: this.getContext() })
.width(‘100%’)
.height(‘100%’)
.onReady(() => this.drawChart())
private getContext(): CanvasRenderingContext2D {
// 创建并返回Canvas上下文
return new CanvasRenderingContext2D();
private drawChart(): void {
// 实现绘制土壤墒情曲线图的逻辑
// 实际项目中可以使用第三方图表库
}
@Component
struct WeatherAlerts {
private alerts: WeatherAlert[];
build() {
Column() {
Text(‘天气预警’)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 8 })
ForEach(this.alerts, (alert) => {
AlertItem({ alert })
.margin({ bottom: 8 })
})
}
@Component
struct AlertItem {
private alert: WeatherAlert;
build() {
Row() {
Image(this.getAlertIcon())
.width(24)
.height(24)
.margin({ right: 8 })
Column() {
Text(this.getAlertTitle())
.fontSize(14)
.fontWeight(FontWeight.Bold)
Text(this.getAlertTime())
.fontSize(12)
.fontColor('#666666')
.layoutWeight(1)
Text({this.alert.value}{this.getUnit()})
.fontSize(14)
.padding(8)
.backgroundColor('#FFF5F5')
.borderRadius(4)
private getAlertIcon(): Resource {
switch (this.alert.type) {
case 'rain': return $r('app.media.ic_rain');
case 'wind': return $r('app.media.ic_wind');
case 'cold': return $r('app.media.ic_cold');
default: return $r('app.media.ic_alert');
}
private getAlertTitle(): string {
switch (this.alert.type) {
case ‘rain’: return ‘暴雨预警’;
case ‘wind’: return ‘大风预警’;
case ‘cold’: return ‘低温预警’;
default: return ‘天气预警’;
}
private getAlertTime(): string {
const date = new Date(this.alert.startTime);
return {date.getHours()}:{date.getMinutes().toString().padStart(2, ‘0’)};
private getUnit(): string {
switch (this.alert.type) {
case 'rain': return 'mm';
case 'wind': return 'm/s';
case 'cold': return '°C';
default: return '';
}
@Component
struct NodeList {
private nodes: SensorData[];
build() {
List() {
ForEach(this.nodes, (node) => {
ListItem() {
NodeItem({ node })
})
}
@Component
struct NodeItem {
private node: SensorData;
build() {
Row() {
Column() {
Text(节点 ${node.nodeId.substr(0, 8)})
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text(温度: {node.temperature.toFixed(1)}°C 湿度: {node.humidity.toFixed(1)}%)
.fontSize(14)
.layoutWeight(1)
Column() {
Text(${node.moisture.toFixed(1)}%)
.fontSize(18)
.fontColor(this.getMoistureColor(node.moisture))
Text('土壤湿度')
.fontSize(12)
}
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
private getMoistureColor(moisture: number): string {
if (moisture < 30) return '#FF5722'; // 干旱
if (moisture > 70) return '#2196F3'; // 过湿
return '#4CAF50'; // 正常
}
灌溉控制界面
// IrrigationView.ets
@Component
struct IrrigationView {
@State autoMode: boolean = true;
@State schedule: IrrigationSchedule[] = [];
@State nextWatering: number | null = null;
aboutToAppear() {
this.loadSchedule();
build() {
Column() {
// 自动模式切换
Row() {
Text('自动灌溉')
.fontSize(16)
.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.autoMode })
.onChange((isOn) => this.toggleAutoMode(isOn))
.margin({ bottom: 24 })
// 下次灌溉时间
if (this.nextWatering) {
Text(下次灌溉: ${this.formatTime(this.nextWatering)})
.fontSize(16)
.margin({ bottom: 16 })
// 灌溉计划列表
ScheduleList({ schedule: this.schedule })
.layoutWeight(1)
// 添加计划按钮
Button('添加计划')
.onClick(() => this.showAddDialog())
.width('100%')
.height(48)
.padding(16)
private loadSchedule(): void {
this.schedule = irrigationService.getSchedule();
this.nextWatering = irrigationService.getNextWateringTime();
this.autoMode = irrigationService.isAutoMode();
private toggleAutoMode(enabled: boolean): void {
this.autoMode = enabled;
irrigationService.setAutoMode(enabled);
private formatTime(timestamp: number): string {
const date = new Date(timestamp);
return {date.getMonth() + 1}月{date.getDate()}日 {date.getHours()}:{date.getMinutes().toString().padStart(2, '0')};
private showAddDialog(): void {
router.push({ url: 'pages/AddSchedule' });
}
@Component
struct ScheduleList {
private schedule: IrrigationSchedule[];
build() {
List() {
ForEach(this.schedule, (item) => {
ListItem() {
ScheduleItem({ schedule: item })
})
}
@Component
struct ScheduleItem {
private schedule: IrrigationSchedule;
build() {
Row() {
Column() {
Text(this.formatTime(this.schedule.time))
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text(持续时间: ${this.schedule.duration}分钟)
.fontSize(14)
.layoutWeight(1)
Button('删除')
.onClick(() => this.deleteSchedule())
.width(80)
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
private formatTime(time: number): string {
const date = new Date(time);
return {date.getHours().toString().padStart(2, '0')}:{date.getMinutes().toString().padStart(2, '0')};
private deleteSchedule(): void {
irrigationService.removeSchedule(this.schedule.id);
}
四、高级功能实现
智能灌溉服务
// IrrigationService.ets
import { moistureService } from ‘./SoilMoistureService’;
import { weatherService } from ‘./WeatherAlertService’;
class IrrigationService {
private static instance: IrrigationService;
private schedule: IrrigationSchedule[] = [];
private isAutoMode: boolean = true;
private nextWatering: number | null = null;
private wateringTimer: number | null = null;
private constructor() {
this.loadSchedule();
this.initEventListeners();
private initEventListeners(): void {
EventBus.on('moisturePredicted', (predictions) => {
if (this.isAutoMode) {
this.adjustWatering(predictions);
});
EventBus.on('weatherAlerts', (alerts) => {
if (this.isAutoMode) {
this.handleWeatherAlerts(alerts);
});
public static getInstance(): IrrigationService {
if (!IrrigationService.instance) {
IrrigationService.instance = new IrrigationService();
return IrrigationService.instance;
private loadSchedule(): void {
const saved = storage.get('irrigationSchedule');
if (saved) {
this.schedule = JSON.parse(saved);
this.calculateNextWatering();
}
private saveSchedule(): void {
storage.set(‘irrigationSchedule’, JSON.stringify(this.schedule));
this.calculateNextWatering();
public setAutoMode(enabled: boolean): void {
this.isAutoMode = enabled;
storage.set('irrigationAutoMode', enabled);
if (enabled) {
this.adjustWatering(moistureService.getLatestPredictions());
}
public isAutoMode(): boolean {
return this.isAutoMode;
public getSchedule(): IrrigationSchedule[] {
return [...this.schedule];
public addSchedule(schedule: IrrigationSchedule): void {
this.schedule.push(schedule);
this.saveSchedule();
public removeSchedule(id: string): void {
this.schedule = this.schedule.filter(s => s.id !== id);
this.saveSchedule();
private calculateNextWatering(): void {
if (this.schedule.length === 0) {
this.nextWatering = null;
return;
const now = Date.now();
let nextTime: number | null = null;
for (const item of this.schedule) {
const timeToday = new Date();
timeToday.setHours(
new Date(item.time).getHours(),
new Date(item.time).getMinutes(),
0, 0
);
if (timeToday.getTime() > now &&
(nextTime === null || timeToday.getTime() < nextTime)) {
nextTime = timeToday.getTime();
}
this.nextWatering = nextTime;
// 设置定时器
if (this.wateringTimer) {
clearTimeout(this.wateringTimer);
if (this.nextWatering) {
const delay = this.nextWatering - now;
this.wateringTimer = setTimeout(() => {
this.executeWatering();
}, delay);
}
private executeWatering(): void {
const currentSchedule = this.schedule.find(s => {
const scheduleTime = new Date(s.time);
const now = new Date();
return scheduleTime.getHours() === now.getHours() &&
scheduleTime.getMinutes() === now.getMinutes();
});
if (currentSchedule) {
EventBus.emit('startWatering', {
duration: currentSchedule.duration,
scheduleId: currentSchedule.id
});
this.calculateNextWatering();
private adjustWatering(predictions: MoisturePrediction[]): void {
if (predictions.length === 0) return;
const next6h = predictions.find(p => p.hours === 6);
if (!next6h) return;
// 根据预测湿度调整灌溉
if (next6h.moisture < 40) { // 干旱
this.addEmergencyWatering(30); // 30分钟紧急灌溉
else if (next6h.moisture > 70) { // 过湿
this.cancelNextWatering();
}
private handleWeatherAlerts(alerts: WeatherAlert[]): void {
const rainAlerts = alerts.filter(a => a.type === ‘rain’);
if (rainAlerts.length > 0) {
// 有降雨预警,取消下次灌溉
this.cancelNextWatering();
}
private addEmergencyWatering(duration: number): void {
const now = Date.now();
const emergencySchedule: IrrigationSchedule = {
id: ‘emergency_’ + now,
duration,
isEmergency: true
};
this.schedule.push(emergencySchedule);
this.saveSchedule();
private cancelNextWatering(): void {
if (!this.nextWatering) return;
// 找到并删除计划中的对应项
const scheduleTime = new Date(this.nextWatering);
this.schedule = this.schedule.filter(s => {
const sTime = new Date(s.time);
return !(sTime.getHours() === scheduleTime.getHours() &&
sTime.getMinutes() === scheduleTime.getMinutes());
});
this.saveSchedule();
public getNextWateringTime(): number | null {
return this.nextWatering;
public manualWatering(duration: number): void {
EventBus.emit('startWatering', {
duration,
manual: true
});
}
export const irrigationService = IrrigationService.getInstance();
传感器数据聚合服务
// SensorDataService.ets
import { meshNetwork } from ‘./MeshNetworkService’;
import { syncService } from ‘./SyncService’;
class SensorDataService {
private static instance: SensorDataService;
private sensorData: SensorData[] = [];
private aggregationInterval: number = 60000; // 1分钟聚合一次
private constructor() {
this.startAggregation();
public static getInstance(): SensorDataService {
if (!SensorDataService.instance) {
SensorDataService.instance = new SensorDataService();
return SensorDataService.instance;
private startAggregation(): void {
setInterval(() => {
this.aggregateData();
}, this.aggregationInterval);
private aggregateData(): void {
const nodes = meshNetwork.getAllNodes();
const aggregated: SensorData[] = [];
// 按区域分组聚合
const zones = this.groupByZone(nodes);
zones.forEach(zone => {
const zoneData = this.aggregateZoneData(zone);
if (zoneData) {
aggregated.push(zoneData);
});
this.sensorData = aggregated;
// 同步到其他设备
aggregated.forEach(data => {
syncService.syncSensorData(data);
});
private groupByZone(nodes: SensorNode[]): SensorNode[][] {
// 简单按节点ID前缀分组(实际项目应根据位置分组)
const zones: Map<string, SensorNode[]> = new Map();
nodes.forEach(node => {
const zoneId = node.id.substr(0, 3); // 前3字符作为区域ID
if (!zones.has(zoneId)) {
zones.set(zoneId, []);
zones.get(zoneId)!.push(node);
});
return Array.from(zones.values());
private aggregateZoneData(nodes: SensorNode[]): SensorData | null {
if (nodes.length === 0) return null;
// 计算平均值
const avgMoisture = nodes.reduce((sum, node) =>
sum + node.moisture, 0) / nodes.length;
const avgTemp = nodes.reduce((sum, node) =>
sum + node.temperature, 0) / nodes.length;
const avgHumidity = nodes.reduce((sum, node) =>
sum + node.humidity, 0) / nodes.length;
return {
nodeId: zone_${nodes[0].id.substr(0, 3)},
timestamp: Date.now(),
moisture: avgMoisture,
temperature: avgTemp,
humidity: avgHumidity,
battery: Math.min(...nodes.map(n => n.battery))
};
public getLatestData(): SensorData[] {
return [...this.sensorData];
public getZoneData(zoneId: string): SensorData | null {
return this.sensorData.find(d => d.nodeId === zone_${zoneId}) || null;
public setAggregationInterval(interval: number): void {
this.aggregationInterval = interval;
}
export const sensorService = SensorDataService.getInstance();
设备管理服务
// DeviceManagementService.ets
import power from ‘@ohos.power’;
import { meshNetwork } from ‘./MeshNetworkService’;
class DeviceManagementService {
private static instance: DeviceManagementService;
private batteryOptimization: boolean = true;
private powerMode: power.Mode = power.Mode.NORMAL;
private constructor() {
this.initPowerListener();
private initPowerListener(): void {
power.on('powerModeChange', (mode) => {
this.powerMode = mode;
this.adjustNodeParameters();
});
public static getInstance(): DeviceManagementService {
if (!DeviceManagementService.instance) {
DeviceManagementService.instance = new DeviceManagementService();
return DeviceManagementService.instance;
private adjustNodeParameters(): void {
const nodes = meshNetwork.getAllNodes();
nodes.forEach(node => {
// 根据电源模式调整节点采样频率
const interval = this.getSamplingInterval();
meshNetwork.setNodeSamplingInterval(node.id, interval);
});
private getSamplingInterval(): number {
switch (this.powerMode) {
case power.Mode.POWER_SAVE: return 300000; // 5分钟
case power.Mode.PERFORMANCE: return 60000; // 1分钟
default: return 120000; // 2分钟
}
public enableBatteryOptimization(enabled: boolean): void {
this.batteryOptimization = enabled;
if (enabled) {
power.setMode(power.Mode.POWER_SAVE);
else {
power.setMode(power.Mode.NORMAL);
}
public isBatteryOptimizationEnabled(): boolean {
return this.batteryOptimization;
public getPowerMode(): power.Mode {
return this.powerMode;
public getNodeStatus(nodeId: string): NodeStatus | null {
const node = meshNetwork.getNodeStatus(nodeId);
if (!node) return null;
return {
id: node.id,
battery: node.battery,
lastSeen: node.lastSeen,
signalStrength: this.getSignalQuality(node.rssi)
};
private getSignalQuality(rssi: number): string {
if (rssi >= -50) return '优秀';
if (rssi >= -70) return '良好';
if (rssi >= -80) return '一般';
return '差';
public rebootNode(nodeId: string): void {
meshNetwork.sendCommand(nodeId, 'reboot');
public updateNodeFirmware(nodeId: string): void {
meshNetwork.sendCommand(nodeId, 'update');
}
export const deviceManager = DeviceManagementService.getInstance();
五、总结
本智慧农业大棚监测系统实现了以下核心价值:
智能组网:自适应网络拓扑优化节点通信
气象预警:实时监测异常天气并提供预警
跨端协同:多终端同步监测数据和控制指令
节水灌溉:基于预测模型优化灌溉计划
扩展方向:
增加作物生长模型
集成病虫害监测
开发产量预测功能
支持更多传感器类型
添加区块链数据存证
注意事项:
土壤传感器需定期校准
网络拓扑优化会消耗额外电量
气象服务需要API密钥
首次使用建议进行系统校准
低电量节点可能影响数据精度
