一、前言
1.1 项目开发背景
随着智能家居和物联网技术的快速发展,电力管理系统逐渐成为现代家庭和商业建筑中不可或缺的一部分。传统的电表主要提供基本的用电量计量功能,但无法满足用户对电力管理日益增长的需求。在这一背景下,智能电度表应运而生。通过结合先进的传感器技术、数据采集与处理技术以及无线通信技术,智能电度表能够实时监测、精确测量电力使用情况,并通过智能化手段提供便捷的电能管理方案。
本项目基于STM32微控制器设计的智能电度表,实现对电力使用情况的精确监控、用电数据的智能管理以及远程控制。通过采用电压和电流互感器对电网进行实时监测,智能电度表能够实时计算功率、电量等参数,确保用户能够随时掌握用电情况。与传统的电表相比,智能电度表不仅能够提供实时的用电数据,还支持远程管理和控制,满足家庭和商业用户对电能管理的多样化需求。
此外,随着电力资源的紧张和用电成本的逐渐上升,科学合理的电能管理显得尤为重要。智能电度表通过提供预付费电量管理功能,帮助用户在用电过程中按需付费,避免因电量耗尽导致的中断问题。用户通过微信小程序可以实时查询电量余额、进行充值操作,从而确保用电不受影响。同时,系统还具备功率过载保护功能,在用电超负荷的情况下自动切断电源,有效防止电气设备因过载而受到损坏。
智能电度表的设计还考虑了数据可视化的需求,系统能够通过LCD显示屏实时展示电力使用数据,便于用户直观地了解设备工作状态。为了提升用户体验,电度表通过WiFi模块将数据上传至云平台,支持远程查看和控制。用户可以通过微信小程序随时查看用电数据、余额信息,并进行设备开关的远程操作。此外,系统还提供用电记录和历史数据分析功能,帮助用户优化用电习惯,提高能源使用效率。
随着物联网技术的进一步发展,智能电度表不再是单纯的电量计量工具,它已经成为智能家居系统中的一个重要组成部分。通过无线通信和云平台的支持,智能电度表不仅能够为用户提供便捷的电力管理工具,还能够为电力公司和物业管理方提供精准的用电数据和实时监控服务。通过这种智能化的电力管理方式,不仅能够提升用户的生活品质,还能够有效节约能源,推动绿色低碳的可持续发展。
因此,基于STM32设计的智能电度表,不仅是对传统电表功能的扩展和创新,也是对现代电能管理需求的积极响应。它通过精确的监测、灵活的控制和智能化的管理,为用户提供了一种高效、便捷的用电解决方案,推动了电力管理技术向智能化、网络化方向的发展。
1.2 设计实现的功能
(1)电能监测与数据采集
系统通过电压互感器(TV1005M)和电流互感器(TA1005M)精确测量电网的电压和电流,实时计算功率和电量。系统持续监控电力使用情况,并将数据通过WiFi模块实时传输至微信小程序,便于用户随时查看电力消耗情况,帮助用户合理管理电能使用,避免浪费。
(2)预付费功能
为满足用户对电能管理的需求,系统设计了预付费电量管理功能。用户可通过微信小程序进行余额查询、充值等操作,确保用电过程中的按需付费。系统通过WiFi模块与微信小程序进行实时数据同步,用户能够随时查看剩余电量,避免因电量耗尽导致的用电中断。
(3)功率过载保护
系统具备过载保护功能,当监测到功率超过设定阈值(如200W)时,系统会自动断开继电器,切断电源,防止过载引发设备损坏或安全隐患。在功率未超过阈值的情况下,用户可通过微信小程序手动控制继电器开关,以适应不同的用电需求。
(4)计时与用电记录
系统能够实时显示设备的使用时长,帮助用户了解电器设备的工作状态。与此同时,系统还具备用电记录功能,用户可以查看历史用电数据,便于进行能效分析与用电习惯的优化调整。
(5)微信小程序与云平台远程控制
通过WiFi模块与云平台实现远程控制功能,用户可通过微信小程序随时查看电度表数据、控制设备开关,查看电量消耗和余额信息。同时,系统支持与云平台的数据同步,通过云平台为用户提供远程监控、统计分析及智能管理服务,提升用电管理的智能化和便捷性。
(6)数据上传至云平台
系统支持通过WiFi模块与MQTT协议将采集的数据上传至OneNet物联网云平台。用户可以通过微信小程序远程查看设备数据,确保用电管理更加透明和智能。数据上云不仅方便了远程操作,还为未来的数据分析与优化提供了基础。
(7)本地LCD显示屏显示测量数据
智能电度表配备1.44寸LCD显示屏,用于本地显示电压、电流、功率、电量、剩余电量等实时测量数据。用户可以直接通过LCD屏幕查看设备当前的用电状态,无需依赖其他设备。
(8)设备安全性与蜂鸣器报警功能
系统设计了安全报警机制,采用有源蜂鸣器,当系统检测到功率过载或其他异常情况时,会触发蜂鸣器报警,提醒用户及时采取措施,防止发生安全隐患。
1.3 项目硬件模块组成
(1)STM32主控芯片
项目采用STM32F103RCT6作为主控芯片。该芯片具有较高的处理能力、丰富的外设接口和较低的功耗,能够满足智能电度表对实时数据采集、处理和通信的需求。STM32主控芯片负责处理传感器数据、控制继电器和蜂鸣器、管理与云平台和微信小程序的通信等功能。
(2)电压与电流互感器(TV1005M 和 TA1005M)
电压互感器(TV1005M)和电流互感器(TA1005M)用于精确测量电网的电压和电流,并实时传输测量数据给STM32主控芯片。电流互感器主要用于采集用电设备的电流数据,电压互感器则用于监测电网的电压状态,确保电力使用的安全和稳定。
(3)电力参数采集模块
电力参数采集模块通过串口接口与STM32主控芯片进行数据交互,采集电压、电流、功率、电量等电力参数,并将实时数据传送给主控芯片。该模块为智能电度表提供精确的电力监测数据支持。
(4)LCD显示屏
系统采用1.44寸LCD显示屏,用于实时显示电度表的各项数据,如电压、电流、功率、电量、剩余电量等信息。LCD屏幕提供本地数据展示,便于用户直观地查看当前用电状态。
(5)WiFi模块(如ESP8266/ESP32)
WiFi模块负责智能电度表与外部设备(如微信小程序和云平台)之间的数据传输。通过WiFi连接,系统能够将实时采集的数据上传至OneNet物联网云平台,并与微信小程序进行数据同步,实现远程监控和控制。
(6)MQTT通信模块
通过MQTT协议,WiFi模块将采集的数据上传至OneNet物联网云平台。MQTT是轻量级的消息传输协议,适用于物联网环境,能有效保证设备与云平台之间的低延迟、高可靠的通信。
(7)继电器模块
继电器用于控制电源的开关,当电力使用超过设定的安全阈值(如200W)时,继电器会自动切断电源,防止过载引发设备损坏或安全隐患。此外,继电器还可以由用户通过微信小程序远程手动控制,满足不同场景的用电需求。
(8)蜂鸣器模块
蜂鸣器用于系统报警功能。当系统检测到电力使用异常(如功率过载、低电量等)时,蜂鸣器会发出警报声音,提醒用户及时检查并处理异常情况,确保系统的安全运行。
(9)电源管理模块
为了保证系统的稳定运行,电源管理模块负责为STM32主控芯片、WiFi模块、LCD显示屏等各个模块提供稳定的电源。该模块还负责电池电量监控,确保系统能够在电量不足时进行预警和自动断电操作。
(10)按键/开关模块
按键或开关模块用于用户与设备的交互,例如手动控制继电器的开关、启动和停止系统、设置阈值等操作。通过按键模块,用户能够直接在本地对电度表进行操作,方便使用。
(11)外部传感器与接口模块
除了电流和电压传感器,系统还可能集成其他传感器模块(如温度传感器、功率因数传感器等),用于进一步增强电度表的功能。接口模块提供与外部传感器的连接,确保数据采集的多样化和精准性。
(12)外部存储模块(可选)
为方便存储和备份数据,系统可以选择集成外部存储模块(如SD卡)。该模块可以用于存储历史用电数据、用户设置参数等信息,确保在没有网络连接的情况下仍能持续运行并记录数据。
1.4 设计思路
在设计基于STM32的智能电度表时,我们的主要目标是实现高效、精准的电力监测与管理,同时提供便捷的用户交互和远程控制功能。整个设计的核心思路是结合实时数据采集、智能化管理、远程控制和用户体验优化等方面,提供一个全面的电能管理解决方案。
在硬件设计方面,系统选择STM32F103RCT6作为主控芯片,具备较高的性能和丰富的接口资源,能够支持电压、电流等多项电力参数的实时采集、处理和数据传输。为了保证电力参数的准确性,系统采用电压互感器(TV1005M)和电流互感器(TA1005M)对电网的电压和电流进行精确测量,并通过电力参数采集模块将这些数据传输给STM32主控芯片。主控芯片对采集的数据进行实时处理,计算功率和电量等指标,并通过WiFi模块将数据上传至云平台和微信小程序,实现远程监控和控制。
为了提升用户体验,系统集成了1.44寸LCD显示屏,本地展示电度表的实时数据,包括电压、电流、功率和电量等参数。用户可以通过LCD屏直观地查看设备的工作状态,了解当前的用电情况,避免对电力使用情况的不清楚。与此同时,系统支持通过微信小程序进行远程操作,用户能够随时查看电力消耗、余额信息,并进行继电器的远程开关控制。这一设计使得智能电度表不仅具备本地数据展示功能,还能为用户提供更加便捷的远程管理手段。
此外,系统设计了预付费电量管理功能。通过与微信小程序的数据同步,用户可以随时查看剩余电量并进行充值,确保电力使用不受干扰。为避免因电力过载导致的设备损坏或安全问题,系统还配备了功率过载保护功能。当电力负荷超过设定阈值时,系统会自动切断电源,防止过载情况发生,从而提高系统的安全性。
在通信部分,系统使用WiFi模块与OneNet物联网云平台进行数据上传和同步。通过MQTT协议,电度表能够将采集到的数据实时上传至云平台,并支持与微信小程序进行数据同步,使得用户能够随时远程监控和控制设备。云平台还为用户提供数据分析和统计功能,帮助用户更好地了解自己的用电习惯,优化电能使用。
另外,为了进一步提升电度表的安全性和可靠性,系统集成了蜂鸣器模块,用于在电力使用异常(如功率过载、电量低等)时发出警报。蜂鸣器作为一个重要的安全提醒模块,能够及时通知用户采取相应的措施,避免由于忽视警告导致的潜在安全问题。
在总体设计上,智能电度表以用户需求为中心,通过无线通信技术和智能化管理手段,整合了实时监测、远程控制、安全保护和数据分析等功能,提升了电力管理的智能化水平。系统的设计不仅提高了电能使用的便捷性和可靠性,也通过数据的可视化和云平台支持,帮助用户更好地了解和优化自己的用电行为,从而达到节能降耗、提高能源利用效率的目的。
1.5 系统功能总结
功能模块 | 功能描述 |
电能监测与数据采集 | 精确测量电网电压和电流,实时计算功率和电量,并将数据上传至云平台和微信小程序进行查看。 |
预付费功能 | 提供余额查询和充值功能,确保用户按需付费,避免因电量耗尽导致用电中断。 |
功率过载保护 | 当功率超过设定阈值时,自动切断电源,防止过载造成设备损坏或安全隐患。 |
计时与用电记录 | 实时显示设备使用时长,记录历史用电数据,帮助用户分析用电行为。 |
微信小程序与云平台远程控制 | 通过WiFi模块与云平台进行数据同步,支持用户远程查看电度表数据和控制设备开关。 |
数据上传至云平台 | 通过WiFi模块和MQTT协议将实时数据上传至OneNet云平台,实现远程监控与数据存储。 |
本地LCD显示屏显示测量数据 | LCD显示屏实时显示电压、电流、功率和电量等电力数据,方便用户本地查看。 |
设备安全性与蜂鸣器报警 | 在检测到功率过载或电量低等异常情况时,触发蜂鸣器报警提醒用户,确保系统安全运行。 |
1.7 模块的技术详情介绍
1. STM32主控芯片(STM32F103RCT6)
STM32F103RCT6是本项目的核心控制单元,采用ARM Cortex-M3架构,具有较高的处理性能、低功耗、丰富的外设接口(如串口、I2C、SPI、GPIO等),非常适合用于物联网设备的嵌入式控制。STM32F103RCT6负责处理电流和电压传感器的数据,进行功率计算、控制继电器、管理与WiFi模块及云平台的数据通信等任务。该芯片的高效能和低功耗特性使其成为智能电度表设计中的理想选择。
2. 电压与电流互感器(TV1005M 和 TA1005M)
• 电压互感器(TV1005M):用于精确测量电网电压。电压互感器的输出信号通过采样转换为数字信号,传送给STM32主控芯片进行处理。该互感器通常具有较高的精度和稳定性,能够确保电网电压的准确检测。
• 电流互感器(TA1005M):用于监测电流信号,尤其是在大功率电器中,能够实时反映电流的变化。电流互感器将电流信号转换为与负载成比例的电压信号,STM32通过ADC采样获取数据,进而计算用电功率。
3. 电力参数采集模块
该模块通过串口(如UART)与STM32主控芯片进行通信,采集电网的电压、电流、功率等数据。电力参数采集模块通过传感器实时测量电气参数,并通过串行接口将数据传输给STM32控制器。通过该模块,系统能够实时监测并计算电力使用的各项指标,包括功率、功率因数、功耗等,为用户提供全面的电能数据支持。
4. LCD显示屏(1.44寸)
该LCD屏幕用于本地显示实时测量的电力数据,包括电压、电流、功率、电量、剩余电量等信息。LCD显示屏采用SPI或并行接口与STM32进行通信,能够将计算结果即时显示在用户面前。1.44寸显示屏具有小巧、低功耗的特点,适合于小型设备的集成。用户可以通过LCD屏幕查看设备的实时运行状态,无需其他外部显示设备。
5. WiFi模块(如ESP8266/ESP32)
WiFi模块通过无线通信与云平台及微信小程序进行数据同步。模块支持802.11b/g/n无线标准,能够连接到本地Wi-Fi网络。通过WiFi,电度表能够将实时采集的电力数据上传至物联网云平台(如OneNet),并通过MQTT协议与云端进行数据交换。此外,WiFi模块还支持与微信小程序进行实时同步,方便用户查看电力使用情况、余额信息及进行设备远程控制。
6. MQTT协议
MQTT(消息队列遥测传输协议)是一种轻量级的、基于发布/订阅模式的消息传输协议,专为低带宽、高延迟或不稳定的网络环境设计。该协议非常适用于物联网设备的数据传输。在本项目中,MQTT协议被用于WiFi模块与OneNet云平台之间的数据通信,确保设备数据能够稳定、实时地上传到云端,并支持云平台与设备之间的双向数据交换。
7. 继电器模块
继电器模块负责控制电度表的电源开关。当系统检测到功率过载时,继电器会自动断开电路,切断电源以避免过载引起的设备损坏或火灾等安全隐患。此外,继电器还可以通过微信小程序或本地按键进行手动控制,满足用户不同的用电需求。继电器模块通过低电压信号控制高电压电路的开关,确保系统操作安全。
8. 蜂鸣器模块
蜂鸣器用于系统的报警功能。当电力负荷超过设定阈值,或者剩余电量低至警戒线时,蜂鸣器会发出警报声音,提醒用户采取必要的措施,防止电力过载或电量耗尽。蜂鸣器通常采用高电平触发的方式,通过STM32的GPIO口控制。该模块能够有效地提高系统的安全性,避免因忽视警告而导致的电力安全问题。
9. 电源管理模块
电源管理模块为整个智能电度表系统提供稳定的电源。该模块包括电池、稳压芯片等组件,用于转换并稳定电压,确保STM32主控芯片、WiFi模块、LCD显示屏等各个模块能够稳定工作。电源管理模块还负责电池电量监测,确保在电池电量低时提供充电提醒或自动进入节能模式。
10. 按键/开关模块
按键模块用于用户与设备的交互,用户可以通过按键控制继电器的开关、设置参数、启动或停止某些功能等。按键模块通过STM32的GPIO接口与主控芯片连接,负责接收用户输入的命令。通过这种方式,用户可以根据需要灵活操作电度表,调整设备设置。
二、微信小程序接入
必备参数
顶置说明: 应用端接入OneNet 需要下面几个参数。要自己找到位置。
# 用户ID(用户头像->访问权限->user_id)
user_id = ''
# 访问密钥(用户头像->访问权限->access_key)
access_key = ""
#产品id(产品->产品ID)
product_id = ""
#设备名称
device_name = ""
2.1 接入OneNet
帮助文档地址:https://open.iot.10086.cn/doc/v5/fuse/detail/1418
【1】查询设备数据点
使用这个接口的时候,要注意。你的设备是不是创建的数据流类型。创建产品的时候会让你选择的。是数据流还是OneJSON
接口功能
根据产品id和设备名称查询,时间范围参数,查询设备历史数据点信息
备注
仅支持数据流、IPSO数据协议,设备能正常下发上报
接口地址
http(s)://iot-api.heclouds.com/datapoint/history-datapoints
API说明
请求方式:GET
http query 请求参数:
参数 | 类型 | 是否必选 | 描述 |
datastream_id | string | 否 | 数据流ID,多个id之间用逗号分开,缺省时取数据流或数据流模板100条为缺省数据流id条件 |
product_id | string | 是 | 产品ID,平台生成唯一ID |
device_name | string | 是 | 设备名称 |
imei | string | 否 | lwm2m协议时传,设备imei |
start | string | 否 | 提取数据点的开始时间,精确到秒,示例:2015-01-10T08:00:35 |
end | string | 否 | 提取数据点的结束时间,精确到秒,示例:2015-01-10T08:00:35 |
duration | int | 否 | 查询时间区间,单位为秒 |
limit | int | 否 | 限定本次请求最多返回的数据点数,默认100,范围为(0,6000] |
cursor | string | 否 | 指定本次请求继续从cursor位置开始提取数据 |
sort | enum | 否 | 时间排序方式,DESC:倒序,ASC:升序,默认为DESC |
返回数据
参数名称 | 类型 | 描述 |
code | int | 调用成功或失败时,返回的code码 |
msg | string | 调用成功或失败时,返回的msg信息 |
request_id | string | 调用API生成的请求标识 |
data | - | 调用成功时,返回的业务数据 |
data.count | int | 本次返回的数据点数量 |
data.cursor | string | 本次请求若未能返回所有数据,则会返回cursor参数,用户可以携带cursor参数进行再次请求,获取剩下的数据 |
data.datastreams | array-json | 设备数据流信息的json数组,见datastreams描述 |
datastreams描述表
参数名称 | 格式 | 说明 |
id | string | 数据流名称 |
datapoints | array-json | 数据点信息的json数组,见datapoints描述表 |
datapoints描述表
参数名称 | 格式 | 说明 |
at | string | 数据记录时间 |
value | string/int/json... | 数据点的值 |
特别说明
1. 当start,end时间均为空
i) limit
不传值或传值等于1时:排序为升序则查询最早一个数据点(30天内),否则查询最新数据点(最新缓存数据)ii) limit
传值大于1时:排序为升序则查询最早x条数据点,否则查询最新x条数据点(30天内,x=limit)
示例
请求示例
GET http(s)://iot-api.heclouds.com/datapoint/history-datapoints
?product_id=XhONWQ5zV5&device_name=mqtts-dev&datastream_id=ds&start=2017-01-01T00:00:00&limit=100
响应示例
{
"data":{
"cursor":"25971_564280_1448961152173",
"count":5,
"datastreams":[
{
"datapoints":[
{
"at":"2015-12-01 17:10:24.981",
"value":"35"
},
{
"at":"2015-12-01 17:10:53.406",
"value":"38"
},
...
],
"id":"3200_0_5501"
},
...
]
},
"request_id": "a25087f46df04b69b29e90ef0acfd115",
"msg": "succ",
"code": 0
}
在Qt项目包含网络模块,在.pro
文件中添加:
QT += network
演示了如何使用Qt发出HTTP GET请求:
#include <QCoreApplication>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QNetworkAccessManager manager;
QUrl url("https://open.iot.10086.cn/api/your-endpoint"); // 替换为实际URL
QNetworkRequest request(url);
// 设置请求头
request.setRawHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN"); // 示例: 使用Bearer Token
QNetworkReply *reply = manager.get(request);
QObject::connect(reply, &QNetworkReply::finished, [&]() {
if (reply->error() == QNetworkReply::NoError) {
QString response_data = reply->readAll();
qDebug() << "Response data:" << response_data;
} else {
qDebug() << "Error:" << reply->errorString();
}
reply->deleteLater();
a.quit(); // 结束应用程序
});
return a.exec();
}
【2】MQTT命令下发
使用这个接口的时候,要注意。你的设备是不是创建的数据流类型。创建产品的时候会让你选择的。是数据流还是OneJSON
接口功能
根据产品id和设备名称查询,时间范围参数,查询设备历史数据点信息
备注
仅支持数据流、IPSO数据协议,设备能正常下发上报
接口地址
http(s)://iot-api.heclouds.com/datapoint/history-datapoints
API说明
请求方式:GET
http query 请求参数:
参数 | 类型 | 是否必选 | 描述 |
datastream_id | string | 否 | 数据流ID,多个id之间用逗号分开,缺省时取数据流或数据流模板100条为缺省数据流id条件 |
product_id | string | 是 | 产品ID,平台生成唯一ID |
device_name | string | 是 | 设备名称 |
imei | string | 否 | lwm2m协议时传,设备imei |
start | string | 否 | 提取数据点的开始时间,精确到秒,示例:2015-01-10T08:00:35 |
end | string | 否 | 提取数据点的结束时间,精确到秒,示例:2015-01-10T08:00:35 |
duration | int | 否 | 查询时间区间,单位为秒 |
limit | int | 否 | 限定本次请求最多返回的数据点数,默认100,范围为(0,6000] |
cursor | string | 否 | 指定本次请求继续从cursor位置开始提取数据 |
sort | enum | 否 | 时间排序方式,DESC:倒序,ASC:升序,默认为DESC |
返回数据
参数名称 | 类型 | 描述 |
code | int | 调用成功或失败时,返回的code码 |
msg | string | 调用成功或失败时,返回的msg信息 |
request_id | string | 调用API生成的请求标识 |
data | - | 调用成功时,返回的业务数据 |
data.count | int | 本次返回的数据点数量 |
data.cursor | string | 本次请求若未能返回所有数据,则会返回cursor参数,用户可以携带cursor参数进行再次请求,获取剩下的数据 |
data.datastreams | array-json | 设备数据流信息的json数组,见datastreams描述 |
datastreams描述表
参数名称 | 格式 | 说明 |
id | string | 数据流名称 |
datapoints | array-json | 数据点信息的json数组,见datapoints描述表 |
datapoints描述表
参数名称 | 格式 | 说明 |
at | string | 数据记录时间 |
value | string/int/json... | 数据点的值 |
特别说明
1. 当start,end时间均为空
i) limit
不传值或传值等于1时:排序为升序则查询最早一个数据点(30天内),否则查询最新数据点(最新缓存数据)ii) limit
传值大于1时:排序为升序则查询最早x条数据点,否则查询最新x条数据点(30天内,x=limit)
示例
请求示例
GET http(s)://iot-api.heclouds.com/datapoint/history-datapoints
?product_id=XhONWQ5zV5&device_name=mqtts-dev&datastream_id=ds&start=2017-01-01T00:00:00&limit=100
响应示例
{
"data":{
"cursor":"25971_564280_1448961152173",
"count":5,
"datastreams":[
{
"datapoints":[
{
"at":"2015-12-01 17:10:24.981",
"value":"35"
},
{
"at":"2015-12-01 17:10:53.406",
"value":"38"
},
...
],
"id":"3200_0_5501"
},
...
]
},
"request_id": "a25087f46df04b69b29e90ef0acfd115",
"msg": "succ",
"code": 0
}
【3】API鉴权
平台需要对API调用方进行资源权限校验,使用API时,需要在请求Header中携带统一的安全鉴权信息。
不同情形下的鉴权方式
• 平台支持三种鉴权方式:用户鉴权、产品鉴权、项目鉴权
• 添加设备后未转移:
1、设备未转移,但绑定了项目时,鉴权支持【用户鉴权】、【项目鉴权】。
2、设备未转移,且没有绑定任何项目时,鉴权支持【用户鉴权】、【产品鉴权】。
• 添加设备后进行设备转移:
1、用户在平台尝试“设备转移”时,需要先解除项目绑定,才能进行转移操作。
2、转移后,再次绑定项目时,支持【用户鉴权】、【项目鉴权】。
3、转移后,没有绑定项目时,仅支持【用户鉴权】。
安全鉴权机制
安全鉴权authorization由多个参数构成,每个参数均采用key = value的形式表示,并用&作为分隔符:
authorization: version=2022-05-01&res=userid%2F{userId}&et={expireTime}&method=sha1&sign={sign}
Token生成工具
• 为便于开发者开发,OneNET提供了Token生成工具,填写字段后可以快速生成安全鉴权信息。前往下载https://open.iot.10086.cn/doc/v5/fuse/detail/1487
参数说明
序 号 | 参数 | 类型 | 说明 | 示例 |
1 | version | string | 签名算法版本 | 目前仅支持 2022-05-01 |
2 | res | string | 访问资源信息 | 支持主用户、产品、项目三种方式:1)主用户访问res为:userid/{userid}, userid为平台用户id,access_key为主用户access_key,参数在个人「账号信息」、「访问权限」中查看。2)产品访问res为:products/{productid},access_key为产品access_key,进入「产品开发」菜单的「产品列表」中,点击「操作」列下的「产品开发」链接,进入「产品信息页面」查看。3)项目访问res为:projects/{projectid},access_key为项目key,进入「应用开发」-「项目管理」菜单的「我的项目」列表中,点击「操作」列下的「进入项目管理」链接,进入「项目信息页面」,在「项目概况」的「项目信息」中查看。注* 以上提到的不同access_key获取方式在下文中会有详细描述 |
3 | et | string | 访问过期时间 | 10位秒级时间戳,1537255523 表示:北京时间 2018-09-18 15:25:23 |
4 | method | string | 签名方法 | 目前支持md5、sha1、sha256 |
5 | sign | string | 签名结果字符串 | version、res、et、method参数计算生成 |
accessKey参数获取
主用户accessKey
产品accessKey
项目accessKey
sign的生成算法为:
sign = base64(hmac_<method>(base64decode(accessKey), utf-8(StringForSignature)))
• accessKey为平台分配的访问密钥(用户访问权限页面查看),如果访问资源以主用户形式访问, 使用主用户的accessKey;如果访问资源以产品方式访问,使用产品的accessKey,且只能对产品下的设备进行操作;如果访问资源以项目方式访问,使用项目的accessKey,且只能对项目下的设备进行操作。
• accessKey参与计算前应先进行base64decode操作。
• 用于计算签名的字符串 StringForSignature按照et、method、res、version的顺序,以"n"作为分隔符进行排列,如下所示:
StringForSignature = et + "n" + method + "n" + res + "n" + version
参数编码
authorization中key=value的形式的value部分需要经过URL编码,需要进行编码的特殊符号如下:
序号 | 符号 | 编码 |
1 | + | %2B |
2 | 空格 | %20 |
3 | / | %2F |
4 | ? | %3F |
5 | % | %25 |
6 | # | %23 |
7 | & | %26 |
8 | = | %3D |
authorization生成示例
nodejs代码示例
'use strict';
const crypto = require('crypto');
/**
* authorization生成函数
*
* @param {String} method - hash method
* @param {String} res - resource
* @param {String} accessKey - access key
* @param {Number} et - effective time
* @return {String} - authorization
*/
function generateAuthorization(method, res, accessKey, et) {
const version = '2022-05-01';
const et = Math.ceil((Date.now() + et) / 1000); // token有效时间
const base64Key = Buffer.from(accessKey, 'base64'); // accessKey base64编码
const StringForSignature = et + 'n' + method + 'n' + res + 'n' + version;
const sign = encodeURIComponent(crypto.createHmac(method, base64Key).update(StringForSignature).digest('base64'));
const encodeRes = encodeURIComponent(res);
return `version=${version}&res=${encodeRes}&et=${et}&method=${method}&sign=${sign}`;
}
const method = 'sha1';
const accessKey = 'mjgvkTCYTBF6DguxMmm+aV9EkDp2CYfL5jzRTph5Th6KhU8gqZz/cBivPTA7tfY5';
const res = 'userid/130037';
const et = 3600 * 1000; // 有效时间:1小时
const authorization = generateAuthorization(method, res, accessKey, et);
python代码示例
import base64
import hmac
import time
from urllib.parse import quote
def token(user_id,access_key):
version = '2022-05-01'
res = 'userid/%s' % user_id
# 用户自定义token过期时间
et = str(int(time.time()) + 3600)
# 签名方法,支持md5、sha1、sha256
method = 'sha1'
# 对access_key进行decode
key = base64.b64decode(access_key)
# 计算sign
org = et + 'n' + method + 'n' + res + 'n' + version
sign_b = hmac.new(key=key, msg=org.encode(), digestmod=method)
sign = base64.b64encode(sign_b.digest()).decode()
# value 部分进行url编码,method/res/version值较为简单无需编码
sign = quote(sign, safe='')
res = quote(res, safe='')
# token参数拼接
token = 'version=%s&res=%s&et=%s&method=%s&sign=%s' % (version, res, et, method, sign)
return token
if __name__ == '__main__':
user_id = '37715'
access_key = 'mjgvkTCYTBF6DguxMmm+aV9EkDp2CYfL5jzRTph5Th6KhU8gqZz/cBivPTA7tfY5'
print(token(user_id,access_key))
Java代码示例
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Token {
public static String assembleToken(String version, String resourceName, String expirationTime, String signatureMethod, String accessKey)
throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
StringBuilder sb = new StringBuilder();
String res = URLEncoder.encode(resourceName, "UTF-8");
String sig = URLEncoder.encode(generatorSignature(version, resourceName, expirationTime, accessKey, signatureMethod), "UTF-8");
sb.append("version=")
.append(version)
.append("&res=")
.append(res)
.append("&et=")
.append(expirationTime)
.append("&method=")
.append(signatureMethod)
.append("&sign=")
.append(sig);
return sb.toString();
}
public static String generatorSignature(String version, String resourceName, String expirationTime, String accessKey, String signatureMethod)
throws NoSuchAlgorithmException, InvalidKeyException {
String encryptText = expirationTime + "n" + signatureMethod + "n" + resourceName + "n" + version;
String signature;
byte[] bytes = HmacEncrypt(encryptText, accessKey, signatureMethod);
signature = Base64.getEncoder().encodeToString(bytes);
return signature;
}
public static byte[] HmacEncrypt(String data, String key, String signatureMethod)
throws NoSuchAlgorithmException, InvalidKeyException {
//根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
SecretKeySpec signinKey = null;
signinKey = new SecretKeySpec(Base64.getDecoder().decode(key),
"Hmac" + signatureMethod.toUpperCase());
//生成一个指定 Mac 算法 的 Mac 对象
Mac mac = null;
mac = Mac.getInstance("Hmac" + signatureMethod.toUpperCase());
//用给定密钥初始化 Mac 对象
mac.init(signinKey);
//完成 Mac 操作
return mac.doFinal(data.getBytes());
}
public enum SignatureMethod {
SHA1, MD5, SHA256;
}
public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
String version = "2022-05-01";
String resourceName = "userid/12321";
//用户自定义token过期时间
String expirationTime = System.currentTimeMillis() / 1000 + 3600 + "";
String signatureMethod = SignatureMethod.SHA1.name().toLowerCase();
String accessKey = "KuF3NT/jUBJ62LNBB/A8XZA9CqS3Cu79B/ABmfA1UCw=";
String token = assembleToken(version, resourceName, expirationTime, signatureMethod, accessKey);
System.out.println("Authorization:" + token);
}
}
go代码示例
func (s *SignService) GenerateSign(params_map map[string]string, access_key string) (string, error){
signature_str := params_map["et"] + "n" + params_map["method"] + "n" + params_map["res"] + "n" + params_map["version"]
hmac_str := ""
switch strings.ToLower(params_map["method"]) {
case "sha1":
hmac_str = tools.HmacSha1(signature_str, tools.Base64Decode(access_key))
case "sha256":
hmac_str = tools.HmacSha256(signature_str, tools.Base64Decode(access_key))
case "md5":
hmac_str = tools.HmacMd5(signature_str, tools.Base64Decode(access_key))
default:
return "",errors.New("签名参数错误!")
}
sign :=tools.Base64Encode(hmac_str)
return sign,nil
}
func Base64Encode(message string) string {
return base64.StdEncoding.EncodeToString([]byte(message))
}
func Base64Decode(message string) string {
decode_str, err := base64.StdEncoding.DecodeString(message)
if err != nil {
return ""
}
return string(decode_str)
}
func HmacSha256(data string, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(data))
return string(h.Sum(nil))
}
func HmacMd5(data string, secret string) string {
h := hmac.New(md5.New, []byte(secret))
h.Write([]byte(data))
return string(h.Sum(nil))
}
func HmacSha1(data string, secret string) string {
h := hmac.New(sha1.New, []byte(secret))
h.Write([]byte(data))
return string(h.Sum(nil))
}
php代码示例
class SafetyAuth
{
private $_version = '2022-05-01';//版本
private $_method = 'sha1';//加密方法,还可以用md5、sha256
private $_access_key;//访问密钥
private $_res ;//资源参数
private $_et;//到期时间戳
private $_expiration;//有效期,有效时间
public function __construct($access_key, $expiration,$res){
$this->_expiration = $expiration;
$this->_access_key = $access_key;
$this->_res = $res;
}
//生成sign
private function _makeSign() {
date_default_timezone_set('PRC');
$this->_et =time() + $this->_expiration;
$string_for_signature = $this->_et . "n" . $this->_method . "n" . $this->_res . "n" . $this->_version;
$b64_decode_acckey = base64_decode($this->_access_key);
$hmac_key = hash_hmac($this->_method, $this->_strToUtf8($string_for_signature), $b64_decode_acckey, true);
$sign = base64_encode($hmac_key);
return $sign;
}
private function _strToUtf8($str) {
$encode = mb_detect_encoding($str, array("ASCII",'UTF-8',"GB2312","GBK",'BIG5'));
if ($encode == 'UTF-8') {
return $str;
} else {
return mb_convert_encoding($str, 'UTF-8', $encode);
}
}
//生成token
public function makeToken() {
$sign = $this->_makeSign();
$token = sprintf("version=2022-05-01&res=%s&et=%s&method=sha1&sign=%s", urlencode($this->_res), $this->_et, urlencode($sign));
return $token;
}
}
2.2 搭建开发环境
开发工具下载:https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html
2.3 小程序页面设计
1. 创建微信小程序工程
打开微信开发者工具,选择“创建项目”,在创建时选择JavaScript模板。接着,设置项目名称、AppID(如果是测试可以使用“测试号”)、项目目录等。
2. 修改 app.json
文件
app.json
是小程序的全局配置文件,配置小程序的页面路径、窗口样式等。你需要在该文件中定义页面。
{
"pages": [
"pages/index/index",
"pages/details/details",
"pages/control/control"
],
"window": {
"navigationBarTitleText": "智能电度表",
"navigationBarBackgroundColor": "#2e2e2e",
"navigationBarTextStyle": "white"
}
}
这里我们假设你有三个页面:
• index:主页面,展示电度表的主要信息。
• details:展示更详细的电力消耗历史记录。
• control:用于控制电度表的继电器开关、查看余额等。
3. 创建页面目录
在项目目录下,创建页面目录,并创建相应的文件:
/pages
/index
- index.wxml
- index.wxss
- index.js
- index.json
/details
- details.wxml
- details.wxss
- details.js
- details.json
/control
- control.wxml
- control.wxss
- control.js
- control.json
4. 设计静态页面(index.wxml
)
在 index.wxml
中,展示电度表的当前状态(电压、电流、功率、电量等)。例如:
<view class="container">
<view class="header">
<text class="title">智能电度表</text>
</view>
<view class="status">
<text class="label">电压:</text>
<text class="value">{{voltage}} V</text>
</view>
<view class="status">
<text class="label">电流:</text>
<text class="value">{{current}} A</text>
</view>
<view class="status">
<text class="label">功率:</text>
<text class="value">{{power}} W</text>
</view>
<view class="status">
<text class="label">电量:</text>
<text class="value">{{energy}} kWh</text>
</view>
<button class="button" bindtap="goToDetails">查看历史</button>
<button class="button" bindtap="goToControl">控制电度表</button>
</view>
5. 设置样式(index.wxss
)
为页面元素设置一些基本的样式。
.container {
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.title {
font-size: 30px;
font-weight: bold;
}
.status {
display: flex;
justify-content: space-between;
margin-bottom: 15px;
}
.label {
font-size: 20px;
}
.value {
font-size: 20px;
color: #ff7f00;
}
.button {
width: 100%;
padding: 10px;
margin-top: 15px;
background-color: #ff7f00;
color: white;
border-radius: 5px;
text-align: center;
font-size: 18px;
}
6. 页面逻辑(index.js
)
在 index.js
中,可以编写页面的逻辑代码,主要是展示数据(电压、电流、功率等)。这些数据自云平台或本地存储。为了简化示例,假设数据是静态的。
Page({
data: {
voltage: 220.5,
current: 5.0,
power: 1100,
energy: 35.2
},
onLoad: function() {
// 在页面加载时获取电度表数据(此处可以调用API)
console.log('电度表数据加载');
},
goToDetails: function() {
wx.navigateTo({
url: '/pages/details/details'
});
},
goToControl: function() {
wx.navigateTo({
url: '/pages/control/control'
});
}
});
7. 详细页面(details.wxml
)
在 details.wxml
中展示电力历史记录。
<view class="container">
<view class="header">
<text class="title">用电历史记录</text>
</view>
<view class="history-list">
<view class="history-item" wx:for="{{history}}" wx:key="index">
<text class="date">{{item.date}}</text>
<text class="usage">用电量: {{item.energy}} kWh</text>
</view>
</view>
<button class="button" bindtap="goBack">返回首页</button>
</view>
8. 控制页面(control.wxml
)
在 control.wxml
中,可以设计一个界面,允许用户控制电度表(例如控制继电器、查看余额等)。
<view class="container">
<view class="header">
<text class="title">控制电度表</text>
</view>
<button class="button" bindtap="turnOnRelay">开启电源</button>
<button class="button" bindtap="turnOffRelay">关闭电源</button>
<view class="balance">
<text class="label">剩余电量:</text>
<text class="value">{{remaining_balance}} kWh</text>
</view>
<button class="button" bindtap="goBack">返回首页</button>
</view>
9. 页面逻辑(details.js
和 control.js
)
这些页面的逻辑和 index.js
类似。处理用户交互,比如获取历史记录、控制电源开关等。
例如,在 details.js
中可以从云平台或本地存储获取用电历史数据。
Page({
data: {
history: [
{ date: '2025-02-17', energy: 1.5 },
{ date: '2025-02-16', energy: 1.2 },
{ date: '2025-02-15', energy: 2.0 }
]
},
goBack: function() {
wx.navigateBack();
}
});
10. 运行与测试
完成代码后,可以在微信开发者工具中进行调试和测试,确保页面的显示效果符合预期,并且各个页面的跳转、数据展示功能正常。
微信小程序的界面展示了智能电度表的核心功能:实时电力监控、历史数据查看、设备控制等。通过简单的设计和实现,用户能够方便地查看电力消耗情况,并进行相关操作。这个界面为后续增加更多功能(如实时数据同步、余额管理等)提供了良好的基础。
三、STM32代码设计
当前项目使用的相关软件工具、模块源码已经上传到网盘:https://ccnr8sukk85n.feishu.cn/wiki/QjY8weDYHibqRYkFP2qcA9aGnvb?from=from_copylink
main.c
包含了主控制代码,初始化硬件模块、处理数据采集、功率计算、LCD显示、Wi-Fi通信、MQTT数据上传等功能。
#include "stm32f1xx_hal.h"
#include "lcd.h"
#include "wifi_module.h"
#include "mqtt_client.h"
#include "power_monitor.h"
#include "relay_control.h"
#include "buzzer.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
// 定义全局变量
float voltage = 0.0f;
float current = 0.0f;
float power = 0.0f;
float energy = 0.0f;
float remaining_balance = 100.0f;
// MQTT连接参数
const char *mqtt_broker = " ";
const char *mqtt_topic = " ";
const char *mqtt_client_id = "";
// 初始化所有硬件模块
void System_Init(void)
{
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_USART1_UART_Init(); // 初始化串口
MX_TIM2_Init(); // 初始化定时器
MX_LCD_Init(); // 初始化LCD显示屏
MX_WiFi_Init(); // 初始化Wi-Fi模块
MX_MQTT_Init(); // 初始化MQTT客户端
MX_Relay_Init(); // 初始化继电器
MX_Buzzer_Init(); // 初始化蜂鸣器
}
// 获取电压、电流、功率和电量数据
void Get_Power_Data(void)
{
voltage = Get_Voltage(); // 从电压互感器获取电压
current = Get_Current(); // 从电流互感器获取电流
power = Calculate_Power(voltage, current); // 根据电压和电流计算功率
energy = Calculate_Energy(power); // 根据功率计算电量
}
// 更新LCD显示屏上的电力数据
void Update_LCD_Display(void)
{
LCD_Clear();
LCD_Printf("Voltage: %.2f V", voltage);
LCD_Printf("Current: %.2f A", current);
LCD_Printf("Power: %.2f W", power);
LCD_Printf("Energy: %.2f kWh", energy);
}
// 上传数据到云平台
void Upload_Data_To_Cloud(void)
{
char payload[256];
snprintf(payload, sizeof(payload), "{"voltage": %.2f, "current": %.2f, "power": %.2f, "energy": %.2f}", voltage, current, power, energy);
MQTT_Publish(mqtt_broker, mqtt_topic, payload); // 将数据通过MQTT协议发布到云平台
}
// 检查功率是否过载,并进行保护
void Check_Overload_Protection(void)
{
if (power > 200.0f) // 设定功率过载阈值为200W
{
Relay_Off(); // 断开继电器,切断电源
Buzzer_Alert(); // 发出蜂鸣器报警
}
}
// 主循环
int main(void)
{
// 系统初始化
System_Init();
// 主循环
while (1)
{
// 获取电力数据
Get_Power_Data();
// 更新LCD显示屏
Update_LCD_Display();
// 检查功率是否过载,并进行保护
Check_Overload_Protection();
// 上传数据到云平台
Upload_Data_To_Cloud();
// 每10秒钟更新一次数据
HAL_Delay(10000); // 延时10秒
// 周期性任务(Wi-Fi连接、MQTT保持连接等)
WiFi_Connect();
MQTT_KeepAlive();
}
}
代码说明:
1. 初始化函数:System_Init()
中包含了所有硬件的初始化,包括时钟、GPIO、UART、定时器、LCD显示、Wi-Fi模块、MQTT客户端、继电器和蜂鸣器。
2. 获取电力数据:Get_Power_Data()
调用相应的电压、电流获取函数并计算功率和电量。
3. 更新LCD显示:Update_LCD_Display()
在LCD屏上实时显示电压、电流、功率和电量。
4. 上传数据到云平台:Upload_Data_To_Cloud()
将采集到的数据封装成JSON格式并通过MQTT协议上传到云平台。
5. 过载保护:Check_Overload_Protection()
检查功率是否超过预设的200W阈值,如果过载,断开继电器并触发蜂鸣器报警。
6. 主循环:main()
函数是程序的主执行部分,在其中定时采集数据、更新LCD、检查过载保护并上传数据到云平台。
四、总结
本项目设计并实现了一款基于STM32的智能电度表,提升家庭和商业用户的电能管理能力。通过实时监控电力使用情况、灵活控制电源、提供安全保护功能,并与云平台和微信小程序实现数据同步,项目实现了电能管理的智能化、便捷化和高效化。
项目的主要功能包括电能监测与数据采集、预付费功能、功率过载保护、用电记录与计时、远程控制与数据上传等,极大地提高了用户对电能的控制能力和使用效率。通过电压互感器和电流互感器,系统能够精确测量电压、电流、功率等电力数据,确保电能使用的实时性和准确性。
在技术实现方面,STM32作为核心控制单元,承担了数据采集、功率计算、显示控制等任务。通过Wi-Fi模块和MQTT协议,系统实现了与OneNet物联网平台和微信小程序的实时数据同步,方便用户随时了解电力消耗情况,进行余额查询和充值操作。此外,系统还具备过载保护功能,当功率超出设定阈值时,自动切断电源,避免设备损坏或安全隐患。
本项目的创新之处在于将传统电度表的功能与现代物联网技术相结合,提供了更智能、更便捷的电能管理解决方案。通过微信小程序,用户不仅可以实时查看电力使用数据,还可以进行电器设备的远程控制,提升了用户体验和用电安全。
随着智能家居和物联网技术的不断发展,本项目可进一步优化和扩展,支持更多智能电器和能源管理功能,如设备状态监控、智能调节电力负载等,进一步提升用户对能源使用的智能管理能力。
本项目为电能管理领域带来了创新性的解决方案,充分利用了STM32的高性能处理能力、Wi-Fi模块的无线通信功能,以及MQTT协议的数据传输能力,成功实现了一个集成化、智能化的电度表系统,具备了较强的实用价值和市场潜力。