• 基于STM32设计的智慧路灯(太阳能+华为云IOT)


    1. 前言

    随着工业和城市的快速发展,道路照明系统的产业也在迅速发展,并趋于复杂化,为了更经济地控制和维护复杂的路灯系统,市面上各大科技公司开发了各种路灯控制系统,并且路灯都连上物联网云端,连上了政府网络,可以远程了解路灯的工作情况,运行情况。
    为了方便了解物联网平台,学习物联网开发,了解智慧路灯的开发,本文就采用华为云IOT物联网平台,选择STM32作为主控芯片,再配合相关的传感器完成智慧路灯开发。

    2. 具体实现的功能以及相关的硬件

    当前设计的智慧路灯采用NBIOT模块-BC20连接华为云物联网服务器,上传路灯的各种参数信息:环境光强度、太阳能充电板电压、锂电池电量等信息。设计了一款手机APP,可以通过华为云物联网平台的应用侧提供的开发接口,获取路灯上传的参数信息,并且可以在手机APP上远程手动控制路灯开关,设置路灯的开启和关闭时间等。 智慧路灯的的主控芯片采用STM32F103C8T6、NBIOT联网模块采用BC20,BC20内置了MQTT协议,可以通过AT指令连接物联网平台实现通信。智慧路灯采用锂电池供电,并且配有太阳能充电板,可以使用太阳进行充电补偿电量。使用了功率监控器,电池电量检测芯片,可以检测充电效率、电流、电压、电池电量等信息。BC20带了GPS功能,可以上报路灯的GPS位置到云端服务器,手机APP获取之后,可以调用百度地图显示出路灯的位置。

    image.png
    image.png
    image.png

    image-20220606092735926

    image-20220606092826153

    image-20220606092858022

    3. 硬件选型

    3.1 STM32F103C8T6

    image.png
    image.png

    3.2 太阳能板

    image.png

    3.3 锂电池充电模块

    image.png

    3.4 功率检测模块

    image.png

    3.5 BH1750光敏传感器

    image.png

    3.6 LED灯

    image.png

    3.7 BC20-NBIOT模块

    型号: BC20+BD+GPS
    品牌: 创思
    产地: 中国大陆
    接口类型: TTL
    适用场景: NBIOT
    尺寸: 40x40x12mm
    工作电流: 0.5A
    支持TCP/IP协议: 
    支持传输速率: 115200Kbps
    工作电压: 5V
    是否支持语音电话: 否
    模块类型: 其他
    是否支持短信: 否
    支持制式标准: GSM/GPRS(2G)
    是否支持电话簿:
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    image.png

    4. 创建产品与设备

    4.1 创建产品

    地址:https://www.huaweicloud.com/?locale=zh-cn
    image.png
    image.png
    image.png
    image.png

    4.2 自定义模型

    地址: https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-dev/all-product/7211833377cf435c8c0580de390eedbe/product-detail/6276134223aaf461a0f6e515

    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png
    image.png

    4.3 创建设备

    image.png
    image.png

    {
        "device_id": "6276134223aaf461a0f6e515_1126626497",
        "secret": "12345678"
    }
    
    • 1
    • 2
    • 3
    • 4

    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
    image.png

    image.png

    MQTT设备登陆密匙生成地址: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/
    image.png
    image.png

    DeviceId      	6276134223aaf461a0f6e515_1126626497
    DeviceSecret 	12345678
    ClientId  		6276134223aaf461a0f6e515_1126626497_0_0_2022050706
    Username  		6276134223aaf461a0f6e515_1126626497
    Password  		73ebe0779dbd5b2e2fd3db8ab8f642b78d7a532576f2e14d2799d4f78d37bcc8
    
    • 1
    • 2
    • 3
    • 4
    • 5

    华为云物联网平台的域名是: 161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
    华为云物联网平台的IP地址是:121.36.42.100
    在软件里参数填充正确之后,就看到设备已经连接成功了。
    接下来打开设备页面,可以看到设备已经在线了。
    image.png
    image.png

    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"}}]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过MQTT客户端软件模拟上报测试:
    image.png
    image.png

    4.6 应用侧开发

    为了更方便的展示设备数据,与设备完成交互,还需要开发一个配套的上位机,官方提供了应用侧开发的API接口、SDK接口,为了方便通用一点,我这里采用了API接口完成数据交互,上位机软件采用QT开发。

    帮助文档地址: ttps://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

    设备属性就是设备上传的传感器状态数据信息,应用侧提供了API接口,可以主动向设备端下发请求指令;设备端收到指令之后需要按照约定的数据格式上报数据;所以,要实现应用层与设备端的数据交互,需要应用层与设备端配合才能完成。

    image.png
    image.png

    5. STM32程序设计

    STM32连接华为云IOT的工程案例: https://download.csdn.net/download/xiaolong1126626497/81993720
    image.png

    5.1 BC20连接华为云物联网服务器-调试

    image.png

    连接MQTT服务器
    AT+QMTOPEN=0,"a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com",1883
    
    OK
    
    +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",2
    
    OK
    
    +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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    5.2 测试模块

    第一步接上之后,串口调试助手选择波特率为115200,勾选软件上的发送新行选项。发送AT过去,正常模块会返回OK

    查询模块是否正常
    AT
    
    OK
    
    
    获取卡号,查询卡是否插好
    AT+CIMI
    
    460041052911195
    
    OK
    
    
    激活网络
    AT+CGATT=1
    
    OK
    
    
    获取网络激活状态
    AT+CGATT?
    
    +CGATT: 1
    
    OK
    
    
    查询网络质量
    AT+CSQ
    
    +CSQ: 26,0
    
    OK
        
    AT+CEREG=? //检查网络状态
    +CEREG: 0,1 //找网成功
    OK
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    5.3 keil工程代码

    MQTT协议连接华为云IOT源码工程: https://download.csdn.net/download/xiaolong1126626497/81993720

    image.png
    image.png
    image.png

    5.4 功率检测

    #include "INA226.h"
    #include "delay.h"
    //	接线说明:
    //	模拟IIC:
    //IIC_SCL -- 时钟线PB6(推挽、开漏输出)
    //IIC_SDA -- 双向数据线PB7
    INA226 ina226_data;
    //初始化INA226
    void 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/bit
    u16 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;
    }
    
    //获取id
    void 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/bit
    u16 INA226_GetVoltage(u8 addr)
    {
    	u32 temp = 0;
    	INA226_SetRegPointer(addr,BV_REG);
    	temp = INA226_ReadData(addr);
    	return (u16)temp;	
    }
    
    //2.5uV/bit
    u16 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;
    }
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181

    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;  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    毫米波V2I网络的链路层仿真研究(Matlab代码实现)
    2023年【上海市安全员C证】考试及上海市安全员C证找解析
    药品企业中的计算机系统验证管理规程
    常见网络协议
    ExpertPrompting:指导大语言模型成为杰出专家
    【LeetCode】232.用栈实现队列
    设计模式-桥接模式
    「网页开发|前端开发|Vue」04 快速掌握开发网站需要的Vue基础知识
    Lingo求解规划优化问题
    第一章 Python的基础语法
  • 原文地址:https://blog.csdn.net/xiaolong1126626497/article/details/125290027