STM32G0+EMW3080+阿里云飞燕平台实现单片机WiFi智能联网功能(三)STM32G0控制EMW3080实现IoT功能

news2024/12/29 15:29:49
  • 项目描述:该系列记录了STM32G0+EMW3080实现单片机智能联网功能项目的从零开始一步步的实现过程;
  • 硬件环境:单片机为STM32G030C8T6;物联网模块为EMW3080V2-P;网联网模块的开发板为MXKit开发套件,具体型号为XCHIP
    MXKit-Base V2.2;
  • 软件环境:STM32需要的软件有STM32CubeMX和STM32CubeIDE;开发IDE为eclipse;MXKit的串口调试工具使用的是putty.exe;
  • 串口指令:串口指令使用的是AT指令; 通信方式使用的是UART
  • 项目过程:本项目采用模块化的形式一步步的实现STM32G0+EMW3080+阿里云实现单片机智能联网功能;第一步先使用MXKit开发板和PC进行通信;第二步是配置阿里云飞燕平台;第三步是MXKit开发板实现配网功能,MXKit和阿里云之间成功通讯;第四步是STM32G0单片机实现和EMW3080的串口通讯;第五步是测试整体的功能;

本节为该项目的第四节,主要任务是实现STM32G030C8T6控制EMW3080实现IoT功能,即STM32G030C8T6控制EMW3080实现配网、断网重连、以及数据的下发、app控制设备等;最终的结果是,单片机上电后,向EMW3080发送配网指令,配网成功后,在云智能app端下发指令能够控制单片机上的LED等开和关;当然也支持wifi断开重连等功能;

经过上一篇文章,STM32G0+EMW3080+阿里云飞燕平台实现单片机WiFi智能联网功能(三)EMW3080完成配网,EMW3080连接到阿里云飞平台,通过串口调试EMW3080已经能成功的进行配网了,所以我们现在要做的就是,让STM32G030C8T6来发送配网指令,完成EMW3080的配网过程,并且在完成配网后,可以向STM32G030C8T6发送和接收数据用于控制设备;

文章目录

  • 一、硬件连接
  • 二、代码实现
  • 笔记

一、硬件连接

STM32G030C8T6和EMW3080的连接原理图如下图所示:
在这里插入图片描述

其中,使用STM32G030C8T6的UART1串口,接到EMW3080的UART串口上,接线如上图所示;然后STM32G030C8T6通过STLINK或JLINK连接到电脑上,便于调试和烧写程序;EMW3080开发板通过自带电源线也连接到电脑的USB端口上用于供电;这样接线部分就接好了;
需要注意的是,STM32G030C8T6我是用的是USART1;EMW3080开发板上有“UART”和“DEBUG”两个区域都由RX和TX,
在这里插入图片描述

我们需要使用UART区域中的RX和TX,而不是DEBUG中的,如果不小心使用了DEBUG中的RX和TX,指令虽然也能发送到EMW3080,但是无法识别;

接线完成后,实物图如下所示:

在这里插入图片描述

二、代码实现

接下来就是在STM32G030C8T6中编写代码实现向EMW3080发送AT指令进行配网,并根据返回的信息判断是否配网成功;待配网成功后,STM32G030C8T6接收云端发下来的指令,并进行响应的控制;本代码示例中通过下发LED等开和关的指令,控制STM32上的灯亮和灭;

整个工程的代码可以从以下链接中下载(https://download.csdn.net/download/AnChenliang_1002/88511568)

下载后的资源可以直接用STM32CubeIDE运行;

下面大致讲解一下代码结构:
在这里插入图片描述
主要的源文件如上图所示,其中我们IoT的功能主要在wilo_wifiMoudule.c中实现;

附上wilo_wifiMoudule.c的完整代码:

#include "wilo_wifiModule.h"

#include "wilo_uart.h"


#define DISCONNECT_TRUE 1
#define DISCONNECT_FALSE 0

extern UART_HandleTypeDef huart1;
extern uint8_t rxBuffer[128];
extern __IO uint8_t receivedIndex;//跟踪接收到的字符的索引
extern uint8_t stringMatched ;//是否接收到完整的字符串
extern uint8_t receivedData[128]; // 全局数组用于存储完整接收到的内容
extern __IO uint8_t receivedLength; // 当前接收到的数据长度,为0时表示未收到数据,大于0时表示收到了数据
extern uint8_t preReceivedLength;//前一次接收到的数据长度
extern const char* atCommands[] ;

// 声明一个设备参数变量
DeviceParameters deviceParams;


void reset_receive()
{
	// 重置接收索引,准备接收下一段内容
	receivedIndex = 0;
	stringMatched = 0;
	receivedLength = 0;
	preReceivedLength = 0;
	memset(receivedData,0,sizeof(receivedData));
}

// 发送指令并等待回复函数
HAL_StatusTypeDef sendCommandAndWait(const char* command, const char* expectedReply)
{

	// 发送指令
	HAL_UART_Transmit_IT(&huart1, (uint8_t*)command, strlen(command));

	// 接收回复
	HAL_UART_Receive_IT(&huart1, &rxBuffer[receivedIndex], 1); 


	while((0 == receivedLength))//如果还未接收到数据,一直等待;直到收到数据
	{
		OLED_ShowString(0,4,"wait response");
	}
	//HAL_UART_Transmit_IT(&huart1, (uint8_t*)"wait!!!!\r\n", 10);
	//OLED_Clear();//OLED清零
	
	while(0 == stringMatched )//如果还没有接收完所有数据,一直等待,直到接收完所有数据
	{
		ReceivedAll();//判断是否接收完所有字符串了
	}
	//HAL_UART_Transmit_IT(&huart1, (uint8_t*)"111\r\n", 5);
	//HAL_Delay(1000);

	//HAL_UART_Transmit_IT(&huart1, (uint8_t*)"received\r\n", 10);
	OLED_Clear();//OLED清零
	OLED_ShowString(0,4,"received:");
	OLED_ShowString(80,4,(u8 *)receivedData);
		// 延时3秒
	//HAL_Delay(3000);
	//replyBuffer = receivedData;

	if (strstr((const char *)receivedData, expectedReply) != NULL)
	{
		// 重置接收,准备接收下一段内容
		reset_receive();

		// 收到期望的回复
		return HAL_OK;
	}

	// 重置接收,准备接收下一段内容
	reset_receive();		
	
	return HAL_ERROR;
}
#if 0
//等待wifi配网成功
HAL_StatusTypeDef WaitConnected()
{
	//uint32_t startTime = HAL_GetTick();
	//uint32_t elapsedTime = 0;

	int Connected = 0;//是否配网完成
	// 持续等待回复,直到收到配网成功的回复;当TimeOut_flag为2时说明超时了
	while ( 1 != Connected)
	{
		HAL_UART_Receive_IT(&huart1, &rxBuffer[receivedIndex], 1);
	    if(0 == stringMatched)//还未接收到数据,一直等待
	    {
	    	OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"000 wait Connect");
			  // 延时3秒
			//HAL_Delay(3000);
	    	while( stringMatched == 0)
	    	{
	    		ReceivedAll();//判断是否接收完所有字符串了
	    	}
	    }
	    if(1 == stringMatched)
	    {
	    	OLED_Clear();//OLED清零
	    	OLED_ShowString(0,0,"received:");
	    	OLED_ShowString(80,0,(u8 *)receivedData);
			  // 延时3秒
			HAL_Delay(3000);

	        if (strstr(receivedData, "ILOPEVENT:ILOP,CONNECTED") != NULL)
	        {
				OLED_Clear();//OLED清零
				OLED_ShowString(0,4,"Connect OK");
				  // 延时3秒
				//HAL_Delay(3000);
			    // 重置接收,准备接收下一段内容
				reset_receive();

		    	Connected = 1;
		    	return HAL_OK;//配网成功
	        }
			// 重置接收,准备接收下一段内容
			reset_receive();

	    }

	    // 更新经过的时间
	    //elapsedTime = HAL_GetTick() - startTime;


	}

	return HAL_TIMEOUT;
}
#endif

//等待wifi配网成功
HAL_StatusTypeDef WaitConnected()
{
	//uint32_t startTime = HAL_GetTick();
	//uint32_t elapsedTime = 0;

	OLED_Clear();//OLED清零
	OLED_ShowString(0,4," waiting Connect");

	int Connected = 0;//是否配网完成
	// 持续等待回复,直到收到配网成功的回复;当TimeOut_flag为2时说明超时了
	while ( 1 != Connected)
	{
		HAL_UART_Receive_IT(&huart1, &rxBuffer[receivedIndex], 1);
	    if(0 != receivedLength)//收到数据了
	    {
	    	//OLED_Clear();//OLED清零
			//OLED_ShowString(0,4,"000 wait Connect");
			  // 延时3秒
			//HAL_Delay(3000);
	    	while( stringMatched == 0)//判断是否接收完数据,如果未接收完,则一直循环,直到接收完
	    	{
	    		ReceivedAll();//判断是否接收完所有字符串了
	    	}
			
			OLED_Clear();//OLED清零
			OLED_ShowString(0,0,"received:");
			OLED_ShowString(80,0,(u8 *)receivedData);
			// 延时3秒
			//HAL_Delay(3000);

			if (strstr((const char *)receivedData, "ILOPEVENT:ILOP,CONNECTED") != NULL)
			{
				OLED_Clear();//OLED清零
				OLED_ShowString(0,4,"Connect OK");
				// 延时3秒
				//HAL_Delay(3000);
				// 重置接收,准备接收下一段内容
				reset_receive();

				Connected = 1;
				return HAL_OK;//配网成功
			}
			// 重置接收,准备接收下一段内容
			reset_receive();

					
	    }
	}

	return HAL_TIMEOUT;
}


// 进入WiFi配网过程的函数
HAL_StatusTypeDef WiFiConfigInit()
{

	HAL_StatusTypeDef status;

	// 发送指令 "AT",直到收到的回复是OK
	status = sendCommandAndWait(atCommands[0], "OK");
	while (status != HAL_OK)
	{
		OLED_Clear();//OLED清零
		OLED_ShowString(0,4,"AT Not OK");
		status = sendCommandAndWait("AT\r\n", "OK");
	}

	OLED_Clear();//OLED清零
	OLED_ShowString(0,4,"AT  OK");
	  // 延时10秒
	//HAL_Delay(10000);

	// 发送指令 "AT+ILOPAWSAP\r\n"
	status = sendCommandAndWait("AT+ILOPAWSAP\r\n", "OK");
	while (status != HAL_OK)
	{
		OLED_Clear();//OLED清零
		OLED_ShowString(0,4,"SWAP  Not OK");
		status = sendCommandAndWait("AT+ILOPAWSAP\r\n", "OK");
	}

	OLED_Clear();//OLED清零
	OLED_ShowString(0,4,"SWAP OK");
	  // 延时10秒
	//HAL_Delay(10000);

	if(HAL_OK ==  WaitConnected())
	{
		OLED_Clear();//OLED清零
		OLED_ShowString(0,4,"Connect OK");
	}
	else
	{
		OLED_Clear();//OLED清零
		OLED_ShowString(0,4,"Connect TimeOut");
		return HAL_TIMEOUT;
	}

	// 配网成功
	return HAL_OK;
}

void DeviceInit()
{
	deviceParams.powerState = 0;
}
/*判断wifi是否断开,返回DISCONNECT 表示wifi断开;返回CONNECT表示wifi处于连接状态*/
uint8_t wifi_isDisconnected()
{
	uint8_t disConnected = DISCONNECT_FALSE;//默认没有断开

	if (strstr((const char *)receivedData, "ILOPEVENT:ILOP,CONNECTING") != NULL)
	{
		OLED_Clear();//OLED清零
		OLED_ShowString(0,4,"wifi disconnect ");
		// 延时3秒
		//HAL_Delay(3000);

		// 重置接收,准备接收下一段内容
		reset_receive();

		disConnected = DISCONNECT_TRUE;//wifi断开
	}	

	return disConnected;
}


void wifi_task()
{
	
	HAL_UART_Receive_IT(&huart1, &rxBuffer[receivedIndex], 1);
	//if(0 == stringMatched)//还未接收到数据,一直等待

	if(0 != receivedIndex)//说明接收到消息了
	{

		//OLED_ShowString(0,4,"Recive date begin");
			// 延时3秒
		//HAL_Delay(3000);
		while( 0 == stringMatched)
		{
			ReceivedAll();//判断是否接收完所有字符串了
		}
	    

		OLED_Clear();//OLED清零
		OLED_ShowString(0,0,"received:");
		OLED_ShowString(80,0,(u8 *)receivedData);
			// 延时3秒
		//HAL_Delay(3000);

		if(DISCONNECT_TRUE == wifi_isDisconnected())//如果wifi断开了
		{
			OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"wifi DisConnect ");
			// 延时3秒
			//HAL_Delay(3000);				
			//while(HAL_OK != WaitConnected());
			
			WaitConnected();//等待wifi重连成功

			OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"wifi recover ");
			// 延时3秒
			//HAL_Delay(3000);		
		}
		else
		{
			OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"wifi parse Task ");
			//HAL_Delay(3000);

			parseWiFiCommand((char *)receivedData);
			
			deviceControl();

			reset_receive();	
					
		}
	}
	{
		OLED_Clear();//OLED清零
		OLED_ShowString(0,4,"wifi connect ");		
	}	
	 

}



void parseWiFiCommand(const char* command)
{
    const char* keyword = "+ILOPEVENT:SETJSON,property,";
    const char* powerstateKeyword = "\"powerstate\":";
    const char* powerstateValue = NULL;

    // 检查指令是否以关键字开头
    if (strncmp(command, keyword, strlen(keyword)) != 0) 
	{
		while(1)
		{
			OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"error 1");
		}
        //return;
    }

    // 定位到powerstate关键字的位置
    powerstateValue = strstr(command, powerstateKeyword);
    if (powerstateValue == NULL) 
	{
        while(1)
		{
			OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"error 2");
		}
        //return;
    }

    // 解析powerstate的值

    powerstateValue += strlen(powerstateKeyword);
    int powerstate = *powerstateValue - '0';//将powerstateValue指针所指向的字符转换为整数,并将结果存储在powerstate变量中。*powerstateValue表示取指针所指向的字符,然后通过减去字符'0'的ASCII值,实现将字符转换为对应的整数值。

    // 根据powerstate设置state的值
    if (powerstate == 0) {
        deviceParams.powerState = 0;
    } else if (powerstate == 1) {
        deviceParams.powerState = 1;
    } else {
        while(1)
		{
			OLED_Clear();//OLED清零
			OLED_ShowString(0,4,"error 3");
		}
    }
}


void deviceControl()
{
	if(0 == deviceParams.powerState)
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);//灯灭
	else
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);//灯亮		
}

// 发送数据
void sendWiFiData(const char* paramName, const char* paramValue) {
  // 构建发送数据的格式
  //sprintf(txBuffer, "+ILOPEVENT:SETJSON,%s,%d,{\"%s\":%s}\r\n", paramName, strlen(paramValue), paramName, paramValue);
  
  // 在这里实现串口发送功能,将txBuffer中的数据发送出去
  // 例如:HAL_UART_Transmit(&huart1, (uint8_t*)txBuffer, strlen(txBuffer), HAL_MAX_DELAY);
}

笔记

记录几个开发中的细节:

1、单片机向wifi模块发送指令 AT+ILOPAWSAP\r\n进行配网
2、当单片机收到wifi模块返回的信息中,包含ILOPEVENT:ILOP,CONNECTED时,说明配网成功
3、当单片机收到wifi模块返回的信息中,包含ILOPEVENT:ILOP,CONNECTING时,说明wifi已经断开,正在重连
4、云端向EMW3080发送的控制指令,也就是单片机需要解析的指令,格式如下(以参数“开关状态”为例):

+ILOPEVENT:SETJSON,property,16,{"powerstate":0}
+ILOPEVENT:SETJSON,property,16,{"powerstate":1}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1181192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chrome安装Vue Devtools插件以及无法添加来自此网站的应用问题

chrome安装Vue Devtools插件以及无法添加来自此网站的应用问题 问题背景 Chrome Vue插件是一款可以帮助前端开发人员在浏览器中实时查看和编辑Vue组件和数据的工具。给前端开发人员提供了极大的便利。本文将介绍Chrome浏览器中如何安装Devtools插件。 问题分析 &#xff08…

SpringBoot上传与下载文件

使用SpringBoot的虚拟路径映射。 Config中的类 import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import o…

iconfont 图标使用

(1)在阿里巴巴矢量图标库注册账号,然后让同伴拉你进项目iconfont-阿里巴巴矢量图标库 (2) 点击资源管理 - 我的项目 - 可以看见该项目的图标了 (3)点击上传图标至项目 - 可以上传图标 使用图标 &#x…

使用高德地图获取定位报错:INVALID_USER_SCODE(无效用户代码)

1. 确定自己已经申请了相关应用的key&#xff0c;这里有两个key 一个是应用的Key&#xff0c;一个是安全密钥 2. 应用key一般是发送请求的是后通过key"xxx"发送 3. 安全密钥要在head里加入进去 <script type"text/javascript">window._AMapSecuri…

CocosCreator使用物理引擎和回调

在2d中开启碰撞需要在项目设置–功能裁剪–2D物理系统【选择 基于Box2D的2D物理系统】 如果想要两个物体碰撞&#xff0c;则需要添加刚体和碰撞【&#xff01;&#xff01;重点】 设置刚体类型 可以设为四种&#xff0c; Static 静态刚体&#xff0c;零质量&#xff0c;零速度…

《微服务架构设计模式》之三:微服务架构中的进程通信

概述 交互方式 客户端和服务端交互方式可以从两个维度来分&#xff1a; 维度1&#xff1a;一对一和多对多 一对一&#xff1a;每个客户端请求由一个实例来处理。 一对多&#xff1a;每个客户端请求由多个实例来处理。维度2&#xff1a;同步和异步 同步模式&#xff1a;客户端…

如何评价现在的CSGO游戏搬砖市场

如何评价现在的csgo市场&#xff1f; 其实整个搬砖市场&#xff0c;现在已经变得乌烟瘴气&#xff0c;散发着“恶臭”。我个人非常鄙视那些虚有其表&#xff0c;大小通吃的做法&#xff0c;那些甚至连搬砖数据都看不懂的人&#xff0c;也出来吹嘘着“实力强大&#xff0c;经验丰…

[架构之路-244]:目标系统 - 设计方法 - 软件工程 - 软件开发方法:结构化、面向对象、面向服务、面向组件的开发方法

目录 前言&#xff1a; 一、概述: 软件聚合的程度由简单到复杂 二、主要开发方法详见 2.1 结构化的开发方法 2.2 面对对象的开发方法 2.3 面向服务的开发方法 2.4 面向组件的开发方法 三、不同开发方法比较 3.1 结构化开发方法 3.2 面向对象(OOP)开发方法 3.3 面向服…

电源模块测试科普:如何检测电源模块的功率?测试方法是什么?

电源模块功率的重要性 功率是电源模块的一个重要指标。电源模块功率描述了电源的输出能力&#xff0c;即电源模块为电路或设备提供电能的大小&#xff0c;通常用来衡量电源模块的性能和效率。 电源功率直接影响着电子设备的性能和功能。电源模块功率越高&#xff0c;提供的电能…

模块化之CJS, AMD, UMD 和 ESM

[[toc]] 模块化优点 防止命名冲突代码复用高维护性CJS, AMD, UMD 和 ESM 历史 ES6之前,JS一直没有自己的模块体系后来社区出现了CommonJS和AMD,CommonJS 主要用于服务器(Node)AMD 主要用于浏览器ES6引入了ESM到此,JS终于有了自己的模块体系,基本上可以完全取代CJS和AMD…

TCP流量控制和拥塞控制

文章目录 流量控制拥塞控制 流量控制 当发送方发送速率大于接收方的处理速率时&#xff0c;接收方会把数据包放入缓冲区&#xff0c;当缓冲区满了&#xff0c;就只能将数据包丢弃。为了应对这种情况&#xff0c;流量控制就可以大展身手了 流量控制的作用对象是发送方和接收方 …

【计算文献解读】ACS Catal.:塑料垃圾回收利用中的均相催化

合理的塑料回收对于解决与塑料垃圾相关的环境挑战至关重要&#xff0c;而在各种回收方法中&#xff0c;化学回收&#xff0c;特别是通过均相催化&#xff0c;有望将塑料垃圾转化为有价值的产品。由于聚烯烃链的结构不均匀性和功能化&#xff0c;聚合物废物对催化循环提出了挑战…

AI人工智能大模型业务到底有多烧钱?

AI大模型业务确实是一个非常烧钱的行业。首先&#xff0c;大模型的训练需要大量的计算资源。 这些模型通常由数百万个参数组成&#xff0c;训练它们需要大量的计算能力和存储空间。这意味着公司需要购买大量的高性能服务器、图形处理单元和存储设备&#xff0c;这些都是非常昂…

Nvidia显卡Failed to initialize NVML Driver/library version mismatch错误解决方案

最近GPT比较火&#xff0c;开始折腾了一下gpu,用来跑项目&#xff1a; https://github.com/OpenTalker/SadTalker 今天运行程序突然发现用不了&#xff0c;经排查应该是由于NVIDIA内核驱动版本与系统驱动版本不一致导致的。 下面简单总结了这个错误的解决方案。 问题复现 查看…

如何规划并新建大数据平台的独立生产域?5步走

一般来说&#xff0c;大数据平台包括以下4类数据生产域——生产生态环境&#xff08;正式生产环境&#xff09;、开发和测试环境、培训和演示环境、灾备环境。各生产域在由平台提供资源、安全、监控、故障恢复等保障的同时&#xff0c;不同的生产域之间还需要严格隔离&#xff…

宏观角度认识递归之 Pow(x,n) 问题

50. Pow(x, n) - 力扣&#xff08;LeetCode&#xff09; 计算 x 的 n 次幂&#xff0c;如果是直接暴力求解的话&#xff0c;会造成计算时间周期过长&#xff0c;所以要从别的角度出发&#xff0c;将幂等数分为两个幂等数相乘&#xff0c;例如&#xff1a;三的八次方&#xff0c…

Python基础入门(19)----Python单元测试基础:unittest模块的基本使用

文章目录 创建测试用例测试套件断言运行测试测试固件Python的unittest模块是基于Java的JUnit框架开发出来的,提供了编写和运行单元测试的工具。这篇文章将介绍unittest模块的基本使用,涵盖创建测试用例、测试套件、断言、运行测试以及测试固件的使用。 创建测试用例 在unit…

货币转换

维护货币汇率 事务代码&#xff1a;OB08 调用BAPI CALL FUNCTION BAPI_EXCHANGERATE_GETDETAILEXPORTINGrate_type Mfrom_curr ls_ekko-waersto_currncy CNYdate sy-datumIMPORTINGexch_rate ls_exch_ratereturn ls_return.ls_zsmm043-header-rmb_price …

python解析xmind统计测试用例/测试点 个数及执行情况

前言&#xff1a;统计的是每个分支最后一个节点的状态 xmind版本 23.0911172 标记打开位置 标记规则如下 解释&#xff1a; res {"total": 0, "pass": 0, "fail": 0, "no_result": 0, "unfinished": 0, "now_fail…

MCU测试科普|如何进行MCU芯片测试,具体流程是什么?

MCU芯片测试系统是一种专门用于检测MCU芯片性能和质量的综合性设备。它通常由硬件和软件两部分组成&#xff0c;硬件包括测试仪器、适配器、测试夹具等&#xff0c;用于连接被测MCU芯片和测试机&#xff0c;实现高效高精度的测试。软件部分通常包括测试程序、测试管理软件等&am…