STM32与ESP8266 WiFi模块结合机智云平台开发实战指南
来源:新闻资讯 /
时间: 2024-11-23
概述
本文介绍在机智云物联网云平台上实现基于STM32的WiFi通信功能,即通过WiFi发送指令,使STM32能够完成特定的操作。具体介绍如何通过WiFi控制LED灯的亮灭,更多复杂功能可自行拓展。
这里将使用一个WiFi模块来连接单片机,在WiFi模块中烧写固件,同时在单片机中移植所需的代码,最终实现所需的功能。
硬件准备
- 任意一种STM32单片机
- 一个WiFi模块:推荐使用ESP-07S、ESP-12S或者ESP-12F,这是机智云平台开发文档中推荐的安信可科技开发的WiFi模块。由于这三种WiFi模块在网上售卖的都是无引脚的,所以需要在同一家网店额外购买引脚并自行焊接。当然了,机智云平台的开发文档中还说明了其他厂商WiFi模块的使用,这里不作详细介绍,只介绍安信可ESP8266系列。按理来说除了ESP-07S、ESP-12S或者ESP-12F,其他WiFi模块也可以,但本人没有试过,如果使用其他安信可ESP8266系列的WiFi模块,注意其Flash大小,只有8Mbit、16Mbit和32Mbit大小的才行,常见的ESP-01S应该是8Mbit的。
软件准备
- keil5 MDK
- 机智云产品调试APP,用于在手机上发送指令来控制STM32(在“开发者中心”-“下载中心”-“机智云产品调试APP”,扫码下载到手机上)
详细步骤
一、创建产品
- 搜索“机智云”,进入官网。
- 在官网点击右上角“开发者中心”,注册,登录。
- 点击创建智能产品。
- 根据需要选择方案。这里举例使用“照明”中的“自定义方案”,点击图中的“灯”。
- 填写产品名称,数据传输方式改为“定长”,点击“创建”。
- 点击“去编辑”以创建数据点。
- 编辑相关信息,由于只需控制LED灯的亮灭,一共两种状态,故选择数据类型为“布尔值”,届时,STM32将收到LED灯的开或者关信息。如需其他功能,可选择枚举(如需要控制灯变为红、绿、蓝等颜色)、数值(如需要给某个变量赋值)、拓展(自定义数据类型)。读写类型的“可写”意味着可以在手机APP上通过WiFi改变变量,“只读”意味着只能在手机APP上显示变量,不能更改。
二、烧录固件
- 说明:将机智云提供的GAgent固件烧录到WiFi模块中,目的是使WiFi模块能够连接WiFi并接收到网络指令。
- 下载烧录工具:在“开发者中心”-“文档中心”-左侧“机智云平台概述”-“设备接入”-“GAgent通讯模组使用教程”-“ESP8266串口烧写说明”,下载模组资料,如图:
- 下载完成后打开得到如下文件,其中,“规格说明书”为三种WiFi模块的说明文档;“模组固件”为要烧写进WiFi模块的固件,提供给Flash为32Mbit的WiFi模块的;“模组日志解码映射文件”另作他用,暂不需要;“烧录工具”即为烧录所需工具。
- 如所用WiFi模块不是SPI Flash为32Mbit的,则需另行下载模组固件,在“开发者中心”-“下载中心”,一般进入“下载中心”即为GAgent固件下载页面,下载如图所示固件包:
- 在之前打开的“ESP8266串口烧写说明”中即有烧写步骤,可按其中步骤烧写,这里不再另行说明,需要注意的是,如果使用的不是SPI Flash为32Mbit的WiFi模块,需要更改相关选项为对应的Flash大小;另外,WiFi模组内部电路有的引脚已经默认上拉,不需要连接,可在“规格说明书”中查看,不确定的也可以直接按步骤说明全部连接。
三、移植代码
(1)文件获取
- 说明:STM32是通过串口和WiFi模块进行通信的,为了STM32能够接收到WiFi模块传递的指令,需要移植所需代码。
- 在“开发者中心”,进入所创建的智能产品中,选择“独立MCU方案”,点击左侧的“MCU开发”,选择硬件平台为“其他平台”,这是为了能将生成的代码包移植到任意一种硬件平台上。填入“Product Secret”,在左上角有显示。然后点击生成“代码包”。生成完毕后点击下载。
- 下载完成后打开得到如下图所示文件:最下面的PDF文档为移植步骤说明,由于其中的步骤并非很详细完整,在这里另外说明移植步骤。
(2)文件转移
- 接下来,开始移植代码。首先,将上图文件中的Gizwits和Utils文件复制粘贴到STM32工程文件中,如下图所示:
- 打开keil5软件,将这两个文件夹添加为STM32工程文件的两个组,如下图中的组Gizwits和组Utils:
- 之后,将这两个组的文件路径添加到头文件路径中,如下图所示:
- 打开上述第3点所示的文件夹中的User文件,有一个main.c文件,打开,复制如下6行代码到STM32的main.c文件中:
- 复制粘贴时按照对应位置放置,如下图所示:
(3)完善代码
①实现串口驱动
#include "stm32f10x.h" // Device header
#include "gizwits_product.h"
void Serial1_Init(void)
{
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA |RCC_APB2Periph_USART1,ENABLE );
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate=9600;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx ;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1 ,&USART_InitStructure);
USART_ITConfig (USART1 ,USART_IT_RXNE ,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd (USART1,ENABLE );
}
void USART1_IRQHandler(void) //USART1中断服务函数
{
uint8_t value = 0;
if(USART_GetITStatus (USART1,USART_IT_RXNE )==SET)
{
value=USART_ReceiveData (USART1);
gizPutData(&value, 1); //用于实现串口数据的接收并且写入协议层数据缓冲区
USART_ClearITPendingBit (USART1,USART_IT_RXNE );
}
}
#ifndef _SERIAL1_H
#define _SERIAL1_H
void Serial1_Init(void);
#endif
②实现定时器
#include "stm32f10x.h" // Device header
#include "gizwits_product.h"
void TIM4_Init(void)
{
RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM4 ,ENABLE );
TIM_InternalClockConfig (TIM4);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up ;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1 ;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
TIM_TimeBaseInitStructure.TIM_Period=72-1;
TIM_TimeBaseInitStructure.TIM_Prescaler=1000-1;
TIM_TimeBaseInit (TIM4,&TIM_TimeBaseInitStructure); //每1ms进入一次中断
TIM_ClearFlag (TIM4,TIM_FLAG_Update );
TIM_Cmd (TIM4,ENABLE );
TIM_ITConfig (TIM4,TIM_IT_Update ,ENABLE );
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2 );
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
}
void TIM4_IRQHandler(void) //定时器TIM4中断服务函数
{
if(TIM_GetITStatus(TIM4, TIM_IT_Update)==1)
{
gizTimerMs(); //实现协议层系统时间的维护
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
}
}
#ifndef _TIM4_H
#define _TIM4_H
void TIM4_Init(void);
#endif
③实现芯片复位函数
__set_FAULTMASK(1);
NVIC_SystemReset();
④实现串口打印驱动
#include "stm32f10x.h" // Device header
#include <stdio.h>
void Serial3_Init(void)
{
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOB ,ENABLE );
RCC_APB1PeriphClockCmd (RCC_APB1Periph_USART3,ENABLE );
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_Init(GPIOB,&GPIO_InitStructure); //配置发送引脚PB10
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
GPIO_Init(GPIOB,&GPIO_InitStructure); //配置接收引脚PB11
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx ;
USART_InitStructure.USART_BaudRate=9600;
USART_InitStructure.USART_WordLength=USART_WordLength_8b ;
USART_InitStructure.USART_Parity=USART_Parity_No ;
USART_InitStructure.USART_StopBits=USART_StopBits_1 ;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;
USART_Init (USART3,&USART_InitStructure);
USART_Cmd (USART3,ENABLE );
}
void Serial_SendByte3(uint8_t Byte) //通过USART3发送一个字节
{
USART_SendData (USART3 ,Byte);
while (USART_GetFlagStatus (USART3 ,USART_FLAG_TXE )==RESET );
}
int fputc(int ch,FILE *f) //printf重定向,需要#include <stdio.h>,作用是打印到串口
{
Serial_SendByte3(ch);
return ch;
}
#ifndef _SERIAL3_H
#define _SERIAL3_H
#include <stdio.h>
void Serial3_Init(void);
#endif
⑤实现配置入网
/*配置入网*/
gizwitsSetMode(WIFI_SOFTAP_MODE); //使用SoftAP方式配置入网
// gizwitsSetMode(WIFI_AIRLINK_MODE); //使用AirLink方式配置入网
// gizwitsSetMode(WIFI_RESET_MODE); //复位
⑥实现下行动作执行
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA ,ENABLE );
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits (GPIOA ,GPIO_Pin_1);
}
void LED_ON(void )
{
GPIO_ResetBits(GPIOA ,GPIO_Pin_1);
}
void LED_OFF(void )
{
GPIO_SetBits (GPIOA,GPIO_Pin_1);
}
#ifndef _LED_H
#define _LED_H
void LED_Init(void);
void LED_ON(void );
void LED_OFF(void );
#endif
⑦实现上行数据采集
- 这里需要将数据采集起来,即根据LED的亮灭更改数据点的值。现在要做的就是完善userHandle()函数,即更改currentDataPoint(数据点)结构体中的变量的值。
- 在gizwits_product.c文件中找到userHandle()函数的定义位置,编写如下代码:
⑧初始化
- 这里需要给数据点中的变量赋一个初始值。
- 在gizwits_product.c文件中找到userInit()函数的定义位置,编写如下代码:
⑨主函数
最后的主函数如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "gizwits_product.h"
#include "common.h"
#include "Serial1.h"
#include "Serial3.h"
#include "TIM4.h"
#include "LED.h"
uint8_t LED_Switch=0;
int main(void)
{
userInit(); //数据点初始化,需用户编写
gizwitsInit(); //完成Gizwits协议相关初始化
Serial1_Init();
Serial3_Init();
TIM4_Init();
LED_Init();
/*配置入网*/
gizwitsSetMode(WIFI_SOFTAP_MODE); //使用SoftAP方式配置入网
// gizwitsSetMode(WIFI_AIRLINK_MODE); //使用AirLink方式配置入网
// gizwitsSetMode(WIFI_RESET_MODE); //复位
while(1)
{
if(LED_Switch==1)
{
LED_ON();
}
else
{
LED_OFF();
}
userHandle(); //用于数据采集,需用户编写
gizwitsHandle((dataPoint_t *)¤tDataPoint);//用于数据处理和上传到机智云平台
}
}
文件有:
四、手机APP测试
- 手机APP可以使用前面要求下载的机智云产品调试APP,也可以另外为这个产品生成一个APP。这里两种都介绍一下。
- 首先,可以打开机智云产品调试APP,点击右上角的“+”号,选择前面的代码中自己指定的配网方式,然后一步步进行。
- 或者,还可以为这个产品创建一个APP,在“开发者中心”,点击“创建应用”(当然,也可以使用微信小程序,这里不再介绍),填写信息,然后点击确认。
- 下面,点开该应用,点击上方的“构建应用”。
- 然后,点击左侧的“构建应用”。
- 之后,点击“构建测试版”。
- 之后就是等待构建完成,然后下载到手机上。
- 最后,回到开发者中心,点开左侧我们创建的产品,然后点击上方的“控制页面”,点击“关联产品”,然后,就可以在已经下载的手机APP上完成配网等操作了,之后就可以在手机上完成通过WiFi控制STM32的功能了。
- 需要注意的是,无论使用哪个APP、哪种配网方式来配网,都需要连接2.4GHz的WiFi,如果连接的是5G的WiFi,那么配网将不成功,注意查看自己所连WiFi是不是5G的。如果没有2.4GHz的WiFi,可以用另一个手机打开热点,在热点设置中将热点设为2.4GHz,然后就可以使用该热点来操作了。
上一篇: java框架在物联网和嵌入式系统中的应用