STM32项目:4G模块+LORA无线模块+MQTT+华为云,采集和上传多类型传感器数据以及控制终端设备(AT指令配置4G模块)

目录

前言:

一、摘要

二、相关链接

三、原理图展示

四、硬件连接

 五、华为云注册与创建

六、主机通过4G模块连接MQTT服务器

七、主机上传数据到华为云

八,华为云下达指令和单片机回复响应

九、华为云的数据保存

                ​​​​​​​                下面的内容是关于主机与从机之间利用LORA传输数据

十、 主机通过LORA轮询从机

十一、从机接收主机问询命令并回复信息

                ​​​​​​​        ​​​​​​​                ​​​​​​​        下面是关于传感器数据采集的介绍

十二、RS485总线采集多个传感器数据

十三、IIC读取INA226电压电流监测模块数据

十四、风速模拟量的采集与转换ADC

总结


前言:

本文是我的学习笔记,我是一个初学者,不可避免有些不足或错误,如果大家发现了,请帮我指出,谢谢。

一、摘要

这个项目除了最小系统板,其它的电路是我自己画的,从硬件到软件都实现了一遍。如果大家只对代码感兴趣,可以自己买模块。(我主要完成代码实现,硬件电路是用顺带画的。)

本项目用到的主要硬件

不自己画电路就用这些模块:STM32F103C8T6最小系统板,有人云4G模块,正点原子LORA模块,OLED屏幕(4脚),INA226电压电流监测模块,MAX485模块,5V供电模块,LED灯(可直接用最小系统板上那个PC13的灯)。

自己画电路:STM32F103C8T6最小系统板(自己焊接太费时间了,直接用的成品),有人云4G模块,正点原子LORA模块,OLED屏幕(4脚),INA226电压电流监测芯片,MAX485芯片,MP2315SGJ-Z稳压芯片,AOD4184N沟道MOS管(或用LED直接连单片机,模拟负载开关),3V5脚继电器(用LED可省略这个)。

使用的是KEIL5+标准库实现

实现功能:

从机:

--通过IIC,RS485,ADC,等原理收集温湿度,光照,风速,风向,太阳辐射,光伏板电压电流功率,风机电压电流功率和电池电压电流功率等信息。将信息按自定义格式打包,通过LORA无线发送至主机(1KM以上距离)。

--接收来自主机的控制信息,做出负载开关动作。

主机:

--通过定时器设置轮询时间间隔,通过LORA模块轮询从机,命令从机返回所收集的信息,

--将从机返回信息通过4G模块的MQTT协议模式上传至华为云,

--接收来自云端的指令,给云端返回响应,并将指令下发给从机。

完整版main.c逻辑展示:

从机:

主机:

二、相关链接

百度网盘资料:

所有资料https://pan.baidu.com/s/1jZ6KuBWUZWtfUZW7b9rmmg?pwd=1234gitee仓库:Wang Jiasong/STM32_MQTT_4G_华为云_LORAhttps://gitee.com/wang_jiasong_HB/stm32mqtt4g-huawei-cloudlora.git

 参考博客:

MQTT协议详解https://xiaolong.blog.youkuaiyun.com/article/details/134755897?fromshare=blogdetail&sharetype=blogdetail&sharerId=134755897&sharerefer=PC&sharesource=qq_56632135&sharefrom=from_linkMQTT协议使用文档中文版:

MQTT协议文档https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/02-ControlPacketFormat.html

三、原理图展示

主机:

从机:

四、硬件连接

引脚连接如下:

引脚分配
引脚名称功能(主机)简介(主机)功能(从机)简介(从机)
PA2 USART2_TX串口2:4G串口USART2_TX串口2:调试串口
PA3 USART2_RXUSART2_RX
PA4 SCL软件IIC:获取电压电流值
PA5 SDA
PA6 ADC读取风速模拟量
PA7 LOAD负载开关控制引脚LOAD负载开关控制引脚
PB1 485REMAX485芯片使能
PB10 USART3_TX串口3:调试串口USART3_TXRS485通信串口:获取传感器信息
PB11 USART3_RXUSART3_RX
PB12 SCLOLED屏幕SCLOLED屏幕
PB13 SDASDA
PA9 USART1_TX串口1:LORA串口USART1_TX串口1:LORA串口
PA10 USART1_RXUSART1_RX
PB6 KEY按键KEY按键

 五、华为云注册与创建

华为云网址

1.注册登录,实名认证

2.找到控制台,找到“设备接入loTDA”,

2.开通免费单元

3.创建产品

4.创建模型

5.添加属性和命令

6.创建产品(创建完之后,会自动下载一个text文本,那是你的设备id和密码

7.点击详情,进如产品详情界面,到这里,云端的东西都创建好了。

然后打开华为云官方网站,生成MQTT的3个重要秘钥:

华为云秘钥生成

把刚刚的设备id和密码复制进去,选择不校验时间戳(这个可以在单片机里一直用,不用更新),把生成的3个数据复制保存起来:

获取华为云MQTT 接入地址 端口号 :1883

现在手上有3个重要秘钥,接入地址以及端口号了。下面开始单片机的操作。

六、主机通过4G模块连接MQTT服务器

4G模块我用的是有人云的WH-LTE-7S1 Cat-1-N40,买回来后直接连接好单片机上电就行。

因为源码都给了,所以我只对关键部分做介绍,具体请看代码。

连接MQTT服务器靠FourG.c.h中的函数FourG_MQTT_Config();    main.c中这样就可以让4G模块连接上服务器了。

#include "stm32f10x.h"   
#include "Delay.h"
#include "FourG.h"
#include "usart3.h"

int main(void)
{
	FourG_Init(115200);					/*串口2:4G串口初始化*/
	Usart3_Init(115200); 				/*串口3:调试串口初始化*/
	
	FourG_MQTT_Config();							/*配置4G模块连接华为云服务器*/

	printf("Waiting for restart !\r\n");
	Delay_s(10);									/*等待10秒,让4G模块重启*/
	printf("Restart Successfully !\r\n");
	
	while (1)
	{}
}	

FourG_MQTT_Config(); 中就是这些函数组成的:

 我先介绍FourG_MQTT_Config();,然后再介绍FourG_SendCmd();的实现。

FourG_MQTT_Config();中需要注意的有这几条代码:

0.这是我根据《WH-LTE-7S1WH-LTE-7S1-GN 说明书》P35写的,属于4G模块本身的规则。

2.填华为云MQTT接入地址,每个账号有一点区别,不是统一的

3.4.5.填之前获取的ClientId, Username 和 Password

6.如果连接的是3.1.1就填4

9.本项目需要实现命令的下发和响应,所以要配置为分发模式,具体参考《ASR1606_Series_MQTT 操作指南V1.0.1》P22 中的3.1.3. MQTT 分发模式

16.设置 MQTT 订阅主题,即要符合华为云订阅主题格式,本项目只用到了3个格式,如下:

18.设备重启,一般重启需要10秒左右。

void FourG_MQTT_Config(void)
{
    // 清空(初始化)串口接收缓存区
    FourG_Clear();								
    
    printf("Configuring 4G...\r\n");

	// 0. 设置模块工作模式为配置模式
    printf("0. +++\r\n");
    while(FourG_SendCmd("+++", "a")) {
        Delay_ms(100);
    }
	
	while(FourG_SendCmd("a", "+ok")) {
		Delay_ms(100);
    }

    // 1. 设置 MQTT 工作模式为常规模式
    printf("1. AT+WKMOD=MQTT,NOR\r\n");
    while(FourG_SendCmd("AT+WKMOD=MQTT,NOR\r\n", "OK")) {
        Delay_ms(500);
    }

    // 2. 设置 MQTT 服务器地址和端口
    printf("2. AT+MQTTSVR=20df243370.st1.iotda-device.cn-north-4.myhuaweicloud.com,1883\r\n");
    while(FourG_SendCmd("AT+MQTTSVR=20df243370.st1.iotda-device.cn-north-4.myhuaweicloud.com,1883\r\n", "OK")) {
        Delay_ms(500);
    }

    // 3. 设置 MQTT 客户用户名
    printf("3. AT+MQTTUSER=67d3983eceaf2e5a8fbe2db5_stm32\r\n");
    while(FourG_SendCmd("AT+MQTTUSER=67d3983eceaf2e5a8fbe2db5_stm32\r\n", "OK")) {
        Delay_ms(500);
    }

    // 4. 设置 MQTT 客户用户密码
    printf("4. AT+MQTTPSW=24fd768f869f630d4a7e73f4af57725b31edb27b14ba6e3793693f02849c0553\r\n");
    while(FourG_SendCmd("AT+MQTTPSW=24fd768f869f630d4a7e73f4af57725b31edb27b14ba6e3793693f02849c0553\r\n", "OK")) {
        Delay_ms(500);
    }

    // 5. 设置 MQTT 客户端 ID
    printf("5. AT+MQTTCID=67d3983eceaf2e5a8fbe2db5_stm32_0_0_2025031007\r\n");
    while(FourG_SendCmd("AT+MQTTCID=67d3983eceaf2e5a8fbe2db5_stm32_0_0_2025031007\r\n", "OK")) {
        Delay_ms(500);
    }

    // 6. 设置 MQTT 版本
    printf("6. AT+MQTTVER=4\r\n");
    while(FourG_SendCmd("AT+MQTTVER=4\r\n", "OK")) {
        Delay_ms(500);
    }

    // 7. 设置 Socket 重连时间间隔
    printf("7. AT+SOCKRSTIM=10\r\n");
    while(FourG_SendCmd("AT+SOCKRSTIM=10\r\n", "OK")) {
        Delay_ms(500);
    }

    // 8. 设置 Socket 最大重连次数
    printf("8. AT+SOCKRSNUM=60\r\n");
    while(FourG_SendCmd("AT+SOCKRSNUM=60\r\n", "OK")) {
        Delay_ms(500);
    }

//    // 9. 设置 MQTT 串口传输模式为 透传模式
//    printf("9. AT+MQTTMOD=0\r\n");
//    while(FourG_SendCmd("AT+MQTTMOD=0\r\n", "OK")) {
//        Delay_ms(500);
//    }
	
    // 9. 设置 MQTT 串口传输模式为 分发模式
    printf("9. AT+MQTTMOD=1\r\n");
    while(FourG_SendCmd("AT+MQTTMOD=1\r\n", "OK")) {
        Delay_ms(500);
    }

    // 10. 设置 MQTT 心跳包和清除缓存标
    printf("10. AT+MQTTCFG=60,0\r\n");
    while(FourG_SendCmd("AT+MQTTCFG=60,0\r\n", "OK")) {
        Delay_ms(500);
    }

    // 11. 开启心跳包
    printf("11. AT+HEARTEN=ON\r\n");
    while(FourG_SendCmd("AT+HEARTEN=ON\r\n", "OK")) {
        Delay_ms(500);
    }

    // 12. 设置心跳包发送间隔为30秒
    printf("12. AT+HEARTTM=30\r\n");
    while(FourG_SendCmd("AT+HEARTTM=30\r\n", "OK")) {
        Delay_ms(500);
    }

    // 13. 设置心跳包发送方式为 NET(网络)
    printf("13. AT+HEARTTP=NET\r\n");
    while(FourG_SendCmd("AT+HEARTTP=NET\r\n", "OK")) {
        Delay_ms(500);
    }

    // 14. 设置心跳包数据类型为 USER(自定义数据)
    printf("14. AT+HEARTSORT=USER\r\n");
    while(FourG_SendCmd("AT+HEARTSORT=USER\r\n", "OK")) {
        Delay_ms(500);
    }

    // 15. 设置心跳包数据为 www.usr.cn
    printf("15. AT+HEARTDT=7777772E7573722E636E\r\n");
    while(FourG_SendCmd("AT+HEARTDT=7777772E7573722E636E\r\n", "OK")) {
        Delay_ms(500);
    }

    // 16. 设置 MQTT 订阅主题(例如,订阅到 $oc/devices/67c8fd1624d7723255231822_stm32/sys/messages/down)
    printf("16. AT+MQTTSUBTP=1,1,$oc/devices/67d3983eceaf2e5a8fbe2db5_stm32/sys/messages/down,0\r\n");
    while(FourG_SendCmd("AT+MQTTSUBTP=1,1,$oc/devices/67d3983eceaf2e5a8fbe2db5_stm32/sys/messages/down,0\r\n", "OK")) {
        Delay_ms(500);
    }

    // 17. 设置 MQTT 发布主题
    printf("17. AT+MQTTPUBTP=1,1,$oc/devices/67d3983eceaf2e5a8fbe2db5_stm32/sys/properties/report,0,0\r\n");
    while(FourG_SendCmd("AT+MQTTPUBTP=1,1,$oc/devices/67d3983eceaf2e5a8fbe2db5_stm32/sys/properties/report,0,0\r\n", "OK")) {
        Delay_ms(500);
    }

    // 18. 保存配置并重启
    printf("18. AT+S\r\n");
    while(FourG_SendCmd("AT+S\r\n", "OK")) {
        Delay_ms(500);
    }
	
//	// 19. 保存配置并重启
//    printf("18. AT+S\r\n");
//    while(FourG_SendCmd("AT+S\r\n", "OK")) {
//        Delay_ms(500);
//    }

    printf("MQTT Configuration Done!\r\n");
}

FourG_SendCmd();的代码如下:

其中,FourG_printf();就是把C语言学习时期的那个printf();重定义为向串口2(和4G模块连接的哪个串口)打印,也就是向串口2发送格式化字符串,在这里利用这个函数向4G模块发送AT指令

更准确的解释是,实现了一个自定义的格式化输出函数FourG_printf(),用于通过 USART2 串口发送格式化数据。它的核心功能是将格式化的字符串写入缓冲区,然后通过 USART2 逐字节发送出去。

其中,FourG_WaitRecive(void);用于循环检测串口2的接收情况,这个函数需要循环调用(也就是代码里的while(timeOut--)),如果检测到串口2接收了完整的数据,就返回0值。

然后通过strstr((const char *)FourG_BUF, res)来检测4G模块接收到AT指令后是否返回了对的值(例如OK)。

/********************************************************
 *简介:	发送AT指令并判断返回是否合规
 *参数:	cmd:命令
		res:需要检查的返回指令
 *返回:	0-成功	1-失败	
*********************************************************/
_Bool FourG_SendCmd(char *cmd, char *res)
{
	unsigned char timeOut = 200;
	FourG_printf(cmd);											/* 串口向FourG发送AT指令 */
	while(timeOut--)
	{
		if(FourG_WaitRecive() == 0)								/* 判断是否收到完整数据 */
		{
			if(strstr((const char *)FourG_BUF, res) != NULL)	/* 如果检索到关键词 */
			{
				FourG_Clear();									/* 清空缓存 */
				return 0;
			}
		}
		Delay_ms(10);
	}
	return 1;
}

以上的代码可以实现4G模块连接MQTT服务器,连接好串口助手,连接好4G模块,打开串口助手:

下载程序后串口助手会显示:

连接上之后,华为云显示在线:

七、主机上传数据到华为云

我的项目因为实际需要,在华为云设置了15个属性变量,温度湿度电压电流什么的,如果是学习的话,设置一个温度,一个LED开关即可,但是我这篇文章就按照我的实际项目来介绍吧,所以下面来自华为云的截图是我另一个账号的,多了很多属性变量。

首先,如果没接LORA和实际传感器的话,那就在主机程序里定义一个数组uint8_t LoraArrRx[38]用来缓存模拟的传感器数据。

/*模拟 传感器数值缓存数组*/
uint8_t LoraArrRx[38] = {0x00,0x01,0x01,0x74,0xFE,0xF1,0x00,0xFB,0xC5,0x20,0x07,0x1D,0x00,0x3E,
						 0x22,0x7A,0x43,0x36,0x04,0x14,0x25,0x34,0x6C,0xBA,0x07,0xAF,0x25,0xE0,
						 0x6F,0x5C,0x07,0x6D,0x00,0x1A,0x00,0x00,0x00,0x00};	/*Lora接收数据缓存区*/

数据格式如下(我自定义的,可以不要最后的CRC校验码,但是在实际现场使用的时候还是加上,因为设备在室外,信号没有测试的时候好,有可能出现数据错误,这里模拟的话校验码就直接设置为00了):

先看这一步的主函数:

上传数据主要用到的有:

DATA_Read(LoraArrRx);功能是把LoraArrRx[38]中的数据转换为实际的温湿度值,并将这些值更新到TEMP,HUMI等变量中。

OneNet_FillBuf(buf);的作用主要是组合MQTT协议的“有效载荷”,其格式是JSON格式,最终把组合好的"有效载荷"放入buf[512]中。这个函数中,组合的数据的第一句是:strcpy(buf, "1,{\"services\": [{\"service_id\": \"stm32\",\"properties\":{");,前面的1的意思是分发模式下发给主题1,请参考《ASR1606_Series_MQTT 操作指南V1.0.1》P22 中的3.1.3. MQTT 分发模式。

//JSON格式
{
  "services": [
    {
      "service_id": <填服务ID>,
      "properties": {
        "<填属性名称1>": <填属性值>,
        "<填属性名称2>": <填属性值>   //最后一项没逗号
        ..........
      }
    }
  ]
}

FourG_TxData(buf,512);这是发送函数,即通过串口2,把数据发送给4G模块,4G模块会自动组合之前设置好的发布主题,然后自动把数据发送到华为云,数据长度建议把整个buf[512]全发过去,不要计算buf中字符串的长度,那样有可能计算错。

main.c:

#include "stm32f10x.h"   
#include "Delay.h"
#include "FourG.h"
#include "usart3.h"				
/*模拟 传感器数值缓存数组*/
uint8_t LoraArrRx[38] = {0x00,0x01,0x01,0x74,0xFE,0xF1,0x00,0xFB,0xC5,0x20,0x07,0x1D,0x00,0x3E,
						 0x22,0x7A,0x43,0x36,0x04,0x14,0x25,0x34,0x6C,0xBA,0x07,0xAF,0x25,0xE0,
						 0x6F,0x5C,0x07,0x6D,0x00,0x1A,0x00,0x01,0x00,0x00};	/*Lora接收数据缓存区*/

/*定义初始变量,这些变量的值直接被上传到华为云*/
float TEMP = 0.0, HUMI = 0.0, WDSPEED = 0.0, WDDER = 0.0;
float CELLU = 0.0 ,PVPOWER = 0.0, WDPOWER = 0.0, PVU = 0.0;
float PVI = 0.0, WDU = 0.0, WDI = 0.0, CELLI = 0.0, CELLPOWER = 0.0;
uint32_t LUX = 0, RAD = 0;
uint8_t load = 0;/*负载默认是关闭状态*/ 
unsigned char buf[512];				/*缓存温湿度上报数据*/
						 
int main(void)
{
	uint32_t timeout = 0;
	FourG_Init(115200);					/*串口2:4G串口初始化*/
	Usart3_Init(115200); 				/*串口3:调试串口初始化*/
	
	FourG_MQTT_Config();							/*配置4G模块连接华为云服务器*/

	printf("Waiting for restart !\r\n");
	Delay_s(10);									/*等待10秒,让4G模块重启*/
	printf("Restart Successfully !\r\n");
	
	while (1)
	{
		
		memset(buf,0,sizeof(buf));
		while(timeout++)
		{
			if(timeout >= 500)
			{
				DATA_Read(LoraArrRx);				/* 将数据按对应格式存放在定义好的变量中*/
				
				memset(buf,0,sizeof(buf));			/*TEMP等变量值已经为最新值,下面上传温湿度信息*/
				OneNet_FillBuf(buf);				/*组合有效载荷,并缓存到buf[]中*/
						
				FourG_TxData(buf,512);				/*直接将512个数据全部上传,
														避免长度计算错误,MQTT会自动截取有用信息*/
				timeout = 0;
			}
		}
		Delay_ms(10);
	}
}	

注意几个点:

1.创建这些属性的时候,选好数据类型(整型或小数或布尔),设置好数据范围,设置好数据步长,否则可能出错。

2.最后那个控制LED的,你此时没接LED的话,你需要把OneNet_FillBuf函数的最后一句直接改成true,不加那个结构体判断LED状态。

	memset(text, 0, sizeof(text));
	sprintf(text, "\"LOADSTA\":%s","true");
	strcat(buf, text);
	strcat(buf, "}}]}");

以上代码实现之后,华为云是这个样子:

八,华为云下达指令和单片机回复响应

先看实现到这里的main.c:

加了这些头文件:

#include "CmdProcessing.h" 这个之后再讲

#include "Load.h" 这个就是一个控制GPIO高低电平的,注意检查IO口是不是连接的你的LED,不然到时候其它都正常,灯就是不亮。

#include "stm32f10x.h"   
#include "Delay.h"
#include "FourG.h"
#include "usart3.h"	
#include "CmdProcessing.h"
#include "Load.h"

/*模拟 传感器数值缓存数组*/
uint8_t LoraArrRx[38] = {0x00,0x01,0x01,0x74,0xFE,0xF1,0x00,0xFB,0xC5,0x20,0x07,0x1D,0x00,0x3E,
						 0x22,0x7A,0x43,0x36,0x04,0x14,0x25,0x34,0x6C,0xBA,0x07,0xAF,0x25,0xE0,
						 0x6F,0x5C,0x07,0x6D,0x00,0x1A,0x00,0x00,0x00,0x00};	/*Lora接收数据缓存区*/

/*定义初始变量,这些变量的值直接被上传到华为云*/
float TEMP = 0.0, HUMI = 0.0, WDSPEED = 0.0, WDDER = 0.0;
float CELLU = 0.0 ,PVPOWER = 0.0, WDPOWER = 0.0, PVU = 0.0;
float PVI = 0.0, WDU = 0.0, WDI = 0.0, CELLI = 0.0, CELLPOWER = 0.0;
uint32_t LUX = 0, RAD = 0;
uint8_t load = 0;/*负载默认是关闭状态*/ 
unsigned char buf[512];				/*缓存温湿度上报数据*/
						 
int main(void)
{
	uint8_t LOADSTATION = 128;			/*记录负载开关指令:0为关,1为开,128为错误*/
	char request_id_buf[256];			/*缓存request_id*/
	char output_buffer[256];			/*缓存用于存储组合好的主题*/
	unsigned char buf[512];				/*缓存温湿度上报数据*/
	char buff[256];						/*缓存回复命令主题的AT指令*/
	
    memset(request_id_buf,0,sizeof(request_id_buf));
	memset(output_buffer,0,sizeof(output_buffer));
	memset(buff,0,sizeof(buff));
	memset(buf,0,sizeof(buf));

	uint32_t timeout = 0;
	FourG_Init(115200);					/*串口2:4G串口初始化*/
	Usart3_Init(115200); 				/*串口3:调试串口初始化*/
	
	FourG_MQTT_Config();							/*配置4G模块连接华为云服务器*/

	printf("Waiting for restart !\r\n");
	Delay_s(10);									/*等待10秒,让4G模块重启*/
	printf("Restart Successfully !\r\n");
	
	while (1)
	{
		memset(buf,0,sizeof(buf));
		while(timeout++)
		{
			if(timeout >= 500)
			{
				DATA_Read(LoraArrRx);				/* 将数据按对应格式存放在定义好的变量中*/
				
				memset(buf,0,sizeof(buf));			/*TEMP等变量值已经为最新值,下面上传温湿度信息*/
				OneNet_FillBuf(buf);				/*组合有效载荷,并缓存到buf[]中*/
						
				FourG_TxData(buf,512);				/*直接将512个数据全部上传,
														避免长度计算错误,MQTT会自动截取有用信息*/
				timeout = 0;
			}
		}
		
		/*处理云端命令*/
		if(SEEK_FourGRxOK() == 0)					/*如果4G完成云端命令的接收*/
		{
			printf("FourG_BUF: %s\r\n", FourG_BUF);	/*打印云端命令*/
			
			LOADSTATION = Look_LOADSTA(FourG_BUF);	/*将单片机开关指令发送至从机*/
			
			if(LOADSTATION != 128)
			{
				Look_REID(request_id_buf, FourG_BUF);/*提取request_id到request_id_buf中*/
				
				printf("request_id_buf: %s\r\n", (char *)request_id_buf);
				
				FourG_MQTT_PUBTEM(request_id_buf, output_buffer,	/*利用request_id配置临时发布主题*/
								  sizeof(output_buffer));
				
				memset(buf,0,sizeof(buf));							/*组合回复报文并发送*/
				OneNet_CmdRequestBuf(buf, output_buffer); 
				
				FourG_TxData(buf, 256);								/*发送响应信息至云端*/
			}
		}
		Delay_ms(10);
	}
}

以上代码中增加的是这些函数:

SEEK_FourGRxOK();

这个函数循环调用FourG_WaitRecive()检查云端是否有消息发过来。

 Look_LOADSTA(FourG_BUF);

这个是CmdProcessing.c.h中的函数,用来从FourG_BUF中解析出云端控制 LOAD 的命令并判断其值是true还是false, 是true就调用Load_Set(1);开灯,反之类似。

到这里,其实主要功能就实现了,但是华为云那边还要判断设备是否接收到了响应,所有下面的函数是单片机返回给华为云一个响应。

Look_REID(request_id_buf, FourG_BUF);

这个函数的传入参数FourG_BUF是串口2的接收缓存区,这时候这里面存的就是4G模块接收到的云端指令,长这样:$oc/devices/12312324d712312312822_stm32/sys/commands/request_id=d49f0bb9-ba87-4c9b-b915-98a1f0fcf689{"paras":{"LOAD":true},"service_id":"LOAD","command_name":"负载开关"}这里面的request_id是我们需要的。现在把储存了这些内容的缓存区数组传入Look_REID()函数,该函数提取出request_id的内容,并将其存放在request_id_buf[]中。

FourG_MQTT_PUBTEM(request_id_buf, output_buffer, sizeof(output_buffer));

接着,把request_id_buf[]传入FourG_MQTT_PUBTEM(),该函数会让4G模块进入配置模式,给4G模块设置一个“临时发布主题”,临时发布主题中需要request_id,用以回复华为云。最后这个函数将“临时发布主题”存入output_buffer[].

OneNet_CmdRequestBuf(buf, output_buffer); 

设置好临时发布主题后,这个函数用来组合回复报文里的“有效载荷(payload)”,有效载荷的格式也是JSON格式,但是发给4G模块的数据除了有效载荷之外,还有其它内容,参考《ASR1606_Series_MQTT 操作指南V1.0.1》P22

FourG_TxData(buf, 256);    

这个是数据发送函数,组合好报文之后,现在报文被存放在buf[]中,把buf[]发送出去就好了,可以填256,也可以填512,只要长度够包含所有报文内容就行,只能长不能短。

/*处理云端命令*/
		if(SEEK_FourGRxOK() == 0)					/*如果4G完成云端命令的接收*/
		{
			printf("FourG_BUF: %s\r\n", FourG_BUF);	/*打印云端命令*/
			
			LOADSTATION = Look_LOADSTA(FourG_BUF);	/*将单片机开关指令发送至从机*/
			
			if(LOADSTATION != 128)
			{
				Look_REID(request_id_buf, FourG_BUF);/*提取request_id到request_id_buf中*/
				
				printf("request_id_buf: %s\r\n", (char *)request_id_buf);
				
				FourG_MQTT_PUBTEM(request_id_buf, output_buffer,	/*利用request_id配置临时发布主题*/
								  sizeof(output_buffer));
				
				memset(buf,0,sizeof(buf));							/*组合回复报文并发送*/
				OneNet_CmdRequestBuf(buf, output_buffer); 
				
				FourG_TxData(buf, 256);								/*发送响应信息至云端*/
			}
		}

上面的代码实现之后的效果:

单片机运行,4G模块重启成功,上传数据成功,然后华为云显示在线,

这时下发一个命令试试,:

这样就好了,如果单片机没有回复华为云,这里会显示设备没有回复响应。

九、华为云的数据保存

因为我这篇文章主要的介绍内容是单片机与华为云之间的数据传输,至于数据上传之后怎么用怎么存储,这里就不多介绍。如果想获取历史数据,可以用下面这个方法:

可以在云端运行日志中找到你上传的数据,你需要开通云端运行日志,当然是免费的,最多5000条数据:

 下载为EXCEL,里面有你想要的数据:

然后这里,设置按什么分隔符分页,最后选择确定,就能把数据分好了,

你如果想把数字单独一列的话,你就选择按冒号:分割

下面的内容是关于主机与从机之间利用LORA传输数据

十、 主机通过LORA轮询从机

把串口1配置为单片机与LORA模块通信的串口,然后在LORA.c.h中实现以下函数和定义变量:

关于LORA的配置:

我用的是正点原子的LORA模块,配置资料我已给出,请参考《ATK-LORA-01无线串口模块用户手册_V1.3》,如果要组一组多从的LORA网,直接把主机LORA的“模块地址”配置为65535,工作模式配置为一般模式,空中速率配置为9.6k,从机只需要保证空中速率,工作模式,发送状态和主机一致就可以了。

这样配置完之后,首先,主机的单片机向串口1发送任何内容,LORA直接广播出去,从机LORA发送的任何内容,主机直接在串口1中断中收到一样的内容。

从机也是一样,单片机向串口发什么,LORA就发什么,然后主机会监听到。

然后主机main.c就是我最终的代码了:

主要增加的有:

Timer_Init();    定时器,定时问询LORA,也就是主机每隔5分钟,问询从机,从机上传数据,主机判断有数据进来了且数据接收完毕,主机把数据上传到华为云。

OLED_Show(LoraArrRx);在OLED屏幕上展示数据,并有翻页功能

在Look_LOADSTA(FourG_BUF);函数中,如果云端下发命令了,解析出LOAD是true还是false,是true就把开指令下发到从机,反之类似,代码如下:发送8次0xAA代表只让从机返回信息,发送0xBB代表让从机打开设备,发送0xCC代表让从机关闭设备(0xAA  0xBB 0xCC是我自己规定的,你也可以发别的,只有你从机代码里约定好就行)

注意:如果想同时控制N个从机,并指定某一个从机上的某一个设备的开启与关闭,原理是一样的,只不过你不能只发AA BB CC了,你需要自定义某个位代表什么意思,从机接收到这一串消息后,根据每个位不同的值来做出不同的动作。

		// 判断是否是 true 或 false
		if (strncmp(start_pos, "true", 4) == 0) 
		{
			Load_Set(1);
			for(i=0;i<8;i++){Lora_SendByte(0xBB);}		/*向从机发送开负载指令*/
			return 1;
		} 
		else if (strncmp(start_pos, "false", 5) == 0) 
		{
			Load_Set(0);
			for(i=0;i<8;i++){Lora_SendByte(0xCC);}		/*向从机发送关负载指令*/
			return 0;
		} 
		else 
		{
			printf("Error: Invalid LOAD value\r\n");
		}
#include "stm32f10x.h"   
#include "Delay.h"
#include "FourG.h"
#include "usart3.h"	
#include "CmdProcessing.h"
#include "Load.h"
#include "stm32f10x.h"   
#include "CmdProcessing.h"
#include "OLED.h"
#include "Delay.h"
#include "LORA.h"
#include "FourG.h"
#include "CRCMODBUS.h"
#include "key.h"
#include "OLED_Show.h"
#include "usart3.h"
#include "Timer.h"


/*定义初始变量,这些变量的值直接被上传到华为云*/
float TEMP = 0.0, HUMI = 0.0, WDSPEED = 0.0, WDDER = 0.0;
float CELLU = 0.0 ,PVPOWER = 0.0, WDPOWER = 0.0, PVU = 0.0;
float PVI = 0.0, WDU = 0.0, WDI = 0.0, CELLI = 0.0, CELLPOWER = 0.0;
uint32_t LUX = 0, RAD = 0;
uint8_t load = 0;/*负载默认是关闭状态*/

uint8_t LoraArrRx[100]; 				/*Lora接收数据缓存区*/
uint8_t LoraDataNum = 0;				/*Lora接收数据的下标*/
uint8_t Lora_RxFlag = 0;				/*Lora的串口接收数据标志位*/
uint8_t Time_Out = 0;					/*定时时间到 标志位*/
uint8_t mode = 0;						/*OLED屏幕翻页 0,1,2,3页*/
int main(void)
{
	uint8_t LOADSTATION = 128;			/*记录负载开关指令:0为关,1为开,128为错误*/
	
	char request_id_buf[256];			/*缓存request_id*/
	char output_buffer[256];			/*缓存用于存储组合好的主题*/
	unsigned char buf[512];				/*缓存温湿度上报数据*/
	char buff[256];						/*缓存回复命令主题的AT指令*/
	
    memset(request_id_buf,0,sizeof(request_id_buf));
	memset(output_buffer,0,sizeof(output_buffer));
	memset(buff,0,sizeof(buff));
	memset(buf,0,sizeof(buf));
	memset(LoraArrRx,0,sizeof(LoraArrRx));

	Lora_Init(9600);					/*串口1:LORA串口初始化*/
	FourG_Init(115200);					/*串口2:4G串口初始化*/
	Usart3_Init(115200); 				/*串口3:调试串口初始化*/
	OLED_Init();						/*OLED屏幕初始化*/
	Load_Init();						/*负载状态初始化*/

	OLED_ShowString(1, 1, "MQTT Init...");
	FourG_MQTT_Config();							/*配置4G模块连接华为云服务器*/
	OLED_ShowString(2, 1, "MQTT OK !");
	
	OLED_ShowString(3, 1, "4G Init...");
	printf("Waiting for restart !\r\n");
	Delay_s(10);									/*等待10秒,让4G模块重启*/
	printf("Restart Successfully !\r\n");
	OLED_ShowString(4, 1, "4G OK !");
	
	Timer_Init();									/*重启后再开始计时,定时器初始化*/

	OLED_Clear();
	OLED_Show(LoraArrRx);							/*屏幕展示数据*/
	while (1)
	{
		
		/*获取温湿度等信息并上传*/
		if(Time_Out == 1) 							/*定时时间到,定时时间为Per_Time秒*/
		{
			uint8_t i = 0;							/*向1号LORA从机发送问询信息 0xAA*/
			for(i=0;i<8;i++){Lora_SendByte(0xAA);}
			printf("Lora_SendByte ok \r\n");
			if(Lora_RxFlag == 1) 					/*如果LORA接收完成*/
			{
				OLED_Show(LoraArrRx);				/*屏幕展示数据*/									
				DATA_Read(LoraArrRx);				/* 将数据按对应格式存放在定义好的变量中*/
				
				memset(buf,0,sizeof(buf));			/*TEMP等变量值已经为最新值,下面上传温湿度信息*/
				OneNet_FillBuf(buf);				/*组合有效载荷,并缓存到buf[]中*/
				
				FourG_TxData(buf,512);				/*直接将512个数据全部上传,避免长度计算错误,MQTT会自动截取有用信息*/
//				memset(LoraArrRx,0,sizeof(LoraArrRx));  /*为了展示从LORA处获得的数据,每次上传云端后可不清零*/
				Lora_RxFlag = 0;
			}
			Time_Out = 0;							/*清除定时器标志位*/
		}
		

		/*处理云端命令*/
		if(SEEK_FourGRxOK() == 0)					/*如果4G完成云端命令的接收*/
		{
			printf("FourG_BUF: %s\r\n", FourG_BUF);	/*打印云端命令*/
			
			LOADSTATION = Look_LOADSTA(FourG_BUF);	/*将单片机开关指令发送至从机*/
			
			if(LOADSTATION != 128)
			{
				Look_REID(request_id_buf, FourG_BUF);/*提取request_id到request_id_buf中*/
				
				printf("request_id_buf: %s\r\n", (char *)request_id_buf);
				
				FourG_MQTT_PUBTEM(request_id_buf, output_buffer,	/*利用request_id配置临时发布主题*/
								  sizeof(output_buffer));
				
				memset(buf,0,sizeof(buf));							/*组合回复报文并发送*/
				OneNet_CmdRequestBuf(buf, output_buffer); 
				
				FourG_TxData(buf, 256);								/*发送响应信息至云端*/
			}
		}
	}
}	

十一、从机接收主机问询命令并回复信息

从机也是先定义好串口1与LORA模块之间的串口数据传输。

逻辑是,如果从机收到了主机发来的命令,如果是8个0xAA,则把收集到的数据通过LORA发送出去,如果是0xBB就开启负载,如果是0xCC就关闭负载。

从机代码里的逻辑是这样的,采集到的数据被存放在DataArr[]中:

		/*如果LORA接收到命令*/
		if(Lora_RxFlag == 1)
		{
			/*0xAA表示仅发送数据回主机*/
			if(LoraArrRx[0] == 0xAA && LoraArrRx[7] == 0xAA)
			{
				Lora_SendArray(DataArr, 38);
				for(i=0;i<20;i++){LoraArrRx[i] = 0;}	/*清除LORA接收指令缓存区*/
				Lora_RxFlag = 0;						/*清除LORA接收指令标志位*/
			}
			/*0xBB表示打开负载*/
			if(LoraArrRx[0] == 0xBB && LoraArrRx[7] == 0xBB)
			{
				OLED_ShowString(4, 11, "L_ON ");
				Load_Set(Load_ON);
				for(i=0;i<20;i++){LoraArrRx[i] = 0;}	/*清除LORA接收指令缓存区*/
				Lora_RxFlag = 0;
			}
			/*0xCC表示关闭负载*/
			if(LoraArrRx[0] == 0xCC && LoraArrRx[7] == 0xCC)
			{
				OLED_ShowString(4, 11, "L_OFF");
				Load_Set(Load_OFF);
				for(i=0;i<20;i++){LoraArrRx[i] = 0;}	/*清除LORA接收指令缓存区*/
				Lora_RxFlag = 0;
			}
			Lora_RxFlag = 0;
		}

需要注意的是:

1.从机发送出去的信息,其它从机是收不到的,因为他们地址不一样,主机是收得到的,因为主机地址是65535,可以收到任何模块的信息(只要他们空中速率一样)。

2.如果2个从机LORA同时给主机发送消息怎么办?这种状况是不可能发生的,因为实际的代码的逻辑是,主机到时间后,先问询从机1,从机1返回消息或在规定文献次数内未返回信息,主机才问询从机2。

下面是关于传感器数据采集的介绍

十二、RS485总线采集多个传感器数据

之后在另一篇文章介绍。

十三、IIC读取INA226电压电流监测模块数据

我的另一篇博客有详细介绍:

INA226采集电压电流功率

十四、风速模拟量的采集与转换ADC

这个比较简单,就是AD转换,不多介绍

总结

其实有人云4G模块还可以直接用透传模式直接连接有人云官方平台,实现数据上传和存储

也可以快速实现一些数据展示:

不过这各有利弊,如果仅仅是做个毕设或者自己的小项目,用有人云官网的就够了。

另外,MQTT的内容还有很多,这里只是实现了最简单的功能,入个门,以后具体使用的时候还要深入学习。

建议:

如果是想实现MQTT,建议仔细阅读MQTT中文使用说明,

如果想实现LORA组网,建议详细阅读LORA的用户手册。

### 大夏龙雀蓝牙 BT24 连接配置解决方案 对于大夏龙雀设备遇到的蓝牙 BT24 问题,通常涉及硬件初始化设置以及软件层面的参数调整。针对此类连接配置问题,建议按照特定的方法排查并解决问题。 #### 设备兼容性确认 确保所使用的外部蓝牙模块或配件支持 BT2.4 协议版本,并且与大夏龙雀系列产品的接口标准相匹配[^1]。不兼容可能导致无法正常建立通信链路或者频繁断连现象。 #### 驱动程序更新 安装最新的官方驱动可以有效改善因固件缺陷引起的各类异常状况。访问制造商网站下载适用于当前操作系统环境的大夏龙雀专用蓝牙驱动包,并依照说明文档完成升级操作[^2]。 #### 参数优化设定 通过修改注册表项或其他方式调整系统内部关于无线传输速率、功率等级等影响因素来增强信号稳定性。例如,在 Windows 平台上可利用命令提示符执行如下指令查看现有属性: ```powershell Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\bthport\Parameters" ``` 同时注意关闭不必要的后台应用程序服务进程以免占用过多资源而干扰正常的蓝牙功能运作[^3]。 #### 故障排除指南 当上述措施未能彻底根治故障时,则需进一步深入分析具体表现形式及其触发条件。收集日志文件提交给技术支持团队获取更专业的指导;另外也可以尝试恢复出厂默认设置重新激活服务端口监听状态以期恢复正常工作模式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值