基于物联网的嵌入式多模态环境感知与动态消杀净化系统

基于物联网的嵌入式多模态环境感知与动态消杀净化系统

采购表


设备采购统计表

类别 设备/模块名称 型号/参数 数量 参考单价(元) 供应商/获取途径 备注
主控开发板 STM32U5 Nucleo开发板 NUCLEO-U575ZI-Q 1 免费(押金申请) ST官方申请(需提交项目计划) 主控芯片STM32U575,支持LPBAM低功耗模式
传感器模块 6轴惯性传感器(加速度计+陀螺仪) LSM6DSV16XTR 1 免费(样片申请) ST官方申请 用于振动检测、人流密度感知
温湿度传感器 HTS221TR 1 免费(样片申请) ST官方申请 监测环境温湿度,评估细菌滋生风险
激光TOF距离传感器(8x8区域检测) VL53L5CX 1 300 第三方(淘宝/得捷电子) 检测空间占用率,需自行设计安装支架
电化学气体传感器(VOC/甲醛检测) SGP30 1 120 第三方(淘宝) 检测挥发性有机物浓度
紫外线强度传感器 GUVA-S12SD 1 80 第三方(淘宝) 监测紫外线灯消杀效果
通信模块 NB-IoT模组(低功耗广域网)(广和通L610模组) BC26 1 150 第三方(淘宝/移远代理商) 用于数据上传至云端
NFC读卡器开发板 X-NUCLEO-NFC09A1 1 免费(押金申请) ST官方申请 实现设备身份认证与参数配置
执行机构 紫外线灯模块(UVC 275nm) 12V/5W带驱动电路 2 50 第三方(淘宝) 用于动态消杀,需配合PWM控制
喷雾消杀装置(12V微型水泵+雾化喷头) 12V/0.5A 1 80 第三方(淘宝) 喷洒消毒液,需MOS管驱动电路
电源管理 锂电池(3.7V/2000mAh) 18650电池+保护板 2 20 第三方(淘宝) 供电核心,需搭配升压模块(5V/12V输出)
低功耗电源管理模块 TPS63020 1 15 第三方(得捷电子) 3.7V升压至5V/12V,效率>90%
结构件 3D打印外壳(定制设计) PLA材料 1 100 本地3D打印服务 包含传感器安装孔位、消杀喷口
其他辅材 PCB打样(主控+传感器集成板) 2层板/10x10cm 5 50(5片总价) 嘉立创/捷配 集成传感器接口与驱动电路
杜邦线、连接器 20cm/公对母 30 0.5/根 淘宝 用于模块间连接
总预算(参考) 约1200元 不含免费申请设备(主控板、ST传感器等),实际成本可压缩至800元内

关键说明

  1. ST官方免费资源:
    • 主控板(NUCLEO-U575ZI-Q)、LSM6DSV16XTR、HTS221TR、NFC开发板可通过ST官网申请(需押金,赛后退还)。

    • 申请链接:ST开发板申请入口

  2. 替代方案:
    • 激光TOF传感器:若预算有限,可改用ST推荐的VL6180(单点测距,成本约50元),但精度下降。

    • 通信模块:可用STM32WL LoRa模组替代NB-IoT(需修改通信协议)。

  3. 核心成本控制:
    • 优先申请ST免费硬件,第三方传感器选择国产平替型号(如SGP30替换为ENS160,成本降至80元)。

    • 3D外壳可手工制作(亚克力板切割),节省60元。

建议团队提前2个月启动采购,优先申请ST资源,确保开发周期!

申请单:

image-20250509180656088

类似作品

公交车内整体消毒只需10秒!光谷企业家居家研发出智能“弥雾消杀系统”

https://mp.weixin.qq.com/s?__biz=MzA3MzE3MTcxMQ==&mid=2649498319&idx=1&sn=9ba16da9e0fcd272b570c2017634827e&chksm=86df3aab7309a53526c0629ca628b79aa495b6342e86a213177f4ebf515fb7989cfc834fc2f2#rd

STM32 NUCLEO-U575ZI-Q

STM32U575是否可以进行通信?是否可以进行云端通信?(与云端服务器进行通信)

如果不行,那加上广和通ADP-L610-Arduino模组呢?

ADP-L610-Arduino_V3.0(低配版)

通信模块:ADP-L610-Arduino

image-20250607155251842

image-20250607155241506

ADP-L610-Arduino开发板介绍

image-20250607155706794

image-20250607160002969

image-20250607160036838

image-20250607160134802

云平台区分

image-20250607200204897

image-20250608154029086

image-20250608154040260

AT命令介绍

image-20250608160744269

SSCOM串口调试工具使用

image-20250608160815865

短信功能

image-20250608163628906

image-20250608163845689

image-20250608171813148

image-20250608171848412

连接公有云(腾讯云)

image-20250621172302897

image-20250621174608810

华为云:https://bbs.elecfans.com/jishu_2327740_1_1.html

image-20250630225415423

image-20250630231929052

连接公有云(华为云)

https://bbs.elecfans.com/jishu_2327740_1_1.html

接入地址337c64ac60.st1.iotda-device.cn-north-4.myhuaweicloud.com

{
 "device_id": "6857bbd732771f177b44a649_tempSensor",
 "shadow": [
  {
   "service_id": "wsdcgq",
   "desired": {
    "properties": null,
    "event_time": null
   },
   "reported": {
    "properties": {
     "temp_info": 25
    },
    "event_time": "20250626T102128Z"
   },
   "version": 0
  },
  {
   "service_id": "Sensor",
   "desired": {
    "properties": null,
    "event_time": null
   },
   "reported": {
    "properties": {
     "temp_info": 27,
     "temperature": 27
    },
    "event_time": "20250629T115721Z"
   },
   "version": 8
  }
 ]
}

后续需要做的事情:增加/修改显示数据(富氧含量)

  1. 增加/修改华为云“产品”——》“详情”——》“模型定义”——》“服务列表”——》“新增属性”
  2. 修改L610中的代码
  3. 修改微信小程序

空气质量:airQuality

最后更新时间:updateTime

AT+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",70,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":27}}]}"

AT+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",105,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":27,\"humidity\":45,\"formaldehyde\":0.053}}]}"

AT+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",117,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":27,\"humidity\":45,\"formaldehyde\":0.052,\"oxygen\":21}}]}"

printf("AT+HMPUB=1,\"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report\",117,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sensor\\\",\\\"properties\\\":{\\\"temperature\\\":23,\\\"humidity\\\":58,\\\"formaldehyde\\\":0.011,\\\"oxygen\\\":21}}]}\"\r\n");

中国移动物联网开发平台OneNET云平台

温度传感器设备秘钥:12345678

连接华为云

AT

AT+MIPCALL?
[16:25:48.120]发→◇AT
□
[16:25:48.122]收←◆AT
OK

[16:25:58.160]发→◇ATI
□
[16:25:58.162]收←◆ATI
Fibocom
L610-CN-62-36
16000.1208.00.86.02.02
V1.2

OK

[16:26:10.871]发→◇AT+CPIN?
□
[16:26:10.872]收←◆AT+CPIN?
+CPIN: READY

OK

[16:27:14.101]发→◇AT+CSQ
□
[16:27:14.103]收←◆AT+CSQ
+CSQ: 21,99

OK

[16:27:27.885]发→◇AT+CGREG?
□
[16:27:27.886]收←◆AT+CGREG?
+CGREG: 0,1

OK

[16:27:41.460]发→◇AT+MIPCALL?
□
[16:27:41.463]收←◆AT+MIPCALL?
+MIPCALL: 0

OK

[16:27:48.445]发→◇AT+MIPCALL=1
□
[16:27:48.447]收←◆AT+MIPCALL=1
[16:27:48.484]收←◆
OK

+MIPCALL: 10.67.24.233

[16:27:55.251]发→◇AT+MIPCALL?
□
[16:27:55.253]收←◆AT+MIPCALL?
+MIPCALL: 1,10.67.24.233

OK

[16:28:28.684]发→◇AT+HMCON=0,60,"337c64ac60.st1.iotda-device.cn-north-4.myhuaweicloud.com","1883","6857bbd732771f177b44a649_tempSensor","12345678",0
□
[16:28:28.685]收←◆AT+HMCON=0,60,"337c64ac60.st1.iotda-device.cn-north-4.myhuaweicloud.com","1883","6857bbd732771f177b44a649_tempSensor","12345678",0
[16:28:29.290]收←◆
+HMCON OK


[16:37:17.666]发→◇AT+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",117,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":23,\"humidity\":60,\"formaldehyde\":0.082,\"oxygen\":21}}]}"
□
[16:37:17.668]收←◆AT+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",117,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":23,\"humidity\":60,\"formaldehyde\":0.082,\"oxygen\":21}}]}"
[16:37:17.789]收←◆
+HMPUB OK

+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",87,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":30,\"humidity\":71,\"formaldehyde\":0.010,\"oxygen\":4280}}]}"
AT+HMPUB=1,"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report",119,"{\"services\":[{\"service_id\":\"Sensor\",\"properties\":{\"temperature\":30,\"humidity\":71,\"formaldehyde\":0.010,\"oxygen\":4280}}]}"
+HMPUB ERR:1

AT+CFUN=1

AT+CGSN?

AT+CFSN?

AT+CGMR?

AT+CGDCONT?



AT+CGDCONT=1,"IP","3gnet"



// 强制关闭当前冲突连接
AT+MIPCALL=0

// 复位网络堆栈(关键!清除运营商风控标记)
AT+CFUN=0
// 等待20秒
AT+CFUN=1

// 重建CID=0的有效通路
AT+CGACT=0,0  // 先解绑,好像不需要
AT+CGACT=0,1  // 再绑定

// 触发IP重新分配
AT+MIPCALL=1

// 预期响应
+MIPCALL: 10.x.x.x  // 新IP
OK

image-20250628172118164

微信小程序(微信开发者工具)

项目类型:物联网设备监控系统
​核心功能描述​​:

构建一个环境监测数据看板小程序,实时显示来自嵌入式设备的:
1. 环境温度数值及可视化变化曲线
2. 相对湿度数值及动态变化图
3. 甲醛浓度检测值及超标预警系统

通过HTTP API接口(支持JSON格式)连接后端服务器:
- 数据获取接口:接收{"temp":25.3,"humidity":60,"formaldehyde":0.05}格式的环境数据
- 设备控制接口:向嵌入式设备发送消杀启动指令

用户界面要求:
1. 首页:卡片式数据展示(温度计/湿度计/甲醛检测可视化组件)
2. 历史数据页:ECharts折线图展示24小时趋势
3. 控制面板:手动消杀开关及自动模式设置
4. 报警通知:甲醛超标时弹出微信服务通知

image-20250621160836285

哔哩哔哩:【手把手讲解华为云物联网云平台的使用以及应用侧的开发(2024最新版)】

应用侧开发:

1. 调用什么API接口获取设备端上传的数据?

获取影子数据的接口

在线接口调试:https://console.huaweicloud.com/apiexplorer/#/openapi/IOTDA/debug?api=ShowDeviceShadow

真实请求URL:https://337c64ac60.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/db5bbccba3484859b838ebbe7a86479c/devices/6857bbd732771f177b44a649_tempSensor/shadow

2. 调用什么API接口下发命令给设备端?

两个方法:下发命令 和 修改设备属性

查询设备属性:https://console.huaweicloud.com/apiexplorer/#/openapi/IOTDA/debug?api=ListProperties

修改设备属性:https://console.huaweicloud.com/apiexplorer/#/openapi/IOTDA/debug?api=UpdateProperties

3. 调用API接口需要什么必要的参数?

IAM账号:
IAM密码:
(domain.user)主账号名称:凭证(项目ID):
设备ID(为了获取token):

项目ID 项目 所属区域
db5bbccba3484859b838ebbe7a86479c cn-north-4 华北-北京四
url: 'https://iotda.cn-north-4.myhuaweicloud.com/v5/iot/db5bbccba3484859b838ebbe7a86479c/devices/6857bbd732771f177b44a649_tempSensor/shadow', 

临时代码

index.js_v1.1
// pages/index/index.js
Page({
  data: {
    activeTab: 'real-time',
    temperature: 'N/A',
    humidity: 'N/A',
    airQuality: 'N/A',  // 45
    airStatus: 'N/A',   // 优
    formaldehyde: 'N/A',
    updateTime: 'N/A',
    overallStatus: 'N/A', // 良好
    overallStatusClass: '',   // good
    showFormaldehydeWarning: false,
    autoUpdate: true,
    formaldehydeAdvice: ''  // 新增甲醛处理建议字段
  },

  onLoad: function() {
    // 加载华为云IoT设备数据
    this.loadDeviceData();
    
    // 启动轮询更新
    this.startPolling();
  },
  
  onUnload: function() {
    // 清理定时器
    this.stopPolling();
  },
  
  onHide: function() {  // 正确放置onHide生命周期
    if (typeof this.stopPolling === 'function') {
      this.stopPolling();
    }
  },
  
  onShow: function() {  // 正确放置onShow生命周期
    if (this.data.autoUpdate) {
      this.startPolling();
    }
  },
  
  loadDeviceData: async function() {
    const that = this;
    
    // 1. 先获取华为云Token
    try {
      const token = await that.getHuaweiCloudToken();
      // 2. 请求设备影子数据
      wx.request({
        url: 'https://337c64ac60.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/db5bbccba3484859b838ebbe7a86479c/devices/6857bbd732771f177b44a649_tempSensor/shadow', 
        method: 'GET',
        header: {
          'Content-Type': 'application/json',
          'X-Auth-Token': token // 使用动态获取的Token
        },
        success(res) {
          if (res.statusCode === 200) {
            // 3. 正确解析华为云数据结构
            const shadowData = res.data.shadow || [];
            const environmentService = shadowData.find(item => item.service_id === 'Sensor');
            
            if (environmentService && environmentService.reported) {
              const properties = environmentService.reported.properties || {};
              const eventTime = environmentService.reported.event_time;
              
              // 解析数据
              const temperature = properties.temperature;
              const humidity = properties.humidity;
              const airQuality = properties.pm25; // 根据实际属性名调整
              const formaldehyde = properties.formaldehyde;
              
              // 4. 转换时间格式(华为云使用UTC时间)
              let updateTimeStr = 'N/A'; // 默认值
              if (eventTime) {
              // 使用正则处理华为云特殊时间格式: "20250629T115721Z"
              const isoTime = eventTime.replace(/^(\d{4})(\d{2})(\d{2})T(.+Z)$/, '$1-$2-$3T$4');
              const dateObj = new Date(isoTime);

              // 检查是否为有效日期
              if (!isNaN(dateObj.getTime())) {
                updateTimeStr = dateObj.toLocaleString('zh-CN', {
                  year: 'numeric',
                  month: '2-digit',
                  day: '2-digit',
                  hour: '2-digit',
                  minute: '2-digit',
                  second: '2-digit',
                  hour12: false
                }).replace(/\//g, '-');
              }
            }
            // 计算状态数据
            const statusData = that.calculateStatus(
              temperature, 
              humidity, 
              airQuality, 
              formaldehyde
            );
              // 5. 更新数据
              that.setData({
                temperature: temperature ? temperature : 'N/A',
                humidity: humidity ? humidity  : 'N/A',
                //airQuality: airQuality || 'N/A',
                formaldehyde: formaldehyde || 'N/A',
                updateTime: updateTimeStr,
                airStatus: statusData.airStatus,
                overallStatus: statusData.overallStatus,
                overallStatusClass: statusData.overallStatusClass,
                showFormaldehydeWarning: statusData.showFormaldehydeWarning,
                formaldehydeAdvice: statusData.formaldehydeAdvice
                // ...其他数据
              });
            } else {
              console.warn('未找到环境服务数据');
              wx.showToast({ title: '数据格式异常', icon: 'none' });
            }
          } else if (res.statusCode === 401) {
            console.warn('Token过期,重新获取');
            that.refreshTokenAndRetry();
          } else {
            wx.showToast({ title: `请求失败: ${res.statusCode}`, icon: 'none' });
          }
        },
        fail(err) {
          console.error('API请求失败', err);
          wx.showToast({ title: '网络错误', icon: 'none' });
        }
      });
    } catch (error) {
      console.error('获取Token失败', error);
      wx.showToast({ title: '认证失败', icon: 'none' });
    }
  },
  // 计算环境状态
  calculateStatus: function(temperature, humidity, airQuality, formaldehyde) {
    // 解析数值
    const tempValue = parseFloat(temperature) || 0;
    const humValue = parseFloat(humidity) || 0;
    const airValue = parseFloat(airQuality) || 0;
    const hchoValue = parseFloat(formaldehyde) || 0;
    
    // 空气质量状态
    const airStatus = airValue <= 50 ? '优' : 
                     airValue <= 100 ? '良' : 
                     airValue <= 150 ? '轻度污染' : 
                     airValue <= 200 ? '中度污染' : '重度污染';
    
    // 默认状态
    let overallStatus = '良';
    let overallStatusClass = 'good';
    let showFormaldehydeWarning = false;
    let formaldehydeAdvice = '';
    
    // 特殊状态检测
    if (hchoValue > 0.08) {
      overallStatus = '甲醛超标';
      overallStatusClass = 'danger';
      showFormaldehydeWarning = true;
      formaldehydeAdvice = '请立即开窗通风,使用空气净化器';
    } else if (airValue > 150) {
      overallStatus = '空气质量差';
      overallStatusClass = 'warning';
    }
    
    // 综合状态评分
    const statusScore = 
      (hchoValue <= 0.08 ? 1 : 0) * 40 +
      (airValue <= 50 ? 1 : airValue <= 100 ? 0.8 : 0.5) * 30 +
      (tempValue >= 18 && tempValue <= 28 ? 1 : 0.7) * 20 +
      (humValue >= 40 && humValue <= 60 ? 1 : 0.7) * 10;
    
    // 综合状态覆盖
    if (statusScore >= 90) {
      overallStatus = '优';
      overallStatusClass = 'excellent';
    } else if (statusScore >= 70 && !showFormaldehydeWarning) {
      overallStatus = '良';
      overallStatusClass = 'good';
    } else if (!showFormaldehydeWarning) {
      overallStatus = '差';
      overallStatusClass = 'poor';
    }
    
    return {
      airStatus,
      overallStatus,
      overallStatusClass,
      showFormaldehydeWarning,
      formaldehydeAdvice
    };
  },
  // 获取华为云Token的方法
  getHuaweiCloudToken: function() {
    return new Promise((resolve, reject) => {
      // 先检查缓存
      const cachedToken = wx.getStorageSync('huawei_token');
      const expireTime = wx.getStorageSync('token_expire');
      
      // 如果Token有效且未过期
      if (cachedToken && expireTime > Date.now()) {
        return resolve(cachedToken);
      }
      
      // 否则请求新Token
      wx.request({
        url: 'http://192.168.3.10:3000/token',
        success: (res) => {
          if (res.data.token) {
            // 缓存Token(华为云Token有效期通常24小时)
            wx.setStorageSync('huawei_token', res.data.token);
            wx.setStorageSync('token_expire', Date.now() + 23 * 60 * 60 * 1000); // 23小时
            resolve(res.data.token);
          } else {
            reject(new Error('无效的Token响应'));
          }
        },
        fail: reject
      });
    });
  },
  
  // Token过期重试逻辑
  refreshTokenAndRetry: function() {
    const that = this;
    this.getHuaweiCloudToken().then(newToken => {
      that.loadDeviceData(); // 用新Token重试
    }).catch(err => {
      console.error('刷新Token失败', err);
    });
  },
  
  switchTab: function(e) {  // 跳转页面按钮
    const tab = e.currentTarget.dataset.tab;
    if (tab === 'history') {
      // 跳转到历史页面
      wx.navigateTo({
        url: '/pages/history/history'
      });
    } else {
      this.setData({
        activeTab: tab
      });
    }
  },
  
  toggleAutoUpdate: function(e) {
    const autoUpdate = e.detail.value;
    this.setData({ autoUpdate });
    
    if (autoUpdate) {
      this.startPolling();
    } else {
      this.stopPolling();
    }
  },
  
  startPolling: function() {
    // 每5秒更新一次数据
    if (this.data.autoUpdate) {
      this.pollingTimer = setInterval(() => {
        this.loadDeviceData();
      }, 5000);
    }
  },
  
  stopPolling: function() {
    if (this.pollingTimer) {
      clearInterval(this.pollingTimer);
      this.pollingTimer = null;
    }
  }
});
index.js_v1.2

氧气浓度

// pages/index/index.js
Page({
  data: {
    activeTab: 'real-time',
    temperature: 'N/A',
    humidity: 'N/A',
    airQuality: 'N/A', // 保留但不再使用
    airStatus: 'N/A',
    formaldehyde: 'N/A',
    updateTime: 'N/A',
    overallStatus: 'N/A',
    overallStatusClass: '',
    showFormaldehydeWarning: false,
    autoUpdate: true,
    formaldehydeAdvice: ''
  },

  onLoad: function() {
    this.loadDeviceData();
    this.startPolling();
  },
  
  onUnload: function() {
    this.stopPolling();
  },
  
  onHide: function() {
    this.stopPolling();
  },
  
  onShow: function() {
    if (this.data.autoUpdate) {
      this.startPolling();
    }
  },
  
  loadDeviceData: async function() {
    const that = this;
    
    try {
      const token = await that.getHuaweiCloudToken();
      wx.request({
        url: 'https://337c64ac60.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/db5bbccba3484859b838ebbe7a86479c/devices/6857bbd732771f177b44a649_tempSensor/shadow', 
        method: 'GET',
        header: {
          'Content-Type': 'application/json',
          'X-Auth-Token': token
        },
        success(res) {
          if (res.statusCode === 200) {
            const shadowData = res.data.shadow || [];
            const environmentService = shadowData.find(item => item.service_id === 'Sensor');
            
            if (environmentService && environmentService.reported) {
              const properties = environmentService.reported.properties || {};
              const eventTime = environmentService.reported.event_time;
              
              // 解析数据 - 使用正确的属性名
              const temperature = properties.temperature || null;
              const humidity = properties.humidity || null;
              const formaldehyde = properties.formaldehyde || null;
              
              // 格式化更新时间
              let updateTimeStr = 'N/A';
              if (eventTime) {
                // 处理时间格式
                let isoTime = eventTime;
                if (/^\d{8}T\d{6}Z$/.test(eventTime)) {
                  isoTime = eventTime.replace(
                    /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/,
                    '$1-$2-$3T$4:$5:$6Z'
                  );
                }
                
                const dateObj = new Date(isoTime);
                if (!isNaN(dateObj.getTime())) {
                  updateTimeStr = dateObj.toLocaleString('zh-CN', {
                    year: 'numeric',
                    month: '2-digit',
                    day: '2-digit',
                    hour: '2-digit',
                    minute: '2-digit',
                    second: '2-digit',
                    hour12: false
                  }).replace(/\//g, '-');
                }
              }
              
              // 计算状态数据
              const statusData = that.calculateStatus(
                temperature, 
                humidity, 
                null, // 没有空气质量数据
                formaldehyde
              );
              
              // 更新界面
              that.setData({
                temperature: temperature ?? 'N/A',
                humidity: humidity ?? 'N/A',
                formaldehyde: formaldehyde ?? 'N/A',
                updateTime: updateTimeStr,
                airStatus: statusData.airStatus,
                overallStatus: statusData.overallStatus,
                overallStatusClass: statusData.overallStatusClass,
                showFormaldehydeWarning: statusData.showFormaldehydeWarning,
                formaldehydeAdvice: statusData.formaldehydeAdvice
              });
            } else {
              console.warn('未找到环境服务数据');
              wx.showToast({ title: '数据格式异常', icon: 'none' });
            }
          } else if (res.statusCode === 401) {
            that.refreshTokenAndRetry();
          } else {
            wx.showToast({ title: `请求失败: ${res.statusCode}`, icon: 'none' });
          }
        },
        fail(err) {
          console.error('API请求失败', err);
          wx.showToast({ title: '网络错误', icon: 'none' });
        }
      });
    } catch (error) {
      console.error('获取Token失败', error);
      wx.showToast({ title: '认证失败', icon: 'none' });
    }
  },
  
  // 计算环境状态(使用修改后的版本)
  calculateStatus: function(temperature, humidity, airQuality, formaldehyde) {
    // 解析数值
    const tempValue = parseFloat(temperature) || 0;
    const humValue = parseFloat(humidity) || 0;
    const hchoValue = parseFloat(formaldehyde) || 0;
    
    // 空气质量状态 - 使用甲醛作为替代
    const airStatus = hchoValue <= 0.08 ? '优' : 
                     hchoValue <= 0.15 ? '良' : 
                     hchoValue <= 0.3 ? '轻度污染' : '重度污染';
    
    // 默认状态
    let overallStatus = '良';
    let overallStatusClass = 'good';
    let showFormaldehydeWarning = false;
    let formaldehydeAdvice = '';
    
    // 特殊状态检测
    if (hchoValue > 0.08) {
      overallStatus = '甲醛超标';
      overallStatusClass = 'danger';
      showFormaldehydeWarning = true;
      formaldehydeAdvice = '请立即开窗通风,使用空气净化器';
    }
    
    // 综合状态评分(调整权重)
    const statusScore = 
      (hchoValue <= 0.08 ? 1 : 0) * 50 + // 甲醛权重增加
      (tempValue >= 18 && tempValue <= 28 ? 1 : 0.7) * 30 +
      (humValue >= 40 && humValue <= 60 ? 1 : 0.7) * 20;
    
    // 综合状态覆盖
    if (statusScore >= 90) {
      overallStatus = '优';
      overallStatusClass = 'excellent';
    } else if (statusScore >= 70) {
      overallStatus = '良';
      overallStatusClass = 'good';
    } else {
      overallStatus = '差';
      overallStatusClass = 'poor';
    }
    
    return {
      airStatus,
      overallStatus,
      overallStatusClass,
      showFormaldehydeWarning,
      formaldehydeAdvice
    };
  },
  
  // 获取华为云Token的方法(保持不变)
  getHuaweiCloudToken: function() {
    return new Promise((resolve, reject) => {
      const cachedToken = wx.getStorageSync('huawei_token');
      const expireTime = wx.getStorageSync('token_expire');
      
      if (cachedToken && expireTime > Date.now()) {
        return resolve(cachedToken);
      }
      
      wx.request({
        url: 'http://192.168.3.10:3000/token',
        success: (res) => {
          if (res.data.token) {
            wx.setStorageSync('huawei_token', res.data.token);
            wx.setStorageSync('token_expire', Date.now() + 23 * 60 * 60 * 1000);
            resolve(res.data.token);
          } else {
            reject(new Error('无效的Token响应'));
          }
        },
        fail: reject
      });
    });
  },
  
  // 其他方法保持不变...
});

image-20250619214004038

image-20250619214250417

STM32U575与L610串口通信

STM32 NUCLEO-U575ZI-Q

STM32U575ZIT6Q

官网资料:https://www.st.com.cn/zh/evaluation-tools/nucleo-u575zi-q.html#documentation

image-20250630232350750

下载烧录配置:

image-20250630141731218

使用USB转TTL烧录:https://www.bilibili.com/video/BV1wR4y1y7E2/?spm_id_from=333.337.search-card.all.click&vd_source=5ea44610e490085e08131485e125bd07

USB烧录:https://www.bilibili.com/video/BV1YB4y1E7Wp/?spm_id_from=333.337.search-card.all.click&vd_source=5ea44610e490085e08131485e125bd07

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>

//¶¨ÒåÊý¾ÝÀàÐÍ
char *strx,*extstrx,*Readystrx;
char *strx1,*extstrx,*Readystrx;
char RxBuffer[1024],Rxcouter;
char *strstr(const char *, const char *);
uint8_t Res;
int len;
int number;
char *id;
uint8_t gSendCount = 0;  //·¢ËÍÊý¾Ý¼ÆÊý
#define RX_BUF_MAX_LEN 32
uint8_t RXbuff2[1];
uint8_t RXbuff3[1];
uint8_t Databuff2[RX_BUF_MAX_LEN];
uint8_t Databuff3[RX_BUF_MAX_LEN];
uint8_t Data_num2;
uint8_t Data_num3;

typedef struct {
unsigned int oxygen;
	float formaldehyde;
	float temperature;
	float humidity;
}SensorData;

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void ReportToCloud(SensorData data);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void Clear_Buffer(void)
{
	uint8_t i;

	for(i=0;i<Rxcouter;i++)
	RxBuffer[i]=0;
	Rxcouter=0;
}
	
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{ 
	UNUSED(huart); 

	if(huart->Instance ==LPUART1)
	{ 
		RxBuffer[Rxcouter++] = Res; 
		HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&Res, 1); 
	} 
	else if(huart->Instance == USART2)																
	{
		Databuff2[Data_num2++] = RXbuff2[0];														
		HAL_UART_Receive_IT(&huart2,(uint8_t*)&RXbuff2,1);				
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
	}
}
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)  
{
	if(huart->Instance == USART2)																	
	{
		HAL_UART_Transmit(&hlpuart1,(uint8_t*)&Databuff2,Data_num2,1000); 
		HAL_UART_Receive_IT(&huart2,(uint8_t*)&RXbuff2,1);							
		Data_num2=0;
	}
}

int round_float(float f) {
    if (f >= 0.0f) {
        return (int)(f + 0.5f);
    } else {
        return (int)(f - 0.5f);
    }
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  HAL_PWREx_EnableVddIO2();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_LPUART1_UART_Init();
	MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
	HAL_UART_Receive_IT(&hlpuart1,&Res,1);

	HAL_UART_Receive_IT(&huart2,(uint8_t*)&RXbuff2,1);    		
	__HAL_UART_CLEAR_IDLEFLAG(&huart2);											
	__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);							
	
		//Ä£¿é³õʼ»¯£¬µÈ´ý5Ãë
		printf("Ä£¿é³õʼ»¯\r\n");
		HAL_Delay(5000);
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
	
		//²éѯ°æ±¾ÐÅÏ¢
		printf("ATI\r\n");
		HAL_Delay(1000);
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
		strx=strstr((const char*)RxBuffer,(const char*)"Fibocom");
		while(strx==NULL)
		{

			Clear_Buffer();	
			printf("²éѯÐÅϢʧ°Ü");
			HAL_Delay(1000);
			printf("ATI\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"Fibocom");
		}
		Clear_Buffer();
		printf("°æ±¾ÐÅÏ¢ÕýÈ·");
		HAL_Delay(1000);
		
		//²éѯSIM¿¨
		printf("AT+CPIN?\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"READY");
		while(strx==NULL)
		{
//			SensorData Data;
//			memcpy(&Data,(SensorData*)&Databuff2,16);
//			printf("anion : %ld,\r\n",Data.oxygen);
//			printf("ch2o : %0.2f\r\n",Data.formaldehyde);
//			printf("temp : %0.2f\r\n",Data.temperature);
//			printf("hum : %0.2f\r\n",Data.humidity);
//			HAL_Delay(3000);
//						char testBuff[7] ={0};
//			unsigned int a = 0;
//			a = (Databuff2[3] << 24) + (Databuff2[2] << 16) + (Databuff2[1] << 8) + Databuff2[0];
//			memset(testBuff,0, sizeof(testBuff));
//			sprintf(testBuff,"%ld",a);
//			printf("a : %s \r\n",testBuff);
//			
//			float b = 0.1;
//			//b = (float)(Databuff2[4] << 24) + (Databuff2[5] << 16) + (Databuff2[6] << 8) + Databuff2[7];
//			
//			
//			for(int i=0; i<4; i++){
//				printf("Databuff2[%d]:0x%x",4+i,Databuff2[4+i]);
//			}
//			memset(testBuff,0, sizeof(testBuff));
////			sprintf(testBuff,"%0.2f",b);
//			
//			sprintf(testBuff,"%s",(char*)&b);
//			for(int i=0 ;i<4; i++){
//				printf("testBuff[%d]:%x\r\n",i,testBuff[i]);
//			}
//			//printf("b : %s \r\n",testBuff);
//			HAL_Delay(1000);
			Clear_Buffer();	
			printf("SIM¿¨²éѯʧ°Ü");
			HAL_Delay(1000);
			printf("AT+CPIN?\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"READY");
		}
		Clear_Buffer();
		printf("SIM¿¨ÒÑ×¼±¸ºÃ");
		HAL_Delay(1000);
	
		//²éѯÐźÅ
		printf("AT+CSQ\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+CSQ:");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("ÐźŲéѯʧ°Ü");
			HAL_Delay(1000);
			printf("AT+CSQ\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"+CSQ:");
		}
		Clear_Buffer();
		printf("ÐźÅÕý³£");
		HAL_Delay(1000);
		
		//²éÑ¯×¢ÍøÇé¿ö
		printf("AT+CGREG?\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"0,1");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("×¢Íø²»¿ÉÓÃ");
			HAL_Delay(1000);
			printf("AT+CGREG?\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"0,1");
		}
		Clear_Buffer();
		printf("×¢Íø¿ÉÓÃ");
		HAL_Delay(1000);
		
		//ÇëÇóIP
		printf("AT+MIPCALL?\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+MIPCALL: 1");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("»¹Î´»ñÈ¡µ½IP");
			HAL_Delay(1000);
			printf("AT+MIPCALL=1\r\n");
			HAL_Delay(1000);
			strx=strstr((const char*)RxBuffer,(const char*)"+MIPCALL: ");
		}
		Clear_Buffer();
		printf("»ñÈ¡IP³É¹¦");
		HAL_Delay(1000);
		
		//Á¬½Ó»ªÎªÔÆ
		//printf("AT+HMCON=0,60,\"a16110f598.iot-mqtts.cn-north-4.myhuaweicloud.com\",\"8883\",\"6110e20e0ad1ed0286438504_Humidifier001\",\"123456789\",0\r\n");
		printf("AT+HMCON=0,60,\"337c64ac60.st1.iotda-device.cn-north-4.myhuaweicloud.com\",\"1883\",\"6857bbd732771f177b44a649_tempSensor\",\"12345678\",0\r\n");
		HAL_Delay(2000);
		strx=strstr((const char*)RxBuffer,(const char*)"+HMCON OK");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("Á¬½Óʧ°Ü");
			HAL_Delay(1000);
			printf("AT+HMCON=0,60,\"337c64ac60.st1.iotda-device.cn-north-4.myhuaweicloud.com\",\"1883\",\"6857bbd732771f177b44a649_tempSensor\",\"12345678\",0\r\n");
			HAL_Delay(1000);
			strx=strstr((const char*)RxBuffer,(const char*)"+HMCON OK");
		}
		Clear_Buffer();
		printf("Á¬½Ó³É¹¦");
		HAL_Delay(1000);
			
		
		//Éϱ¨ÊôÐÔ
		//printf("AT+HMPUB=1,\"$oc/devices/6110e20e0ad1ed0286438504_Humidifier001/sys/properties/report\",76,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sprayswitchcontrol\\\",\\\"properties\\\":{\\\"Switch\\\":1}}]}\"\r\n");
		printf("AT+HMPUB=1,\"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report\",117,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sensor\\\",\\\"properties\\\":{\\\"temperature\\\":26,\\\"humidity\\\":58,\\\"formaldehyde\\\":0.011,\\\"oxygen\\\":21}}]}\"\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+HMPUB OK");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("Éϱ¨Ê§°Ü");
			HAL_Delay(1000);
			printf("AT+HMPUB=1,\"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report\",117,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sensor\\\",\\\"properties\\\":{\\\"temperature\\\":23,\\\"humidity\\\":58,\\\"formaldehyde\\\":0.011,\\\"oxygen\\\":21}}]}\"\r\n");
			HAL_Delay(1000);
			strx=strstr((const char*)RxBuffer,(const char*)"+HMPUB OK");
		}
		Clear_Buffer();
		printf("Éϱ¨³É¹¦");
		HAL_Delay(1000);
		
		Clear_Buffer();
			
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
			SensorData Data;
			memcpy(&Data,(SensorData*)&Databuff2,16);
//			printf("anion : %ld,\r\n",Data.oxygen);
//			printf("ch2o : %0.2f\r\n",Data.formaldehyde);
//			printf("temp : %0.2f\r\n",Data.temperature);
//			printf("hum : %0.2f\r\n",Data.humidity);
		ReportToCloud(Data);
		HAL_Delay(3000);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

void ReportToCloud(SensorData data) {
  // Construct JSON data
	int temp = round_float(data.temperature);
	int humi = round_float(data.humidity);
  char json_payload[200];
  snprintf(json_payload, sizeof(json_payload),
           "{\\\"services\\\":[{\\\"service_id\\\":\\\"Sensor\\\",\\\"properties\\\":{\\\"temperature\\\":%d,\\\"humidity\\\":%d,\\\"formaldehyde\\\":%.3f,\\\"oxygen\\\":%d}}]}",
           temp, humi, data.formaldehyde, data.oxygen);
  
  // Calculate JSON length
  int payload_len = strlen(json_payload) - 48;	// strlen(json_payload) - len(\\\)
  
  // Construct AT instruction
  char at_command[300];
  snprintf(at_command, sizeof(at_command),
           "AT+HMPUB=1,\"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report\",%d,\"%s\"\r\n",
           payload_len, json_payload);
  
  // Send AT command
  printf("%s", at_command);
  HAL_Delay(1000);  // Waiting for the instruction to be sent to complete
  
  // Check response
	strx=strstr((const char*)RxBuffer,(const char*)"+HMPUB OK");
	while(strx==NULL)
	{
		Clear_Buffer();	
		printf("Éϱ¨Ê§°Ü");
		HAL_Delay(1000);
		printf("%s", at_command);
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+HMPUB OK");
	}
	Clear_Buffer();
	printf("Éϱ¨³É¹¦");
	HAL_Delay(1000);
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE4) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_RCC_PWR_CLK_DISABLE();
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

问题:

  1. 确定传感器数据的发送格式(长度?16进制?4位表示一个信息?)
  2. 发送浮点型信息到华为云是否可以?struct SensorData

STM32U575与ADP-L610-Arduino连接华为云工程

这个代码是使用STM32CubeMX生成的基于STM32U5系列微控制器的主程序,通过LPUART1与广和通L610模块通信,实现物联网设备(加湿器)连接到华为云IoT平台的完整功能。以下是详细解析:


一、程序整体结构
graph TD
A[头文件 & 全局声明] --> B[用户自定义函数]
B --> C[main主程序]
C --> D[时钟配置]
C --> E[外设初始化]
C --> F[业务逻辑]
F --> G[AT指令交互]
G --> H[云端通信]

image-20250630154231018


二、核心功能详解
1. 头文件和全局声明
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>
#include <string.h>

char RxBuffer[1024]; // 串口接收缓冲区
uint8_t Res; // 单字节接收变量
int len, number;
char *id; // 用于解析云端指令ID
  • RxBuffer[1024]:1KB接收缓冲区,存储L610模块返回的AT指令响应
  • Res:中断接收时使用的单字节缓存
2. 关键自定义函数

串口接收回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if(huart->Instance == LPUART1) { 
        RxBuffer[Rxcouter++] = Res; // 字符存入缓冲区
        HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&Res, 1); // 重新启用接收中断
    }
}
  • 工作模式:每收到1字节触发中断,累积存放到RxBuffer
  • Rxcouter:缓冲区指针(代码中未显式声明,可能是截图遗漏)

缓冲区清理函数

void Clear_Buffer() {
    for(uint8_t i=0; i<Rxcouter; i++)
        RxBuffer[i] = 0;
    Rxcouter = 0;
}
3. main主程序流程

初始化阶段

HAL_Init(); // 初始化HAL库
HAL_PWREx_EnableVddIO2(); // 使能VDDIO2电源域(STM32U5特有)
SystemClock_Config(); // 配置160MHz系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_LPUART1_UART_Init(); // 初始化LPUART1
HAL_UART_Receive_IT(&hlpuart1, &Res, 1); // 启动串口接收中断

设备初始化流程(AT指令序列)

// 模块初始化
printf("模块初始化\r\n");
HAL_Delay(5000);

// 检查模块版本
printf("ATI\r\n"); // 查询模块信息
while(strstr(RxBuffer, "Fibocom") == NULL); // 检测Fibocom响应

// SIM卡检测
printf("AT+CPIN?\r\n"); // SIM卡状态查询
while(strstr(RxBuffer, "READY") == NULL);

// 信号强度检查
printf("AT+CSQ\r\n"); // 信号质量查询
while(strstr(RxBuffer, "+CSQ:") == NULL);

// 网络注册状态
printf("AT+CGREG?\r\n"); // 网络注册状态
while(strstr(RxBuffer, "0,1") == NULL); // 0,1表示已注册到归属网络

// 获取IP地址
printf("AT+MIPCALL=1\r\n"); // 激活移动IP连接
while(strstr(RxBuffer, "+MIPCALL:") == NULL);
4. 华为云IoT接入

MQTT连接建立

printf("AT+HMCON=0,60,\"a16110f598.iot-mqtts.cn-north-4.myhuaweicloud.com\",\"8883\",\"6110e20e0ad1ed0286438504_Humidifier001\",\"123456789\",0\r\n");
while(strstr(RxBuffer, "+HMCON OK") == NULL);
  • 连接到华为云MQTT服务器(端口8883)
  • 设备ID:6110e20e0ad1ed0286438504_Humidifier001
  • 密码:123456789

设备属性上报

printf("AT+HMPUB=1,\"$oc/devices/6110e20e0ad1ed0286438504_Humidifier001/sys/properties/report\",76,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sprayswitchcontrol\\\",\\\"properties\\\":{\\\"Switch\\\":1}}]}\"\r\n");
while(strstr(RxBuffer, "+HMPUB OK") == NULL);
  • 属性路径:$oc/devices/[设备ID]/sys/properties/report
  • 上报内容:开关状态("Switch":1
5. 主循环处理
while(1) {
    if(RxBuffer[0] != 0) { // 检测接收数据
        // 解析云端指令
        char* cmd = strstr(RxBuffer, "SpraySwitchParam");
        char* reqId = strstr(RxBuffer, "request_id=");
        
        if(cmd) {
            number = cmd[18]; // 提取指令参数值(开关状态)
            
            // 解析请求ID (格式:request_id="xxx")
            id = strtok(reqId, "\"");
            id = strtok(NULL, "\"");
            
            // 构造响应消息
            printf("AT+HMPUB=1,\"$oc/devices/6110e20e0ad1ed0286438504_Humidifier001/sys/commands/response/request_id=%s\",102,...", id);
        }
        Clear_Buffer();
    }
    HAL_Delay(500);
}
  • 命令响应机制:收到云平台下发的控制指令后,提取request_id构造响应路径
  • 指令格式SpraySwitchParam=值 控制加湿器开关

三、华为云IoT平台交互协议
1. MQTT主题格式
主题类型 路径模板 用途
属性上报 $oc/devices/{device_id}/sys/properties/report 设备主动上报状态
命令响应 $oc/devices/{device_id}/sys/commands/response/{request_id} 响应平台指令
命令下发 $oc/devices/{device_id}/sys/commands/request/{request_id} 接收平台指令
2. 数据格式示例
// 属性上报
{
  "services": [{
    "service_id": "Sprayswitchcontrol",
    "properties": {"Switch": 1}
  }]
}

// 指令响应
{
  "paras": {"SpraySwitchParam":1},
  "service_id":"Sprayswitchcontrol",
  "command_name":"SpraySwitchCommand"
}

四、潜在问题与改进建议
  1. 安全性问题

    • MQTT密码明文存储(123456789
    • 应采用动态令牌或证书认证
  2. 缓冲区隐患

    RxBuffer[Rxcouter++] = Res; // 未检查Rxcouter是否<1024
    

    改进:添加溢出检查

    if(Rxcouter < sizeof(RxBuffer)-1)
        RxBuffer[Rxcouter++] = Res;
    
  3. 阻塞式延迟

    HAL_Delay(1000); // 阻塞CPU
    

    改进:改用状态机+超时检测

  4. AT指令解析
    使用strstr搜索特定字符串,建议改用环形缓冲区和状态机解析响应

  5. 电源管理缺失
    未利用STM32U5的超低功耗特性,可添加睡眠模式


五、关键硬件依赖
  1. STM32U575ZI-Q特性:
    • LPUART1:用于连接L610模块
    • VDDIO2:专用于GPIO的独立电源域
    • 低功耗模式:未在本代码中启用
  2. 广和通L610
    • 支持LTE Cat.1网络
    • AT指令集兼容华为云IoT对接要求
    • 工作电流:峰值~500mA

总结

这段代码实现了:

  1. STM32U5通过LPUART驱动L610模块
  2. 完整的华为云IoT设备接入流程
  3. 设备状态上报与控制命令响应
  4. 基于阻塞式轮询的简单通信框架

需要重点优化

  • AT指令交互机制
  • 安全认证方案
  • 低功耗设计
  • 错误处理机制
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>

//¶¨ÒåÊý¾ÝÀàÐÍ
char *strx,*extstrx,*Readystrx;
char *strx1,*extstrx,*Readystrx;
char RxBuffer[1024],Rxcouter;
char *strstr(const char *, const char *);
uint8_t Res;
int len;
int number;
char *id;

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void Clear_Buffer(void)
{
	uint8_t i;

	for(i=0;i<Rxcouter;i++)
	RxBuffer[i]=0;
	Rxcouter=0;
}
	
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 
{ 
	UNUSED(huart); 

	if(huart->Instance ==LPUART1)
	{ 
		RxBuffer[Rxcouter++] = Res; 
		HAL_UART_Receive_IT(&hlpuart1, (uint8_t *)&Res, 1); 
	} 
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  HAL_PWREx_EnableVddIO2();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_LPUART1_UART_Init();
  /* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&hlpuart1,&Res,1);

		//Ä£¿é³õʼ»¯£¬µÈ´ý5Ãë
		printf("Ä£¿é³õʼ»¯\r\n");
		HAL_Delay(5000);
	
		//²éѯ°æ±¾ÐÅÏ¢
		printf("ATI\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"Fibocom");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("²éѯÐÅϢʧ°Ü");
			HAL_Delay(1000);
			printf("ATI\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"Fibocom");
		}
		Clear_Buffer();
		printf("°æ±¾ÐÅÏ¢ÕýÈ·");
		HAL_Delay(1000);
		
		//²éѯSIM¿¨
		printf("AT+CPIN?\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"READY");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("SIM¿¨²éѯʧ°Ü");
			HAL_Delay(1000);
			printf("AT+CPIN?\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"READY");
		}
		Clear_Buffer();
		printf("SIM¿¨ÒÑ×¼±¸ºÃ");
		HAL_Delay(1000);
	
		//²éѯÐźÅ
		printf("AT+CSQ\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+CSQ:");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("ÐźŲéѯʧ°Ü");
			HAL_Delay(1000);
			printf("AT+CSQ\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"+CSQ:");
		}
		Clear_Buffer();
		printf("ÐźÅÕý³£");
		HAL_Delay(1000);
		
		//²éÑ¯×¢ÍøÇé¿ö
		printf("AT+CGREG?\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"0,1");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("×¢Íø²»¿ÉÓÃ");
			HAL_Delay(1000);
			printf("AT+CGREG?\r\n");
		  HAL_Delay(1000);		
			strx=strstr((const char*)RxBuffer,(const char*)"0,1");
		}
		Clear_Buffer();
		printf("×¢Íø¿ÉÓÃ");
		HAL_Delay(1000);
		
		//ÇëÇóIP
		printf("AT+MIPCALL?\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+MIPCALL: 1");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("»¹Î´»ñÈ¡µ½IP");
			HAL_Delay(1000);
			printf("AT+MIPCALL=1\r\n");
			HAL_Delay(1000);
			strx=strstr((const char*)RxBuffer,(const char*)"+MIPCALL: ");
		}
		Clear_Buffer();
		printf("»ñÈ¡IP³É¹¦");
		HAL_Delay(1000);
		
		//Á¬½Ó»ªÎªÔÆ
		//printf("AT+HMCON=0,60,\"a16110f598.iot-mqtts.cn-north-4.myhuaweicloud.com\",\"8883\",\"6110e20e0ad1ed0286438504_Humidifier001\",\"123456789\",0\r\n");
		printf("AT+HMCON=0,60,\"337c64ac60.st1.iotda-device.cn-north-4.myhuaweicloud.com\",\"1883\",\"6857bbd732771f177b44a649_tempSensor\",\"12345678\",0\r\n");
		HAL_Delay(2000);
		strx=strstr((const char*)RxBuffer,(const char*)"+HMCON OK");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("Á¬½Óʧ°Ü");
			HAL_Delay(1000);
			printf("AT+HMCON=0,60,\"a16110f598.iot-mqtts.cn-north-4.myhuaweicloud.com\",\"8883\",\"6110e20e0ad1ed0286438504_Humidifier001\",\"123456789\",0\r\n");
			HAL_Delay(1000);
			strx=strstr((const char*)RxBuffer,(const char*)"+HMCON OK");
		}
		Clear_Buffer();
		printf("Á¬½Ó³É¹¦");
		HAL_Delay(1000);
			
		
		//Éϱ¨ÊôÐÔ
		//printf("AT+HMPUB=1,\"$oc/devices/6110e20e0ad1ed0286438504_Humidifier001/sys/properties/report\",76,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sprayswitchcontrol\\\",\\\"properties\\\":{\\\"Switch\\\":1}}]}\"\r\n");
		printf("AT+HMPUB=1,\"$oc/devices/6857bbd732771f177b44a649_tempSensor/sys/properties/report\",117,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sensor\\\",\\\"properties\\\":{\\\"temperature\\\":23,\\\"humidity\\\":58,\\\"formaldehyde\\\":0.011,\\\"oxygen\\\":21}}]}\"\r\n");
		HAL_Delay(1000);
		strx=strstr((const char*)RxBuffer,(const char*)"+HMPUB OK");
		while(strx==NULL)
		{
			Clear_Buffer();	
			printf("Éϱ¨Ê§°Ü");
			HAL_Delay(1000);
			printf("AT+HMPUB=1,\"$oc/devices/6110e20e0ad1ed0286438504_Humidifier001/sys/properties/report\",76,\"{\\\"services\\\":[{\\\"service_id\\\":\\\"Sprayswitchcontrol\\\",\\\"properties\\\":{\\\"Switch\\\":1}}]}\"\r\n");
			HAL_Delay(1000);
			strx=strstr((const char*)RxBuffer,(const char*)"+HMPUB OK");
		}
		Clear_Buffer();
		printf("Éϱ¨³É¹¦");
		HAL_Delay(1000);
		
		Clear_Buffer();
			
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
		HAL_Delay(500);
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE4) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
  RCC_OscInitStruct.MSIState = RCC_MSI_ON;
  RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB3CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_RCC_PWR_CLK_DISABLE();
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

作品上传

作品名称:

基于物联网的嵌入式多模态环境感知与动态消杀净化系统

作品简介:

针对传统环境消杀与监测方式实时性弱、功能单一、交互不便的不足,本作品“基于物联网的嵌入式多模态环境感知与动态消杀净化系统”以STM32U5超低功耗主控芯片为核心,集成多模态传感器(温湿度、负氧离子、甲醛、TVOC、PM2.5等),实现对室内环境健康的全方位实时感知。
系统具备智能动态消杀净化能力(包含加湿功能、增加负氧离子功能、立体消毒功能等),其中立体消毒功能经验证有效杀菌率高达99.9%。用户可通过直观的本地控制屏(清晰展示各项环境指标与消杀进度)或便捷的语音控制,灵活切换“康养”、“养肤”、“母婴”等多种定制化场景模式,满足不同环境健康需求。
所有关键环境数据(温湿度、负氧离子含量、甲醛浓度等)均通过华为云IoTDA平台稳定传输,并实时同步至配套微信小程序,实现环境状态的远程查看,为用户打造可知、可控的健康舒适环境。

作品关键词
环境感知、空气净化、物联网监控

image-20250705211305789

技术文档

加拍传感器照片(七合一传感器和负氧离子浓度传感器)

流程图:
触摸屏模块、执行器模块、语音处理和信息上传模块

1. 硬件分层架构设计

  • 三级供电架构:

    电压等级 供电模块
    24V 雾化器(高压隔离驱动)
    12V 4G模块、显示屏、风机
    5V STM32U575主控、语音模块
  • 双核协同控制:

    • 主控层:STM32U575通过UART/SPI/I2C连接传感器、4G模块及显示屏
    • 驱动层:iLTE9866协处理器→MB9F功率芯片→雾化器(三级安全隔离)
  • 抗干扰设计:传感器独立线性供电,4层PCB叠层+磁珠滤波抑制噪声(信号误差≤±0.5%FS)。

2. 软件系统开发

边缘层(STM32U575) → 传输层(4G MQTT) → 云端(Huawei IoTDA) → 应用层(微信小程序)  
  • 关键实现:
    • 动态阈值算法实时校准传感器数据
    • 中断恢复逻辑(断电≤5分钟续消)
    • 语音指令响应链:MIC→语音模块→串口→STM32U575(响应<0.5s)

软件分层架构

    flowchart TD
    subgraph 设备端
        A[STM32U575微控制器] -->|串口通信+AT指令| B[广和通ADP-L610模组]
        C[本地控制屏UI] -->|SPI交互| A
        
    end

    subgraph 云平台
        B -->|MQTT| D[华为云IoTDA]
    end

    subgraph 服务层
        D -->|HTTPS Webhook| E[Node.js后端]
    end

    subgraph 用户端
        E -->|WebSocket| F[微信小程序]
        G[用户语音交互] -->|操作指令| A
    end

    style 设备端 fill:#F0F9FF,stroke:#1E88E5,stroke-width:2px
    style 云平台 fill:#E8F5E9,stroke:#388E3C,stroke-width:2px
    style 服务层 fill:#FFF3E0,stroke:#EF6C00,stroke-width:2px
    style 用户端 fill:#FFEBEE,stroke:#D32F2F,stroke-width:2px
    
    classDef hardware fill:#BBDEFB,stroke:#1976D2,color:#0D47A1,stroke-width:1.5px;
    classDef cloud fill:#C8E6C9,stroke:#2E7D32,color:#1B5E20,stroke-width:1.5px;
    classDef server fill:#FFE0B2,stroke:#E65100,color:#BF360C,stroke-width:1.5px;
    classDef user fill:#FFCDD2,stroke:#C62828,color:#B71C1C,stroke-width:1.5px;
    
    class A,B,C hardware;
    class D cloud;
    class E server;
    class F,G user;

    linkStyle 0 stroke:#1565C0,stroke-width:1.5px;
    linkStyle 1 stroke:#1565C0,stroke-width:1.5px;
    linkStyle 2 stroke:#388E3C,stroke-width:1.5px;
    linkStyle 3 stroke:#EF6C00,stroke-width:1.5px;
    linkStyle 4 stroke:#E65100,stroke-width:1.5px;

感谢您的确认!关于您希望开发的连接华为云物联网设备的微信小程序,我先帮您进一步完善和扩展需求,确保功能完整且体验流畅。

基于您提供的实时数据展示和历史数据折线图需求,我补充了登录注册功能保障数据安全,设计了首页集成实时数据和甲醛超标提醒,历史数据页面支持多指标时间范围选择和图表交互,个人信息页支持资料修改和退出登录。底部导航栏设计包含首页、历史数据和个人信息,方便用户快速切换。考虑到微信小程序的使用场景,所有新增和编辑操作均采用弹窗表单,避免页面跳转过多,提升用户体验。

接下来,我将输出完整且结构规范的需求文档,方便您确认和后续开发。

需求文档

1. 页面风格样式

  • 整体风格简洁现代,突出科技感,适合物联网设备监测场景。
  • 主要色调采用蓝色和白色,搭配绿色和红色作为状态和提醒色。
  • 实时数据和历史数据均用卡片式布局,卡片带圆角和阴影,层次分明。
  • 图表采用清晰的折线图,颜色区分不同指标,支持触摸滑动查看数据点。
  • 按钮和交互元素尺寸适合手机操作,保证点击准确。
  • 底部固定导航栏,包含首页、历史数据、个人信息,方便页面切换。
  • 甲醛超标提醒以红色显著展示,确保用户第一时间注意。

2. 核心业务流程

2.1 登录与注册

  • 用户打开小程序,默认进入登录页面。
  • 输入账号和密码进行登录,登录成功进入首页,失败显示错误提示。
  • 新用户点击“注册”,填写用户名、密码、邮箱等信息注册,注册成功后跳转回登录页面。

2.2 首页(实时数据展示与提醒)

  • 登录后默认进入首页。
  • 首页顶部显示当前设备的温湿度、空气质量和甲醛含量实时数据,数据自动刷新(约30秒一次)。
  • 当甲醛含量超过安全阈值时,首页顶部显著显示红色提醒信息。
  • 首页中间以卡片形式展示各项实时指标数据,清晰易读。
  • 首页底部导航栏包含:首页(当前页)、历史数据、个人信息。
  • 首页提供快捷按钮,用户可快速跳转至历史数据页面。

2.3 历史数据页面

  • 展示温湿度、空气质量和甲醛含量的历史数据折线图。
  • 页面顶部提供时间范围选择(近24小时、7天、30天)和指标切换标签。
  • 支持图表缩放和滑动查看具体数据点。
  • 支持下拉刷新加载最新历史数据。
  • 页面布局简洁,突出数据趋势和对比。

2.4 个人信息页面

  • 用户查看和修改个人资料(用户名、邮箱、密码)。
  • 支持退出登录功能,退出后返回登录页面。

页面切换与导航

  • 底部导航栏固定显示:首页、历史数据、个人信息,方便用户快速切换。
  • 登录和注册页面不包含在底部导航栏中,仅首次访问或退出登录时显示。
  • 首页快捷按钮可直接跳转至历史数据页面。

3. 页面之间的关系

  • 用户首次打开小程序,进入登录页面。
  • 登录成功后进入首页。
  • 首页展示实时数据和甲醛超标提醒,提供跳转历史数据页面的入口。
  • 底部导航栏允许用户随时切换首页、历史数据、个人信息三个主要页面。
  • 历史数据页面支持查看不同指标和时间范围的折线图。
  • 个人信息页面支持资料查看修改和退出登录,退出后返回登录页面。
posted @ 2025-11-26 10:09  挽安Wanan  阅读(3)  评论(0)    收藏  举报