家庭IOT监测之WIFI模块与ONENET平台

本篇目标:能够驱动WIFI模块(ESP8266),并且能够连接物联网平台(ONENET)上传传感器数据与图像

材料准备:

  • stm32f4标准工程:stm32f407自建标准工程(stm32f4标准工程
  • ONENET平台资料:包含WIFI模块的驱动和ONENET平台的连接例程,还有修改后的移植代码(onenet资料
  • 最终工程:移植优化后的最终stm32f407工程(onenet接入修改工程

WIFI模块硬件连接与接口

这里WIFI模块使用的是 安信可ESP-12F ESP8266,附上一张模块的最小系统图:
wifi

ps:模块的REST是复位引脚,可接高电平,也可以接IO口,来控制模块的复位;模块的GPIO0是烧写固件引脚,可以不接,也可以接IO口用作烧写固件使用;模块的TXD和RXD就是串口接口引脚,交叉对应接到STM32F4的引脚上即可,默认波特率115200.

在串口正常通信的情况下,STM32F4与WIFI模块用标准AT命令进行通信,具体AT指令可以参考资料里面的AT指令集。


ONENET平台资料说明

打开材料准备中的ONENET平台资料,对里面的文件夹进行相关说明:

  • onenet资料\onenet开发板资料:这个文件夹中包含了ONENET平台的开发板例程,里面包含了很多连接的例程。
    onenet开发板资料

  • onenet资料\移植相关的文件:这个文件中包含了三部分:
    (1)第一部分为onenet平台EDP连接示例代码;
    (2)第二部分为自己修改的onenet平台EDP连接的模块化代码,因为原来的示例代码是stm32f103的,所以修改成stm32f407的代码,可以直接移植使用,可以自己对比第一部分来看有哪些修改的地方;
    (3)第三部分为onenet平台EDP接入的协议文档,里面介绍了相关指令,数据帧的意义,可以参考着看。
    移植资料

  • onenet资料\移植相关的文件\dev:里面有两个文件夹,wifi文件夹中为驱动WIFI模块(ESP8266)的底层驱动代码;onenet为连接平台的相关应用代码。
    dev文件


向标准工程中添加驱动代码

将下载的 stm32f407标准工程 重命名为 stm32f407_iot ,然后找到工程,用keil打开,可以看到标准的工程,里面包含了PLL倍频、串口USART1重定向到printf、精确delay函数、LED灯闪烁。需要修改的地方包括:

  • stm32f4xx.h文件139行的HSE_VALUE值,修改为自己板载的晶振大小。
  • LED的IO初始化和宏定义需要根据自己的IO口进行修改。

接下来就是往工程里添加WIFI模块和ONENET平台的代码了:

  1. 将准备好的 onenet资料文件夹->移植相关的文件下的dev文件夹复制到工程文件夹 stm32f407_iot 下。
  2. 在keil中点击 这里写图片描述 建立wifi和onenet文件夹,并向里面添加刚才对应文件夹的所有文件,如图:
    添加工程

  3. 点击 这里写图片描述 在C/C++ Include Paths添加wifi文件夹和onenet文件夹的路径。

  4. wifi文件下的 net_device.c 和 net_io.c 是WIFI模块(ESP8266)的底层驱动函数,需要修改的包括RESET引脚、MODE引脚、串口相关的代码。打开net_device.h 和 net_io.h,需要修改的部分都有注释标注出来了,如图:
    net_device.h:
    net_device.h
    net_io.h:
    net_io.h

  5. 在main.c中添加头文件 #include “net_device.h” ,在main函数中添加 NET_DEVICE_IO_Init();函数,如下所示:
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发送连接请求:

  • 在main.c中添加头文件 #include “onenet.h” 和 #include “fault.h”
    删除 while(1) 函数中的代码,并添加如下函数:
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");
            }
        }
    }
}
  • 定位net_device.c第374行中的 WIFI_NAME 和 WIFI_PASSWORD ,这是你需要连接路由器的名称和密码,记得在 net_device.h 中修改宏定义。
  • 编译成功后,烧写进stm32f407,在串口助手观察打印信息,如图成功:
    这里写图片描述
    ps:串口打印了WIFI的状态,包括IP地址,如果笔记本连的是同一个路由器,那么他们肯定在同一个网段上,用指令应该可以ping通:
    ping

连接ONENET平台

在连接ONENET平台之前,需要一些步骤:

  • 需要去官网(中移物联网)注册一个帐号。
  • 登录开发者中心
    这里写图片描述
  • 点击创建产品
    这里写图片描述
  • 填写基本信息,最后的设备接入协议选为“EDP”即可,确认。
  • 立即添加设备,任意添加信息,接入设备即可。
  • 右边部分点击产品概况,找到APIKey,记下来:
    这里写图片描述
  • 右边部分点击设备管理,找到设备ID,记下来:
    这里写图片描述

好了,现在可以编写代码连接ONENET平台了!

  • 向 while(1) 函数中添加登录平台代码,如下:
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");
            }
        }
    }
}
  • 修改结构体 oneNetInfo 的值,里面包含了devID、apiKey,将上面记下来的设备ID->对应devID,APIKey->对应apiKey 替换成自己申请的值:
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) 添加数据发送的代码:
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)中添加变量,让其能够发生变化,方便观察
    /************** 打包数据发送平台 **************/
    //下面代码如上
    //......
}
  • 这时候编译已经可以通过了,但是还是不能发送数据,观察一下代码,需要改变 oneNetInfo.sendData 这个数据请求变量,这个值分别有 SEND_TYPE_OK(代表发送成功,等待)、SEND_TYPE_DATA(代表发送传感器数据点)、SEND_TYPE_HEART(代表发送心跳)、SEND_TYPE_PICTURE(代表发送图片)。
    在这里使用一个1s的定时器来分段发送不同数据,规划一下,每180s为周期:1s的时候发送传感器数据点;每25s发送心跳保持;90s的时候发送图片数据。
  • 在 sys_cfg.c 开始添加定时器相关代码:

(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();
}  
  • 编译烧写,查看串口打印数据:
    打印

  • 登录ONENET平台->开发者中心->自己创建的产品->设备管理->点进自己的设备里面->数据展示,可以发现有了一个新的数据点:
    数据点
    等待一会后,会发现另一个数据点:
    图片数据点

ps:这时候基本就可以上传自己想要的传感器和图片数据了。


优化

在此之前,已经可以完成相关功能了,但是还是需要进行相关的优化,包括断网重连,故障优化等来完善整个系统,提高稳定性,当然这不是必要的。

  • 向while(1)中添加部分代码,如下:
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.c第210行,在后面加上:
OneNet_SendData_Picture(devid, Array, sizeof(Array));
status = SEND_TYPE_OK;    //新添加
  • 定位onenet.c第234行,添加宏定义:
#define PKT_SIZE 1024
#define PKT_NAME "IOT_PIC_TEST"  //新添加,可定义的图片数据名称
  • 定位onenet.c第239行,添加修改部分代码:
char type_bin_head[25];    //新添加
sprintf(type_bin_head, "{\"ds_id\":\"%s\"}", PKT_NAME);    //新修改
  • 定位onenet.c第404行,添加部分代码:
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应用代码的移植和修改,能够上传数据点和图片点,下面有简单的流程图来解析一下发送的过程,和一些重要的函数:
lgt
ps:流程图只展示了大致的过程,更加详细的,比如协议等,需要对照着文档和代码,自己研究一下。


总结:上面的利用stm32f407+WIFI模块(ESP8266)接入ONENET,只使用了最基本的功能,还有很多其他协议方式也可以接入,代码也可能有些未知bug可以优化,但最终我们可以通过web或者手机端的onenet_app进行远程查看上传的数据。

这样就可以为后期上传一些传感器数据做好了准备,之后会写关于上传温湿度数据,以及摄像头图像数据,上面文章有不理解或者错误的地方请谅解,互相学习,共同进步,共勉!

猜你喜欢

转载自blog.csdn.net/q361750389/article/details/79362565