将STM32作为主控芯片,ESP8266作为外设,利用串口传递信息,通过机智云平台实现STM32与手机之间的数据传输!之所以选择机智云平台,是因为机智云平台相关配套的软件工具非常齐全,而且和正点合作,按照原子哥的精神推出有详细的基于STM32的教程,非常适合对云服务的认知处于一脸懵的人对其进行初步的探索。 其实原子哥的东西很详细,但是总得自己亲身经历一番,才知道这条路上有哪些自己没有注意的坑,同一个地方摔倒一次就好!正点原子以及其他开发板资料下载链接:开发板资料下载链接 准备工作: (1)STM32板子和ESP8266模块必不可少,下载机智云的DEMO APP,链接:机智云DEMO APP (2)ESP8266的固件,链接:GAgent for ESP8266 04020034 (3)串口调试工具,链接:机智云串口调试助手 for win7\win8\win10 v2.3.5 (4)使用MCU代码自动生成工具,自动生成MCU代码,使用方法链接:MCU代码生成工具介绍 (5)这个链接包含乐鑫官方烧录工具,机智云串口调试工具,ESP8266固件,以及原子哥详细的机智云教程的PDF资料;提取码为:txs8:工具 步骤有四: (1)注册机智云,创建新产品,使用MCU代码生成工具自动生成代码; (2)刷ESP8266固件,并在机智云的串口助手上进行调试; (3)将生成的代码移植到自己的工程; (4)联网,调试;
创建产品的过程中要弄明白几个关键要素之间的关系,Product Key,Product Secret,以及数据点所包含的内容! 首先Product Key和Product Secret可以理解为你所创建产品的登录账号和密码,后面自动生成的代码会把这个写进协议里面,这样的话后期把程序烧录进MCU入网之后,手机会通过这个获取你所创建的产品信息! 接下来以最简单的控制LED为例! 比如:实现的目标既可以在MCU开发板控制三个LED的亮灭,又可以在手机上控制,而且在开发板上控制时手机上可以显示当前LED的亮灭状态!那么你可以创建一个产品,然后产品里面有三个数据点!这三个数据点就是指这三个LED!然后三个LED中每一个又包含“亮”“灭”两种状态! 这里的产品就像是用MDK写STM32点亮发光二极管的时候要建立的工程类似!然后因为三个LED有且只有两种状态所以我们设置的数据包类型为布尔值!然后我们希望手机端和开发板上都可以控制所以数据读写类型设置为可写! 关于数据类型和读写类型解释如下图: 关于数据点的其他介绍可以点如下链接:数据点概述 创建后的产品界面和数据点界面如下图:
可以根据自己使用的开发板的不同生成对应的代码,在此生成的是通用版,生成流程如下图: 生成的两个文件回头要移到自己创建的工程中,不需要理解具体的代码流程,只要知道4个特定的API函数,将他们像STM32封装好的库函数一样调用即可!简单介绍一下这四个函数的功能,具体的内容修改会在下文说明,详细的介绍点如下链接:移植说明 (1)void gizwitsInit (void):相关协议的初始化函数; (2)void gizwitsSetMode (uint8_t mode):配置入网方式函数; (3) void gizwitsHandle(dataPoint_t *dataPoint):对当前数据点的内容进行处理和上传; (4)int8_t gizwitsEventProcess(eventInfo_t info, uint8_t data, uint32_t len):事件处理函数,类似于MCU的中断函数,发生了什么事件,对应的处理相应的内容 这部分其实就是一个准备工作,以及建立对机智云的大致的了解,了解了这四个函数的大概描述建立一个概念就行,详细的代码移植会在后面说明!
必须调试!必须调试!必须调试!重要的事情说三遍!因为烧录软件即使提示刷固件成功了,但是不清楚其他的硬件有没有问题,或者刷的固件对不对!这样刷了固件之后盲目的和单片机进行连接,后面无法通信的话就不清楚到底是程序的问题,还是硬件的问题又或者是ESP8266以及其固件的问题了!如下图原子哥的刷固件教程很详细! 我刷的固件是从机智云的官网上下载的最新固件,下载完成之后打开之前准备的串口调试助手! 机智云的串口调试助手有三大主要功能,分别对应不同的场景;以及附带的小工具 (1)串口调试助手模拟MCU: 使用场景:目前手上没有MCU或者MCU程序正在开发又或者验证ESP8266功能是否正常 (2)串口调试助手模拟WIFI模块: 使用场景:目前手上没有WIFI模块,或者调试MCU程序是否正常 (3)双串口通信: 使用场景:MCU程序正常,ESP8266功能正常,但是连接之后无法正常工作,所以用它抓取MCU和ESP8266之间的命令,帮助检查漏洞 (4)小工具:校验与计算;生成二维码;生成Signature 在这里我只使用了第一个功能,因为主要是为了熟悉通过ESP8266上云的流程,初步实现点亮LED就好了,所以代码比较简单,中间没出什么岔子,所以其他的功能没用到! 言归正传:如下图,选择模拟V4MCU,当输入Product key和Product secret之后打开串口,若8266功能正常则会在打印区域打印设备信息! 然后可以通过手机端的DEMO APP发送控制命令,在串口调试助手右边的区域会同步显示自己所建产品数据点的状态!下图1 同样改变串口调试助手上数据点的状态也可以同步到APP端,不过在改变状态之后要记得点上报数据!下图2
如下图,原子哥的教程中将移植过程分成这样的八条,接下来将详细解释 我们能够想到,若想建立手机端和设备端都可以控制且实现实时通信!以点亮一个LED为例,那么整个流程应该是这样的: (1)ESP8266要能够正常的收发数据:因为ESP8266是设备端和云端之间信息传递的桥梁; (2)要入网:因为就算ESP8266正常,上不了网,就像咱们手机正常,没有流量打什么王者; (3)信息的上下传递:我按下一个按键,LED亮了,我要把这个数据上传给云端,然后云端传给APP;同理,我在APP上更改了LED的状态,我要把这个命令通过云下发给MCU让他给LED一个电平! (4)实现MS定时器:和云端,手机端进行通信,肯定得需要一个时间作为基准!协议层是建立在以MS为单位的时间基础上的,所以需要用其他不用的定时器建立一个精准的MS级的定时器,不然通信会出现紊乱 (5)日志打印,设备重启:倘若设备通过云端和手机之间的通信使用一段时间出现问题,那么我们就需要检查设备的运行日志帮助我们找到问题;而且如果更严重的话,需要控制MCU重启。 捋清楚了上面的几步,那么我们就可以挨个实现程序的移植了!
在这里,我们是将ESP8266作为一个串口设备来用!所以要初始化相应的串口,然后在接收中断函数中将通过ESP8266接收到的数据转存起来;而且也要实现将需要发送的数据通过串口发送出去! 串口初始化函数:在这里我是将ESP8266和MCU的串口2连接的,所以初始化串口2
void usart2_init(void) { GPIO_InitTypeDef PA2_3; USART_InitTypeDef usart2; NVIC_InitTypeDef NVIC_initstructrue; //使能串口和GPIO口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //初始化TX端口PA2,RX端口PA3 PA2_3.GPIO_Pin=GPIO_Pin_2; PA2_3.GPIO_Mode=GPIO_Mode_AF_PP; PA2_3.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&PA2_3); PA2_3.GPIO_Pin=GPIO_Pin_3; PA2_3.GPIO_Mode=GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA,&PA2_3); //初始化USART2 usart2.USART_BaudRate=9600; usart2.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; usart2.USART_WordLength=USART_WordLength_8b; usart2.USART_HardwareFlowControl=USART_HardwareFlowControl_None; usart2.USART_Parity=USART_Parity_No; usart2.USART_StopBits=USART_StopBits_1; USART_Init(USART2,&usart2); USART_Cmd(USART2,ENABLE); USART_ClearFlag(USART2,USART_FLAG_TC); USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //初始化USART2中断 NVIC_initstructrue.NVIC_IRQChannel=USART2_IRQn; NVIC_initstructrue.NVIC_IRQChannelPreemptionPriority=3; NVIC_initstructrue.NVIC_IRQChannelSubPriority=4; NVIC_initstructrue.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_initstructrue); }串口接收中断函数:如下文,要注意最后一句【 gizPutData(&r, 1);】这句是将 接收到的数据存到缓冲区中!至于这个函数要执行什么,不必考虑,这是机智云代码生成工具自动生成的API,我们只要知道接受到了数据,然后调用这个函数存起来,就算接收成功!
void USART2_IRQHandler(void) { u8 r; if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET) { r=USART_ReceiveData(USART2); gizPutData(&r, 1); } }串口发送函数:我们知道串口发送的时候其实只要执行usart_senddata然后在while循环里等待发送完成就可以了,但是针对不同的人的不同产品,平台不同,而且就算MCU都一样,ESP8266接的端口可能也不同!所以机智云的协议层在需要发送数据的时候都是调用一个发送函数 uartWrite() 所以我们只要根据自己的设备和端口将数据发送函数写在 uartWrite() 这个函数里面就可以了!如下文编码的第二个 for循环语句,至于为什么要发送0X55,我也不清楚,有知道的麻烦留言告诉我,不胜感激!
int32_t uartWrite(uint8_t *buf, uint32_t len) { uint32_t i = 0; if(NULL == buf) { return -1; } #ifdef PROTOCOL_DEBUG GIZWITS_LOG("MCU2WiFi[M:M]: ", gizGetTimerCount(), len); for(i=0; i<len; i++) { GIZWITS_LOG("x ", buf[i]); } GIZWITS_LOG("\n"); #endif for(i=0; i<len; i++) { USART_SendData(USART2, buf[i]);//STM32 test demo while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); if(i >=2 && buf[i] == 0xFF) { USART_SendData(USART2,0X55); while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); } } return len; }至此,串口收发数据的代码移植完成
配置入网也就是MCU给WIFI模组发送不同的命令以实现不同的功能!只是在这里我们使用它的入网功能而已!这个命令的发送是通过调用 gizwitsSetMode() 这个API函数来实现的,如下图! 这个是自动生成的代码,而我们要做的是在什么情况下,调用这个函数来配置WIFI模组实现相应的功能!如下图 这是一个按键扫描程序,只要我们按下不同的按键就可以调用 gizwitsSetMode() 函数配置WIFI模组实现不同的功能!
在移植这个之前我们要先了解自动生成的代码里面的一个结构体,如下图 这个结构体里面的元素就是我们当时创建的产品里面的数据点,因为我当时只创建了一个数据点,所以这里只有一个元素!在这里生成的代码自动帮我们创建了一个全局变量,如下图! 当我们在设备端实现LED的亮灭时,对应的就会改变这个变量的值,然后上传到云端发给APP!这个上报的过程要调用一个API函数 userHandle(); 同理,当我们在APP端改变这个值的时候也会相应的下发给MCU,然后MCU根据这个值进行相应的动作!数据的下发需要调用gizwitsEventProcess这个API函数! 我们只要对这两个API函数进行修改: 在 userHandle(); 中检测到LED灯状态改变就改变currentDataPoint,然后上报到云端; 在gizwitsEventProcess中检测到currentDataPoint数值改变了就让MCU动作; 代码如下图: 数据上报: 数据下发: 至此,我们能够实现数据的上报和下发过程!
实现MS级定时器,与实现串口接收一样,初始化的时候正常初始化,修改一下定时器中断函数就行了!在定时器中断函数中,触发了定时器中断的时候,调用一下 gizTimerMs(); API函数就行了,至于调用之后,协议怎么处理,我们不需要弄清楚!只要初始化的时候注意TIM_Period和TIM_Prescaler将相应周期控制在MS级!
void time3init() { TIM_TimeBaseInitTypeDef time3inittype; NVIC_InitTypeDef time3nvic; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); time3inittype.TIM_Period=9; time3inittype.TIM_Prescaler=7200-1; time3inittype.TIM_ClockDivision=TIM_CKD_DIV1; time3inittype.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3,&time3inittype); TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); time3nvic.NVIC_IRQChannel=TIM3_IRQn; time3nvic.NVIC_IRQChannelPreemptionPriority=0; time3nvic.NVIC_IRQChannelSubPriority=3; time3nvic.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&time3nvic); TIM_Cmd(TIM3,ENABLE); } void TIM3_IRQHandler(void) { if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); gizTimerMs(); } }日志的打印和设备的重启其实很简单!日志打印用的就是我们经常用的Printf函数,只不过机智云的协议定义了一个变量名GIZWITS_LOG代替了Printf 函数,如下图!所以他们协议调用的是GIZWITS_LOG,但是我们只要实现printf的重定向就好了!这个重定向代码网上很多,就不在赘述! 接下来是设备重启,MCU的重启有专门的代码,我们只要复制到mcuRestart函数里面,让机智云的协议能够调用就可以了!如下图
通过以上的步骤,基本上协议就移植成功了!然后手机端就可以控制开发板上LED灯的状态了,同时,在开发板上控制LED灯的状态也可以同步到手机端显示!如下图,点击LED测试后面的箭头会进入子页面,然后改变SWITCH的值,就可以控制LED灯了,而且在开发板上通过按键改变LED灯的状态,这里也会显示!而且在机智云的开发者中心也可以看到自己创建的产品的设备在线情况!!!至此,移植完毕!