前言:
之前物联网课设时就想用CC2530单片机和ESP8266-01S来实现数据上传和远程控制,当时在网上也找了很多资料,无果。本来是不想再碰这个的,由于我同学做毕设需要用到,所以再努力了一把,也终于解决了困扰很久的问题。先分享给大家,希望可以帮到正在做课设或是毕设的各位!!!
功能介绍:
使用CC2530单片机实现数据上传和远程控制的功能。大概流程如下:终端采集数据无线发送到协调器,协调器再将数据上传到OneNET云平台,在云平台上可以下发命令控制LED灯。
前期准备:
1、有OneNET的账号
2、刷入 OneNET 提供的 ESP8266 固件
刷固件的工具,提取码:8266
需要下载的固件位置:
下载注意:
硬件准备:
1、两块CC2530板子
2、WIFI模块esp8266-01s或者esp8266-01
3、USB转串口模块,用于烧录固件
4、HC-SR04超声波测距模块,用于终端采集距离数据(也有使用温湿度的源码)
模块接线:
ESP8266-01S: TX–P0_5 RX–P0_4 VCC–3.3V GND–GND
ESP8266-01: TX–P0_5 RX–P0_4 VCC–3.3V GND–GND EN–3.3V
HC-SR04: TRIG — P1_1 ECHO — P0_6 VCC --5V
演示图片:
1、实物图
2、协调器串口输出数据
3、设备处于在线状态
4、终端采集的数据Distance
5、下发命令
在设备列表中的详情/数据流/更多可以找到下发命令
发送字符串LED1翻转P1_0引脚处的LED灯,发送LED2翻转P2_0处的LED灯
6、设备响应下发的指令
代码实现:
SampleApp.c文件
/*********************************************************************
超声波引脚连接说明 andy
TRIG ---- P1_1
ECHO ---- P0_6
VCC ---5V
*/
#include "OSAL.h"
#include "ZGlobals.h"
#include "AF.h"
#include "aps_groups.h"
#include "ZDApp.h"
#include "stdio.h"
#include "string.h"
#include <stdlib.h>
#include "Hcsr04.h"
#include "SampleApp.h"
#include "SampleAppHw.h"
#include "OnBoard.h"
/* HAL */
#include "hal_lcd.h"
#include "hal_led.h"
#include "hal_key.h"
#include "MT_UART.h"
#include "MT_APP.h"
#include "MT.h"
#include "hal_uart.h" /* 串口头文件 */
/*
需要修改的地方:WIFI名字、密码、OneNET的登录信息
*/
//参数分别代表WIFI名称、密码
#define WIFI_INFO "AT+CWJAP=XIAOCHUN,3118003167\r\n"
//参数分别代表设备ID、产品ID、鉴权信息
#define OneNET_INFO "AT+IOTCFG=879125822,486413,0713\r\n"
//SampleApp_ClusterList就是一个簇,
//包含了命令SAMPLEAPP_PERIODIC_CLUSTERID、SAMPLEAPP_FLASH_CLUSTERID
const cId_t SampleApp_ClusterList[SAMPLEAPP_MAX_CLUSTERS] =
{
SAMPLEAPP_PERIODIC_CLUSTERID,
SAMPLEAPP_FLASH_CLUSTERID
};
/*简单设备描述符--使用一个网络地址可以描述一个节点,在一个节点上有很多端口
故用简单描述符来描述一个端口*/
const SimpleDescriptionFormat_t SampleApp_SimpleDesc =
{
SAMPLEAPP_ENDPOINT, //端口号,这里是20,可以在1-240随便取
SAMPLEAPP_PROFID, //应用规范ID
SAMPLEAPP_DEVICEID, //应用设备ID
SAMPLEAPP_DEVICE_VERSION, //应用版本号
SAMPLEAPP_FLAGS, //保留
SAMPLEAPP_MAX_CLUSTERS, //输入簇包含的命令个数,这里是2
(cId_t *)SampleApp_ClusterList, //输入簇列表
SAMPLEAPP_MAX_CLUSTERS, //输出簇包含的命令个数
(cId_t *)SampleApp_ClusterList // 输出簇列表
};
endPointDesc_t SampleApp_epDesc; //端口描述符
uint8 SampleApp_TaskID; //任务ID
devStates_t SampleApp_NwkState; //保存节点状态的变量
uint8 SampleApp_TransID; //数据发送序号
afAddrType_t SampleApp_Periodic_DstAddr; //广播
/* 串口基本定义 */
#define MY_DEFINE_UART0_PORT 0 //自定义串口号(0,1);
#define MY_DEFINE_UART1_PORT 1 //自定义串口号(0,1);
#define RX_MAX_LENGTH 20 //接收缓冲区最大值: 20个字节;
uint8 RX_BUFFER[RX_MAX_LENGTH]; //接收缓冲区;
/*********************************************************************
* LOCAL FUNCTIONS
*/
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pckt );//处理终端数据 发送数据到云平台
void SampleApp_SendPeriodicMessage( void ); //消息处理函数
void SampleApp_HandleKeys( uint8 shift, uint8 keys ); //按键事件处理函数
void Uart0_Config(void); //串口0配置函数
void Uart1_Config(void); //串口1配置函数
void Uart0CallBackFunction(uint8 port , uint8 event); //回调函数声明,定义在最后面;
void Uart1CallBackFunction(uint8 port , uint8 event); //回调函数声明,定义在最后面;
/*应用层初始化函数*/
void SampleApp_Init( uint8 task_id )
{
unsigned char tmp[10];
SampleApp_TaskID = task_id; //初始化任务优先级
SampleApp_NwkState = DEV_INIT;//将设备的状态初始化为DEV_INIT,表示该节点没有连接到Zigbee网络
SampleApp_TransID = 0; //将发送数据包的序号初始化为0
SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_Periodic_DstAddr.addr.shortAddr = 0x0000;
//对节点描述符进行初始化
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;
SampleApp_epDesc.task_id = &SampleApp_TaskID;
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;
SampleApp_epDesc.latencyReq = noLatencyReqs;
//用afRegister函数将节点描述符进行注册,注册后才能使用OSAL提供的服务
afRegister( &SampleApp_epDesc );
//注册按键事件
RegisterForKeys( SampleApp_TaskID );
//串口初始化
Uart0_Config();
Uart1_Config();
osal_memset(tmp,0,10);
tmp[0] = HAL_UART_DMA+0x30; //1
tmp[1] = HAL_UART_ISR+0x30; //2
tmp[2] = HAL_UART_USB+0x30; //0
//HalUARTWrite(0, tmp, 6);
HalUARTWrite(0, "\r\nuart ok\r\n\r\n", 18);//串口测试
#if defined ( LCD_SUPPORTED )
HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 );
#endif
}
/*消息处理函数*/
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
(void)task_id; // Intentionally unreferenced parameter
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
//当接收到此终端的无线消息
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
case ZDO_STATE_CHANGE: //协调器不执行定时发送命令
//读取节点的设备类型,如果是终端节点,实现无线数据发送
SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( //(SampleApp_NwkState == DEV_ZB_COORD) ||//协调器不执行定时发送命令
(SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
Init_UltrasoundRanging(); //初始化超声波
//3S后执行SAMPLEAPP_SEND_PERIODIC_MSG_EVT事件
osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
else
{
// Device is no longer in the network
}
break;
default:
break;
}
Delay_ms(10);
osal_msg_deallocate( (uint8 *)MSGpkt );
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
// (setup in SampleApp_Init()). 协调器不执行定时发送函数
if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )
{
SampleApp_SendPeriodicMessage();
//3S后再次执行
osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );
return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
return 0;
}
//协调器收到终端数据 发送数据到云平台
void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
uint16 flashTime;
unsigned char dataBuf[36 ]; //确保数组能够装下发送的内容
unsigned int distance = 0;
switch ( pkt->clusterId )
{
case SAMPLEAPP_P2P_CLUSTERID:
break;
case SAMPLEAPP_PERIODIC_CLUSTERID:
//收到终端数据后向平台发送数据
// pkt->cmd.Data形如:100,为了到到100做如下操作
distance = atoi((char const *)pkt->cmd.Data); //碰到非数字结束转换
sprintf((char *)dataBuf,(char *)"AT+IOTSEND=0,Distance,%d\r\n",distance);
HalUARTWrite(1, dataBuf,30);
break;
case SAMPLEAPP_FLASH_CLUSTERID:
flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
break;
}
}
//终端采集数据 无线发送给协调器
void SampleApp_SendPeriodicMessage( void )
{
unsigned char strBuf[4];
uint16 date=0;
UltrasoundRanging(LoadRegBuf);
date=256*H2+L2-L1-256*H1;
//得到距离
distance=(uint)(date*1.7)/100;
sprintf((char *)strBuf,"%d",distance);
//将strBuf无线发送给协调器
if ( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc,
SAMPLEAPP_PERIODIC_CLUSTERID,
4,
strBuf,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
{
}
else
{
// Error occurred in request to send.
}
}
/*********************************************************************
一些自定义函数
*********************************************************************/
/*串口配置函数*/
void Uart0_Config(void) //函数定义;
{
halUARTCfg_t uartConfig; //定义串口配置结构体变量;
uartConfig.configured = TRUE; //允许配置;
uartConfig.baudRate = HAL_UART_BR_115200;//波特率;
uartConfig.flowControl = FALSE;
uartConfig.flowControlThreshold = 64; //don't care - see uart driver.
uartConfig.rx.maxBufSize = 128; //串口接收缓冲区大小
uartConfig.tx.maxBufSize = 128; //串口发送缓冲区大小
uartConfig.idleTimeout = 6; //don't care - see uart driver.
uartConfig.intEnable = TRUE; //使能中断
uartConfig.callBackFunc = Uart0CallBackFunction; //指定回调函数名;
HalUARTOpen(MY_DEFINE_UART0_PORT , &uartConfig); //打开串口
}
void Uart1_Config(void) //函数定义;
{
halUARTCfg_t uartConfig; //定义串口配置结构体变量;
uartConfig.configured = TRUE; //允许配置;
uartConfig.baudRate = HAL_UART_BR_115200;//波特率;
uartConfig.flowControl = FALSE;
uartConfig.flowControlThreshold = 64; //don't care - see uart driver.
uartConfig.rx.maxBufSize = 128; //串口接收缓冲区大小
uartConfig.tx.maxBufSize = 128; //串口发送缓冲区大小
uartConfig.idleTimeout = 6; //don't care - see uart driver.
uartConfig.intEnable = TRUE; //使能中断
uartConfig.callBackFunc = Uart1CallBackFunction; //指定回调函数名;
HalUARTOpen(MY_DEFINE_UART1_PORT , &uartConfig); //打开串口
}
/*********************************************************************
*
* 函数名: UartCallBackFunction
* 函数功能:串口回调函数,接收到数据时会调用到该函数;
* 传入参数:port:串口号 event:事件
* 返回参数:无
*
*********************************************************************/
static void Uart0CallBackFunction(uint8 port , uint8 event)
{
uint8 RX_Length = 0; //接收到字符串大小;
RX_Length = Hal_UART_RxBufLen(MY_DEFINE_UART0_PORT); //读取接收字符串大小;
if(RX_Length != 0) //有数据存在;
{
//读取串口0数据;
HalUARTRead(MY_DEFINE_UART0_PORT , RX_BUFFER , RX_Length);
//输出串口0接收的数据
HalUARTWrite(MY_DEFINE_UART0_PORT , RX_BUFFER , RX_Length);
}
}
static void Uart1CallBackFunction(uint8 port , uint8 event)
{
uint8 RX_Length = 0; //接收到字符串大小;
static int s_count = 99;
s_count++;
RX_Length = Hal_UART_RxBufLen(MY_DEFINE_UART1_PORT); //读取接收字符串大小;
if(RX_Length==0) return ; //如果长度为0,直接返回
//读取串口1数据;
HalUARTRead(MY_DEFINE_UART1_PORT , RX_BUFFER , RX_Length);
//s_count从0加到100时进入,防止刷爆串口1
if(strstr((char const *)RX_BUFFER,"WIFI DISCONNECT")&&(s_count%100==0))
{
//手动接入AP 后面自动接入
HalUARTWrite(1, WIFI_INFO, strlen(WIFI_INFO));
s_count=0;
}
else if(strstr((char const *)RX_BUFFER,"WIFI GOT IP"))
{
//配置登录信息
HalUARTWrite(1, OneNET_INFO, strlen(OneNET_INFO));
}
else if(strstr((char const *)RX_BUFFER,"Connect:0"))
{
HalUARTWrite(0, "Successful access OneNET\r\n", strlen("Successful access OneNET\r\n"));
}
//下发命令LED1
else if(strstr((char const *)RX_BUFFER,"LED1"))
{
HalLedSet(HAL_LED_1, HAL_LED_MODE_TOGGLE); //状态翻转
}
//下发命令LED2
else if(strstr((char const *)RX_BUFFER,"LED2"))
{
HalLedSet(HAL_LED_2, HAL_LED_MODE_TOGGLE);
}
//继续else if来添加其他命令
//输入到串口0,方便看串口1接收的信息
HalUARTWrite(MY_DEFINE_UART0_PORT , RX_BUFFER , RX_Length);
}
代码需要更改的地方:
代码存在的问题:
1、路由节点编译报错
尝试过网上的解答,没用。原本代码就有这个问题,暂时无法解决。
2、EndDeviceEB-1、2、3、4编译不通过,由于很久之前我觉得这部分多余,移除了这部分代码,下拉框的选项不会删。
代码位置说明:
1、终端采集数据代码 无线发送给协调器代码
2、实现远程控制的代码
追加内容:
鉴于大家平时接触最多的是DHT11温湿度传感器,故另外写了一份DHT11的源码。与超声波测距模块不同,DHT11有温度、湿度两个数据,协调器收到终端的数据后,需要把温湿度值分别拿到,再分别发送到串口1。具体可以看如下代码:
终端采集数据 无线发送给协调器
协调器收到终端数据 发送数据到云平台
如果出现温湿度读取数值为0的情况,可能是你的传感器和我的温湿度代码不匹配,建议可以另找合适温湿度程序。
最后:
需要源码的可以自行下载。代码下载链接
下载操作: