- 项目描述:该系列记录了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}