1. 前言
随着工业和城市的快速发展,道路照明系统产业也在快速发展,趋于复杂,为了更经济地控制和维护复杂的路灯系统,市场主要技术公司开发了各种路灯控制系统,路灯连接物联网云,连接政府网络,可以远程了解路灯的工作、运行。 为了便于了解物联网平台,学习物联网开发,了解智能路灯的开发,本文采用了华为云IOT选择物联网平台STM智能路灯开发作为主控芯片,配合相关传感器。
2. 具体功能及相关硬件
目前设计的智能路灯采用NBIOT模块-BC连接华为云物联网服务器,上传各种路灯参数信息:环境光强度、太阳能充电板电压、锂电池电量等信息。设计了一部手机APP,通过华为云物联网平台应用侧提供的开发网平台应用侧提供的开发的参数信息,并在手机上APP远程手动控制路灯开关,设置路灯开关时间。智能路灯的主控芯片采用STM32F103C8T6、NBIOT采用联网模块BC20,BC20内置了MQTT协议,可通过AT指令连接物联网平台实现通信。智能路灯采用锂电池供电,配备太阳能充电板,可利用太阳充电补偿功率。使用功率监控器、电池功率检测芯片,可检测充电效率、电流、电压、电池功率等信息。BC20带了GPS功能,可报告路灯GPS位置到云端服务器,手机APP获取后,可以调用百度地图显示路灯的位置。
3. 硬件选型
3.1 STM32F103C8T6
3.2 太阳能板
3.3 锂电池充电模块
3.4 功率检测模块
3.5 BH1750光敏传感器
3.6 LED灯
3.7 BC20-NBIOT模块
型号: BC20 BD GPS品牌: 创思产地: 中国大陆接口类型: TTL适用场景: NBIOT尺寸: 40x40x12mm工作电流: 0.5A支持TCP/IP协议: 支持传输速率: 115200Kbps工作电压: 5V是否支持语音电话: 无模块类型: 其他人是否支持短信: 不支持标准标准: GSM/GPRS(2G)是否支持电话簿: 否
4. 创建产品和设备
4.1 创建产品
地址:https://www.huaweicloud.com/?locale=zh-cn
4.2 自定义模型
地址: https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product/7211833377cf435c8c0580de390eedbe/product-detail/6276134223aaf461a0f6e515
4.3 创建设备
{
"device_id": "6276134223aaf461a0f6e515_1126626497", "secret": "12345678"}
4.4 MQTT密匙生成
创建完产品、设备之后,接下来就需要知道如何通过MQTT协议登陆华为云服务器。 官方的详细介绍在这里: https://support.huaweicloud.com/devg-iothub/iot_01_2127.html#ZH-CN_TOPIC_0240834853__zh-cn_topic_0251997880_li365284516112
属性上报格式: https://support.huaweicloud.com/api-iothub/iot_06_v5_3010.html
MQTT设备登陆密匙生成地址: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
DeviceId 6276134223aaf461a0f6e515_1126626497DeviceSecret 12345678ClientId 6276134223aaf461a0f6e515_1126626497_0_0_2022050706Username 6276134223aaf461a0f6e515_1126626497Password 73ebe0779dbd5b2e2fd3db8ab8f642b78d7a532576f2e14d2799d4f78d37bcc8
华为云物联网平台的域名是: 161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
华为云物联网平台的IP地址是:121.36.42.100
在软件里参数填充正确之后,就看到设备已经连接成功了。 接下来打开设备页面,可以看到设备已经在线了。
4.5 主题订阅与发布
//订阅主题: 平台下发消息给设备$oc/devices/6276134223aaf461a0f6e515_1126626497/sys/messages/down//设备上报数据$oc/devices/6276134223aaf461a0f6e515_1126626497/sys/properties/report//上报的属性消息 (一次可以上报多个属性,在json里增加就行了){
"services": [{
"service_id": "led","properties":{
"GPS":"lat:12.345,lng:45.678"}}]}
通过MQTT客户端软件模拟上报测试:
4.6 应用侧开发
为了更方便的展示设备数据,与设备完成交互,还需要开发一个配套的上位机,官方提供了应用侧开发的API接口、SDK接口,为了方便通用一点,我这里采用了API接口完成数据交互,上位机软件采用QT开发。
帮助文档地址: ttps://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html
设备属性就是设备上传的传感器状态数据信息,应用侧提供了API接口,可以主动向设备端下发请求指令;设备端收到指令之后需要按照约定的数据格式上报数据;所以,要实现应用层与设备端的数据交互,需要应用层与设备端配合才能完成。
5. STM32程序设计
STM32连接华为云IOT的工程案例: https://download.csdn.net/download/xiaolong1126626497/81993720
5.1 BC20连接华为云物联网服务器-调试
连接MQTT服务器AT+QMTOPEN=0,"a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com",1883OK+QMTOPEN: 0,0登录MQTT服务器命令格式: AT+QMTCONN=<tcpconnectID>,<clientID>,<username>,<password>AT+QMTCONN=0,"6210e8acde9933029be8facf_dev1_0_0_2022021913","6210e8acde9933029be8facf_dev1","6cea55404b463e666cd7a6060daba745bbaa17fe7078dfef45f8151cdf19673d"OK+QMTCONN: 0,0,0订阅主题命令格式: AT+QMTSUB=<tcpconnectID>,<msgID>,"<topic1>”,<qos1>[,"<topic2>”,<qos2>…]AT+QMTSUB=0,1,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/messages/down",2OK+QMTSUB: 0,1,0,2发布主题命令格式:AT+QMTPUB=<tcpconnectID>,<msgID>,<qos>,<retain>,"<topic>","<msg>"先发送指令: AT+QMTPUB=0,0,0,0,"$oc/devices/6210e8acde9933029be8facf_dev1/sys/properties/repor"等待返回 ">" 接着发送数据.不需要加回车。"{"services": [{"service_id": "gps","properties":{"longitude":12.345,"latitude":33.345}}]}"数据发送完毕,再发送结束符。 十六进制的值--0x1a 。某些串口调试助手可以适应ctrl+z 快捷键输入0xA等待模块返回"OK",到此数据发送完成。 OK+QMTPUB: 0,0,0
5.2 测试模块
第一步接上之后,串口调试助手选择波特率为115200,勾选软件上的发送新行选项。发送AT
过去,正常模块会返回OK
。
查询模块是否正常ATOK获取卡号,查询卡是否插好AT+CIMI460041052911195OK激活网络AT+CGATT=1OK获取网络激活状态AT+CGATT?+CGATT: 1OK查询网络质量AT+CSQ+CSQ: 26,0OK AT+CEREG=? //检查网络状态+CEREG: 0,1 //找网成功OK
5.3 keil工程代码
MQTT协议连接华为云IOT源码工程: https://download.csdn.net/download/xiaolong1126626497/81993720
5.4 功率检测
#include "INA226.h"#include "delay.h"// 接线说明:// 模拟IIC://IIC_SCL -- 时钟线PB6(推挽、开漏输出)//IIC_SDA -- 双向数据线PB7INA226 ina226_data;//初始化INA226void INA226_Init(void){
IIC_Init(); INA226_SendData(INA226_ADDR1,CFG_REG,0x8000); //重新启动 INA226_SendData(INA226_ADDR1,CFG_REG,0x484f); //设置转换时间204us,求平均值次数128,采样时间为204*128,设置模式为分流和总线连续模式 INA226_SendData(INA226_ADDR1,CAL_REG,CAL); //设置分辨率 //INA226_SendData(INA226_ADDR1,CAL_REG,0x0012);//设置分流电压转电流转换参数 INA226_Get_ID(INA226_ADDR1); //获取ina226的id}//设置寄存器指针void INA226_SetRegPointer(u8 addr,u8 reg){
IIC_Start(); IIC_Send_Byte(addr); IIC_Wait_Ack(); IIC_Send_Byte(reg); IIC_Wait_Ack(); IIC_Stop();}//发送,写入数据void INA226_SendData(u8 addr,u8 reg,u16 data){
u8 temp=0; IIC_Start(); IIC_Send_Byte(addr); IIC_Wait_Ack(); IIC_Send_Byte(reg); IIC_Wait_Ack(); temp = (u8)(data>>8); IIC_Send_Byte(temp); IIC_Wait_Ack(); temp = (u8)(data&0x00FF); IIC_Send_Byte(temp); IIC_Wait_Ack(); IIC_Stop();}//读取数据u16 INA226_ReadData(u8 addr){
u16 temp=0; IIC_Start(); IIC_Send_Byte(addr+1); IIC_Wait_Ack(); temp = IIC_Read_Byte(1); temp<<=8; temp |= IIC_Read_Byte(0); IIC_Stop(); return temp;}//1mA/bitu16 INA226_GetShunt_Current(u8 addr){
u16 temp=0; INA226_SetRegPointer(addr,CUR_REG); temp = INA226_ReadData(addr); if(temp&0x8000) temp = ~(temp - 1); return temp;}//获取idvoid INA226_Get_ID(u8 addr){
u32 temp=0; INA226_SetRegPointer(addr,INA226_GET_ADDR); temp = INA226_ReadData(addr); ina226_data.ina226_id = temp;}//获取校准值u16 INA226_GET_CAL_REG(u8 addr){
u32 temp=0; INA226_SetRegPointer(addr,CAL_REG); temp = INA226_ReadData(addr); return (u16)temp;}//1.25mV/bitu16 INA226_GetVoltage(u8 addr){
u32 temp = 0; INA226_SetRegPointer(addr,BV_REG); temp = INA226_ReadData(addr); return (u16)temp; }//2.5uV/bitu16 INA226_GetShuntVoltage(u8 addr){
int16_t temp = 0; INA226_SetRegPointer(addr,SV_REG); temp = INA226_ReadData(addr); if(temp&0x8000) temp = ~(temp - 1); return (u16)temp; }//获取电压void GetVoltage(float *Voltage)//mV{
*Voltage = INA226_GetVoltage(INA226_ADDR1)*Voltage_LSB;}//获取分流电压void Get_Shunt_voltage(float *Voltage)//uV{
*Voltage = (INA226_GetShuntVoltage(INA226_ADDR1)*INA226_VAL_LSB);//如需矫正电流分流参数请将这里改为2.5}//获取电流void Get_Shunt_Current(float *Current)//mA{
*Current = (INA226_GetShunt_Current(INA226_ADDR1)* CURRENT_LSB);}//获取功率= 总线电压 * 电流void get_power()//W{
GetVoltage(&ina226_data.voltageVal); //mV Get_Shunt_voltage(&ina226_data.Shunt_voltage); //uV Get_Shunt_Current(&ina226_data.Shunt_Current); //mA Get_Power(&ina226_data.Power); ina226_data.Power_Val = ina226_data.voltageVal*0.001f * ina226_data.Shunt_Current*0.001f; //mV*mA}//获取功率装载值,ina226内部计算的的功率,由于未经校准,故不采用u16 INA226_Get_Power(u8 addr){
int16_t temp=0; INA226_SetRegPointer(addr,PWR_REG); temp = INA226_ReadData(addr); return (u16)temp;}//获取功率,ina226内部计算,不准确,不采用void Get_Power(float *Power)//W{
*Power = (INA226_Get_Power(INA226_ADDR1)*POWER_LSB);}//不设置报警,舍弃/*u8 INA226_AlertAddr(){ u8 temp; IIC_Start(); IIC_Send_Byte(INA226_GETALADDR); IIC_Wait_Ack(); temp = IIC_Read_Byte(1); IIC_Stop(); return temp;}*/
5.5 BH1750环境光强度
#include "bh1750.h"float Read_BH1750_Data(){
unsigned char t0; unsigned char t1; float t; u8 r_s=0; IIC_Start(); //发送起始信号 IIC_WriteOneByteData(0x46); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:1\r\n"); IIC_WriteOneByteData(0x01); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:2\r\n"); IIC_Stop(); //停止信号 IIC_Start(); //发送起始信号 IIC_WriteOneByteData(0x46); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:3\r\n"); IIC_WriteOneByteData(0x01); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:4\r\n"); IIC_Stop(); //停止信号 IIC_Start(); //发送起始信号 IIC_WriteOneByteData(0x46); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:5\r\n"); IIC_WriteOneByteData(0x10); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:6\r\n"); IIC_Stop(); //停止信号 DelayMs(100); //等待 IIC_Start(); //发送起始信号 IIC_WriteOneByteData(0x47); r_s=IIC_GetACK();//获取应答 if(r_s)printf("error:7\r\n"); t0=IIC_ReadOneByteData(); //接收数据 IIC_SendACK(0); //发送应答信号 t1=IIC_ReadOneByteData(); //接收数据 IIC_SendACK(1); //发送非应答信号 IIC_Stop(); //停止信号 t=(((t0<<8)|t1)/1.2); return t; }