本篇目标:能够驱动WIFI模块(ESP8266),并且能够连接物联网平台(ONENET)上传传感器数据与图像
材料准备:
这里WIFI模块使用的是 安信可ESP-12F ESP8266,附上一张模块的最小系统图:
ps:模块的REST是复位引脚,可接高电平,也可以接IO口,来控制模块的复位;模块的GPIO0是烧写固件引脚,可以不接,也可以接IO口用作烧写固件使用;模块的TXD和RXD就是串口接口引脚,交叉对应接到STM32F4的引脚上即可,默认波特率115200.
在串口正常通信的情况下,STM32F4与WIFI模块用标准AT命令进行通信,具体AT指令可以参考资料里面的AT指令集。
打开材料准备中的ONENET平台资料,对里面的文件夹进行相关说明:
onenet资料\移植相关的文件:这个文件中包含了三部分:
(1)第一部分为onenet平台EDP连接示例代码;
(2)第二部分为自己修改的onenet平台EDP连接的模块化代码,因为原来的示例代码是stm32f103的,所以修改成stm32f407的代码,可以直接移植使用,可以自己对比第一部分来看有哪些修改的地方;
(3)第三部分为onenet平台EDP接入的协议文档,里面介绍了相关指令,数据帧的意义,可以参考着看。
onenet资料\移植相关的文件\dev:里面有两个文件夹,wifi文件夹中为驱动WIFI模块(ESP8266)的底层驱动代码;onenet为连接平台的相关应用代码。
将下载的 stm32f407标准工程 重命名为 stm32f407_iot ,然后找到工程,用keil打开,可以看到标准的工程,里面包含了PLL倍频、串口USART1重定向到printf、精确delay函数、LED灯闪烁。需要修改的地方包括:
接下来就是往工程里添加WIFI模块和ONENET平台的代码了:
点击 在C/C++ Include Paths添加wifi文件夹和onenet文件夹的路径。
wifi文件下的 net_device.c 和 net_io.c 是WIFI模块(ESP8266)的底层驱动函数,需要修改的包括RESET引脚、MODE引脚、串口相关的代码。打开net_device.h 和 net_io.h,需要修改的部分都有注释标注出来了,如图:
net_device.h:
net_io.h:
int main(void)
{
/* stm32系统配置 */
Sys_Config();
/* WIFI模块IO初始化配置 */
NET_DEVICE_IO_Init();
while(1)
{
LED1_ON;
delay_ms(500);
LED1_OFF;
delay_ms(500);
}
}
编译通过即可,这样就做好初步的移植工作了。
做好了初步工作,就要让WIFI模块连接路由器,并向ONENET发送连接请求:
while(1)
{
if(oneNetInfo.netWork == 0)
{
/*********** 检测WIFI模块状态 ***********/
if(checkInfo.NET_DEVICE_OK == DEV_ERR)
{
if(!NET_DEVICE_Exist())
{
NET_DEVICE_GetWifiIP();
printf("NET Device :Ok\r\n");
checkInfo.NET_DEVICE_OK = DEV_OK;
}
else
{
printf("NET Device :Error\r\n");
}
}
}
}
在连接ONENET平台之前,需要一些步骤:
好了,现在可以编写代码连接ONENET平台了!
while(1)
{
/************** 平台登录初始化相关 **************/
if(oneNetInfo.netWork == 0)
{
/*********** 登录onenet平台 ***********/
if(!oneNetInfo.netWork && (checkInfo.NET_DEVICE_OK == DEV_OK))
{
if(!NET_DEVICE_Init(oneNetInfo.protocol, oneNetInfo.ip, oneNetInfo.port))
{
OneNet_DevLink(oneNetInfo.devID, oneNetInfo.apiKey);
if(oneNetInfo.netWork)
{
printf("Login in Onenet Succeed.\r\n");
}
else
{
printf("Login in Onenet Failed.\r\n");
}
}
}
/*********** 检测WIFI模块状态 ***********/
if(checkInfo.NET_DEVICE_OK == DEV_ERR)
{
if(!NET_DEVICE_Exist())
{
printf("NET Device :Ok\r\n");
checkInfo.NET_DEVICE_OK = DEV_OK;
NET_DEVICE_GetWifiIP();
}
else
{
printf("NET Device :Error\r\n");
}
}
}
}
ONETNET_INFO oneNetInfo = {"25739329", "iCljma3PqyPYAqNdIHpxtS79d60=",
"183.230.40.39", "876",
1,
NULL, 0, 0, 0, 1, 0};
ps:这时候登录ONENET个人开发者中心的设备管理,会发现设备名称旁边的小灰点变绿了,说明WIFI已经连接上了平台:
- - - - - - - - - - - - - - >
当WIFI已经可以连接上ONENET平台的时候,就可以添加代码测试向平台发送数据了,数据包括传感器点数据、图片数据(格式为图片格式,如jpg、bmp等)。
while(1)
{
/************** 打包数据发送平台 **************/
if(oneNetInfo.netWork == 1)
{
switch(oneNetInfo.sendData)
{
case SEND_TYPE_DATA:
OneNet_SendData(FORMAT_TYPE3, NULL, NULL, dataStream, dataStreamCnt);//上传数据到平台
printf("\r\nOnenet Data Ready.\r\n");
break;
case SEND_TYPE_HEART:
OneNet_SendData_Heart(); //心跳检测
printf("\r\nOnenet Heart Ready.\r\n");
break;
case SEND_TYPE_PICTURE:
oneNetInfo.sendData = OneNet_SendData(FORMAT_TYPE2, NULL, NULL, NULL, 0);
printf("\r\nOnenet Picture Ready.\r\n");
break;
default:
break;
}
if (oneNetInfo.sendData != SEND_TYPE_OK)
{
if(NET_DEVICE_CheckListHead())
{
//printf("Wifi Send Data Start.\r\n");
oneNetInfo.sendData = NET_DEVICE_SendData(NET_DEVICE_GetListHeadBuf(),
NET_DEVICE_GetListHeadLen());
NET_DEVICE_DeleteDataSendList();
//printf("Wifi Send Data Ok.\r\n\r\n");
}
}
}
/************** 平台登录初始化相关 **************/
//....
//此段代码与上部分相同,这里不展示
//....
}
ps:可以发现数据结构体dataStream未定义,所以在头文件后面定义一个这个变量,并且建立一个测试变量,添加定义代码:
u16 IOT_DATA_TEST = 0;
DATA_STREAM dataStream[] = {
{"IOT DATA TEST", &IOT_DATA_TEST, TYPE_USHORT, 1},
};
unsigned char dataStreamCnt = sizeof(dataStream) / sizeof(dataStream[0]);
while(1)
{
IOT_DATA_TEST++; //在while(1)中添加变量,让其能够发生变化,方便观察
/************** 打包数据发送平台 **************/
//下面代码如上
//......
}
(1)向 NVIC_Configuration 添加如下代码:
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
(2)添加定时器初始化代码,并在Sys_Config 函数中添加定时器初始化函数,如下:
/***
* 函数名称 : Timer_Configuration();
*
* 函数描述 : 定时器初始化配置;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
static void Timer_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_DeInit(TIM3);
TIM_TimeBaseStructure.TIM_Period = 10000;
TIM_TimeBaseStructure.TIM_Prescaler = (84000000/10000 - 1);
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
} //新添加
/***
* 函数名称 : Sys_Config();
*
* 函数描述 : 系统初始化配置;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
void Sys_Config(void)
{
RCC_Configuration();
GPIO_Configuration();
NVIC_Configuration();
USART_Configuration();
Delay_Configuration();
Timer_Configuration(); //新添加
printf("\r\n\r\n***********STM32 System Config!***********\r\n\r\n");
}
(3)向 net_io.c 中添加定时器中断相关函数,如下:
/************** 额外添加 **************/
#include "sys_cfg.h"
#define LED1_TOGGLE GPIO_ToggleBits(GPIOE,GPIO_Pin_0);
#define LED2_TOGGLE GPIO_ToggleBits(GPIOE,GPIO_Pin_1);
#define NET_TIME_DELAY 180 //180s数据间隔
u16 net_send_time = 0;
void TIM3_IRQHandler(void)
{
//清中断标识
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
//---------------- 中断处理 ------------------//
if(oneNetInfo.netWork == 1)
{
net_send_time++;
if (net_send_time == 1)
{
oneNetInfo.sendData = SEND_TYPE_DATA;
LED2_TOGGLE;
}
else if ((net_send_time % 25) == 0) //每25s发送心跳请求
{
oneNetInfo.sendData = SEND_TYPE_HEART;
LED2_TOGGLE;
}
else if (net_send_time == NET_TIME_DELAY/2)
{
oneNetInfo.sendData = SEND_TYPE_PICTURE;
LED2_TOGGLE;
}
else if (net_send_time == NET_TIME_DELAY)
{
net_send_time = 0;
}
}
LED1_TOGGLE;
OneNet_Check_Heart();
}
ps:这时候基本就可以上传自己想要的传感器和图片数据了。
在此之前,已经可以完成相关功能了,但是还是需要进行相关的优化,包括断网重连,故障优化等来完善整个系统,提高稳定性,当然这不是必要的。
while(1)
{
IOT_DATA_TEST ++;
/************** 平台命令处理**************/
if(oneNetInfo.cmd_ptr)
{
OneNet_RevPro(oneNetInfo.cmd_ptr);
oneNetInfo.cmd_ptr = NULL;
}
/************** 错误处理函数 **************/
if(faultType != FAULT_NONE) //如果错误标志被设置
{
printf("WARN: Fault Process\r\n");
Fault_Process(); //进入错误处理函数
}
/************** 打包数据发送平台 **************/
//下面代码如上
//....
}
OneNet_SendData_Picture(devid, Array, sizeof(Array));
status = SEND_TYPE_OK; //新添加
#define PKT_SIZE 1024
#define PKT_NAME "IOT_PIC_TEST" //新添加,可定义的图片数据名称
char type_bin_head[25]; //新添加
sprintf(type_bin_head, "{\"ds_id\":\"%s\"}", PKT_NAME); //新修改
if(strstr((char *)dataPtr, "SEND OK") != NULL)
{
netDeviceInfo.send_ok = 1;
}
else if (strstr((char *)dataPtr, "WIFI DISCONNECT") != NULL ) //新添加else if 分支
{
printf("WARN: WIFI断开,准备重连\r\n");
checkInfo.NET_DEVICE_OK = DEV_ERR;
oneNetInfo.netWork = 0;
NET_DEVICE_ReConfig(0);
}
else if(strstr((char *)dataPtr, "CLOSE") != NULL && netDeviceInfo.netWork)
{
printf("WARN: 连接断开,准备重连\r\n");
oneNetInfo.netWork = 0;
NET_DEVICE_ReConfig(0);
}
else
NET_DEVICE_CmdHandle((char *)dataPtr);
上面完成了ONENET应用代码的移植和修改,能够上传数据点和图片点,下面有简单的流程图来解析一下发送的过程,和一些重要的函数:
ps:流程图只展示了大致的过程,更加详细的,比如协议等,需要对照着文档和代码,自己研究一下。
总结:上面的利用stm32f407+WIFI模块(ESP8266)接入ONENET,只使用了最基本的功能,还有很多其他协议方式也可以接入,代码也可能有些未知bug可以优化,但最终我们可以通过web或者手机端的onenet_app进行远程查看上传的数据。
这样就可以为后期上传一些传感器数据做好了准备,之后会写关于上传温湿度数据,以及摄像头图像数据,上面文章有不理解或者错误的地方请谅解,互相学习,共同进步,共勉!