(一)数据采集模块
底层-->高层
SHT->SHTData--> SensorCollection-->AtoSensorCollection-->ANTTreenetNode
数据传输模块
主要是AtosRoute
节点 ---------------------------> 基站
AtosRouteC.SendData---------------->AtosRouteC.Intercept
具体结构如下:
1、发送部分
App.AMSend -> AtosRouteC.SendData;(此处APP指节点程序)
command error_t SendData.send(am_addr_t addr, message_t*msg, uint8_t len)
{(中间省略大部分代码)
return call SubSendData.send(addr, msg, len);
}
components newAtosRouteSendC(ATOS_ROUTE_MSG_ID_DATA) as SendDataC;
AtosRouteP.SubSendData -> SendDataC;
AtosRouteP.ReceiveData -> SendDataC;
components newAtosRouteSendP(AR_RESOURCE_ID, MSG_ID);
AMSend = AtosRouteSendP;
commanderror_t AMSend.send(am_addr_t addr, message_t* msg, uint8_t len)
{atomic
{RF_MSG_GET_ROUTE_RESOURCEID(msg)= AR_RESOURCE_ID;
//存储header->route_resource_id
RF_MSG_GET_TYPE(msg)= MSG_ID;
//存储metadata->type
returncall AtosRouteSend.send(addr, msg, len);
}}
components AtosRouteSendCenterC;
AtosRouteSendP.AtosRouteSend->AtosRouteSendCenterC.AtosRouteSend[AR_RESOURCE_ID];
components newAtosRouteSendQueueC(AR_RESOURCE_COUNT);
AtosRouteSendCenterP.SubAtosRouteSend ->AtosRouteSendQueueC;
#include "AtosRoute.h"
generic module AtosRouteSendQueueP()//发送队列,当中仍然接入了更加底层的剖面操作模块、数据包处理操作、无线传输模块等等,在结构叙述中我们将其视作发送模块的底层,具体将在正文中进行分析
但是为了结构的完整我们加入以下几个更加底层的模块
AtosRouteEngineProfileP
AtosRouteEngineProfileTableP(uint32_t FLASH_ADDR)
这两个底层的模块的作用基本就是将数据写入最后的FLASH_ADDR里面了,这里面的语句比较晦涩,直接针对引脚的操作比较多,如果电路设计要进行变更将更多的关注这两个模块,但是本文不对电路设备进行更改,所以这两个模块就不在正文中进行分析赘述了。
整理以上结构如下作为数据发送:
高层-->底层
ANTTreenetNode->AtosRoute->AtosRouteSend->AtosRouteSend(ATOS_ROUTE_MSG_ID_DATA)->AtosRouteSendCenter->AtosRouteSendQueue->AtosRouteEngineProfile->AtosRouteEngineProfileTable(uint32_tFLASH_ADDR)
2、接收部分
module ActiveMessageAddressC {
provides async command am_addr_t amAddress();
provides async command void setAmAddress(am_addr_t a);
}
implementation {
bool set = FALSE;
am_addr_t addr;
async command am_addr_t amAddress() {
if (!set) {
addr = TOS_NODE_ID;
set = TRUE;
}
return addr;
}
async command void setAmAddress(am_addr_t a) {
set = TRUE;
addr = a;
}}//最底层获取地址模块
componentsActiveMessageAddressC;
RfPacketP.ActiveMessageAddress-> ActiveMessageAddressC;
configuration RfReliableMacPacketC
{ providesinterface AMPacket;
(省略)}
implementation
{(省略)
components RfPacketC;
AMPacket=RfPacketC;
SubPacket =RfPacketC;
(省略)}
configuration PlatformMacPacketC
{ providesinterface AMPacket;
(省略)}
implementation
{(省略)
AMPacket =RfReliableMacPacketC.AMPacket;
}
configuration AtosRoutePacketC
{ providesinterface AMPacket;
(省略)}
implementation
{(省略) componentsPlatformMacPacketC;
AMPacket=PlatformMacPacketC;
(省略)}
command boolAtosRouteEngine.forwardDefault(message_t *msg)
{
return (!call AtosRouteEngine.isRoot());
}//当AMPacket.address()与初始值不同时说明已经成功读到了地址
command boolAtosRouteEngine.isRoot()
{
return (call AMPacket.address() ==ATE_PROFILE_ROOT_ADDR);
}//ATE_PROFILE_ROOT_ADDR的值为0x0001,此处用来判断AMPacket.address()是否是初始值
configuration AtosRouteEngineC
{(省略)
providesinterface AtosRouteEngine;
}
implementation
{(省略)
components AtosRouteEngineProfileC;
AtosControl = AtosRouteEngineProfileC;
AtosRouteEngine = AtosRouteEngineProfileC;
(省略)}
configuration AtosRouteC{(省略)}
implementation
{(省略)
componentsAtosRouteEngineC;
AtosRouteP.AtosRouteEngine-> AtosRouteEngineC;
(省略)}
configuration ANTTreenetBaseC{}
implementation
{(省略)
componentsAtosRouteC;
App.Intercept-> AtosRouteC;
(省略)}
整理以上结构如下作为数据接收:
底层->高层
ActiveMessageAddress->RfPacket->RfReliableMacPacket->PlatformMacPacket-> AtosRoutePacket->AtosRouteEngine->AtosRoute->ANTTreenetBase
总体框架
图七:ATOS树状网络软件构架
大致的框架如上图所示,在如上的框架当中,有很大一部分不在我们环境监测这个课题范围之内,都是一些数据格式的转换,还有一些指针的操作,只是为了框架的完整性罗列出来,不在正文当中赘述。而根据以上的框架我们可以做很多事情,根据不同的需求在不同的地方对程序稍加修改就可以实现各种各样的功能。比如当传感器因为老化或者其他原因产生系统误差时,可以再SHTP.nc中加入一段误差补偿的程序。对于一些对极限值要求很敏感的场合,可以再ANTTreenetNodeP.nc当中直接发出动作,简单的如用LED_BLUE_ON直接打开发光二极管,修改相应的电路将发光二极管更换成放大电路和警报器,这样操作虽然增加了成本,但是节约了反应时间,适用于一些对时间精度要求较高的场合。
对于一些运算量较大但要求在基站中直接完成的数据处理项目可以直接修改ANTTreenetBase.nc。根据这些不同的要求,以温湿度传感器为例,在SHTDataP.nc,ANTTreeNodeP.nc,ANTTreeBaseP等程序中加入了一些标签,在正文中讲详细说明,然而在总结与展望当中将举一个笔者认为具有一定普适性数据处理方案并将一些程序加入到这些标签当中。而正文主要分析一些关键的程序和运行结果,实现的功能是以温湿度为例进行实时的环境监测,并不对数据进行处理,相应的实验结果通过串口助手SSCOM32进行监听。
程序分析与设计
基于TinyOS的例程:树状网络通信例程进行修改的编程不同于自己从零开始的编程,需要花更多的时间熟悉整个系统的结构,并在其中找到切入点。因为硬件系统基本没有变化,特别是通信部分,所以在那一部分就比较简略的带过,但是会详细的介绍其中一些变量的含义,这是我们应用时候必须知道的,然而通信模块当中还有一些复杂的数据结构的转换对我们而言是没有太大必要了解甚至换了一套设备之后就完全不同的,那些程序将不作介绍。但是其中AtosRouteSendQueueP.nc文件的算法,在文件传输中的作用比较大,将拿出来进行介绍。然而程序的修改将主要在数据采集部分(对于高时间精度要求的项目而言),以及基站主程序ANTTreenetBaseP.nc,所以这两部分会作详细分析。
SHTP.nc
#include "SHT.h"
module SHTP
{ providesinterface Init;
providesinterface SHT;
uses interfaceTimer
}
implementation
{#define SHT_NOACK 0
#define SHT_ACK 1
#define SHT_MODE_TEMP 0
#define SHT_MODE_HUMI 1
//adr command r/w
#define SHT_STATUS_REG_W 0x06 //000 0011 0
#define SHT_STATUS_REG_R 0x07 //000 0011 1
#define SHT_MEASURE_TEMP 0x03 //000 0001 1
#define SHT_MEASURE_HUMI 0x05 //000 0010 1
#define SHT_RESET 0x1e //000 1111 0
#define DELAY_TICK(n) {tick=(n);while (tick--);}
enum
{
SHT_STATE_NONE,
SHT_STATE_TEMP,
SHT_STATE_HUMI,
};
uint8_t m_state =SHT_STATE_NONE;//工作状态。
uint8_t tick;//延时用计数器单元。
uint8_t m_error =0;//错误标志。
uint16_t m_temperature;//四位16进制温度数据,经过转换可以变成实型。
uint16_t m_humidity;//四位16进制湿度数据,经过转换可以变成实型。
uint8_t m_checksum;//校验码。
uint8_t *p_value;//发送监测数据的指针。
task void readDoneTask();
command error_t Init.init()
{
call WaitTimer.stop();
m_state = SHT_STATE_NONE;
return SUCCESS;
}//初始化。
uint8_t sendByte(uint8_tvalue)//按位输出。
{
uint8_t i, error;
SHT_DATA_OUT;
//#define SHT_DATA_OUT MAKE_IO_PIN_OUTPUT(P1_DIR, 7)
SHT_SCK_OUT;
#define SHT_SCK_OUT MAKE_IO_PIN_OUTPUT(P0_DIR, 4)
//更改时钟输出引脚。
for (i=0x80; i>0; i/=2)
{
if (i & value)
{ SHT_DATA = 1; }
else
{ SHT_DATA = 0; }
SHT_SCK = 1;
DELAY_TICK(3);
SHT_SCK = 0;
}
//按位输出value的值,每位sck的时间周期为3,数据从SHT_DATA口出,SHT_SCK作为时钟信号。
SHT_DATA = 1;
SHT_SCK = 1;
SHT_DATA_IN; //用SHT_DATA位(P0_5)来接收反馈的信号。
error = SHT_DATA;
SHT_SCK = 0;
return error;//返回的信号SHT_DATA位(P0_5)应当为0,否则出错。
}
uint8_t recvByte(uint8_t ack)//按位输入。
{ uint8_t i,val = 0;
SHT_SCK_OUT;
SHT_DATA_OUT;//输出 DATA P0_5和 SCK P0_4的值。
SHT_DATA = 1;//P0_5出高电平。
SHT_DATA_IN;//P0_5输入打开。
for (i=0x80; i>0; i/=2)
{ SHT_SCK = 1;
DELAY_TICK(1);
if (SHT_DATA)
{
val = (val | i);
}
SHT_SCK = 0;
}
//SCK P0_4每输出一个高电平,DATA P0_5读入一位、按位读入SHT_DATA的值到val中。
SHT_DATA_OUT;
SHT_DATA = !ack; //如果ack=1,SHT_DATA输出的就是0 。 SHT_SCK = 1; //clk #9 for ack。
DELAY_TICK(10);
//输出!ack的值,时长为10
SHT_SCK = 0;
SHT_DATA = 1; //release DATA-line。
return val;//返回值为读到的数据。
}
uint8_t start()//通过SHT_DATA、SHT_SCK输出标志电平来表示开始工作。
{ SHT_DATA_OUT;
SHT_SCK_OUT;
SHT_DATA = 1;
SHT_SCK = 0;
//_nop_();
DELAY_TICK(1);
SHT_SCK = 1;
//_nop_();
DELAY_TICK(1);
SHT_DATA = 0;
//_nop_();
DELAY_TICK(1);
SHT_SCK = 0;
DELAY_TICK(3);//_nop_();*3
SHT_SCK = 1;
//_nop_();
DELAY_TICK(1);
SHT_DATA = 1;
//_nop_();
DELAY_TICK(1);
SHT_SCK = 0;
//start电平:
//输出 时间 1 2 3 4 5 6 7 8 -
//DATA P0_5 1 1 0 0 0 0 0 1 1
//SCK p0_4 0 1 1 0 0 0 1 1 0
}
void reset()
// 通信重置:在开始通信之后,至少有9个SCK周期 DATA-line=1。
// _____________________________________________________ ________
//DATA: |_______|
// _ _ _ _ _ _ _ _ _ ___ ___
// SCK :__| |__| |__| |__| |__| |__| |__| |__| |__| |______| |___| |______
{ uint8_t i;
SHT_DATA_OUT;
SHT_SCK_OUT;
SHT_DATA = 1;
SHT_SCK = 0; //初始状态。
for (i=0; i<9; i++) //9个SCK 周期 。
{ SHT_SCK = 1;
DELAY_TICK(1);
SHT_SCK = 0;
DELAY_TICK(1);
}
//输出上面给的重置电平。
start();
//输出start电平。
}
void enableDataInt()
{}
void disableDataInt()
{}//然而这两个函数我也不知道用来干什么。
uint8_t cmdReset()
// 通过软重置方式对传感器进行重置 。
{
reset(); //输出重置电平和开始电平。
m_error +=sendByte(SHT_RESET); //通过按位输出SHT_RESET=0x1E=00011110电平方式对传感器传输重置信号。
return m_error; //当传感器没有反馈信号时认为重置失败。
}
uint8_t cmdMeasure()
// 温湿度读取命令的传达。
{ reset();
start(); //开始传输信号发送。
if (m_state ==SHT_STATE_TEMP)
{ //ADBG(1, "\r\nSHT temp");此处作输出可以测试温度检测信号是否发送。
p_value = (uint8_t*)(&m_temperature);//value指针指向温度。
m_error +=sendByte(SHT_MEASURE_TEMP);//通过按位发送000 0001 1至P0_5即SHT_DATA来向传感器传输测试温度的命令。
}
else
{
//ADBG(1, "\r\nSHThumi");此处作输出可以测试湿度检测信号是否发送。
p_value = (uint8_t*)(&m_humidity);//value指针指向湿度。
m_error +=sendByte(SHT_MEASURE_HUMI);//按位发送000 0010 1至P0_5即SHT_DATA来向传感器传输测试湿度的命令。
}//当传感器没有反馈信号时认为命令发送失败。
if(m_error > 0)
{
post readDoneTask();//对上级发送读取失败的信号。
return m_error;
}
callWaitTimer.startOneShot(SHT_TIMEOUT);//延迟300ms一次。
return m_error;
}
event voidWaitTimer.fired()
//通过定时器触发的方式来向上层传输数据。
{
//ADBG(1, "\r\nWaitTimer.fired state=%d", ADBG_N(m_state));这个输出可以用来测试定时器是否触发。
if(m_state == SHT_STATE_TEMP || m_state == SHT_STATE_HUMI)
{
SHT_DATA_IN;
if(SHT_DATA) m_error += 1; // 读取超时,传感器仍在发送数据,判定出错。
if(m_error > 0)
{ post readDoneTask();
return;
}//出错直接跳转输出出错结果。
//ADBG(1, "\r\nNO error");这个输出可以用来检测传感器传输是否有错误发生,当没有时输出no error。
*(p_value) =recvByte(SHT_ACK); //读入高八位。
*(p_value+1) = recvByte(SHT_ACK); //读入低八位 。 m_checksum = recvByte(SHT_NOACK); //读入校验位 。 if (m_state < SHT_STATE_HUMI)
{ //ADBG(1, "\r\nSHT NEXT");与cmdMeasure中的输出配合,可以用来检测cmdMeasure是否触发。
m_state++;
cmdMeasure();
}//按顺序读温度、湿度。
else
{ post readDoneTask();
}//向上层传递成功取数的信号以及相应的结果。
}
}
task void readDoneTask()
{ error_t result = (m_error > 0) ? FAIL :SUCCESS;
//ADBG(1,"\r\nreadDoneTask");
atomic m_state =SHT_STATE_NONE;//重置m_state。
signal SHT.readDone(result,m_temperature, m_humidity);//像上一层传递读取成功或失败以及相应的结果。
}
command error_tSHT.read()
//可调用的读数命令函数。
{ //ADBG(1, "\r\nSHT state=%d",m_state);
atomic
{if (m_state != SHT_STATE_NONE)
{if (m_state >=SHT_STATE_NONE && m_state <= SHT_STATE_HUMI)
{ return FAIL; }
m_state =SHT_STATE_NONE;
}
m_state =SHT_STATE_TEMP;
}//当数据正在被别的程序读取时直接输出出错。
m_error = 0;
cmdMeasure();//测量。
returnSUCCESS;
}
defaultevent void SHT.readDone(error_t result, uint16_t temperature, uint16_thumidity) {}//在外部编辑的默认事件在这里进行声明。
command voidSHT.calcRealValue(float *temperature, float *humidity, uint16_t raw_temperatue,uint16_t raw_humidity)
//换算4位16进制原始数据为实型数据。
{
/** 转换温湿度数据,具体转换方法请参加SHT11的PDF手册。*/
#define D1 -39.66 /*3.5V */
#define D2 +0.01 /*14bit */
#define C1 -4.0
#define C2 0.0405
#define C3 -2.8E-6
#define T1 0.01
#define T2 0.00008
float RH1; /* 相对湿度中间值。 */
*temperature = D1 + D2 * raw_temperatue;
RH1 = C3*raw_humidity*raw_humidity +C2*raw_humidity + C1; /* 相对湿度非线性补偿。 */
*humidity =(*temperature-25)*(T1+T2*raw_humidity) + RH1; /*相对湿度对于温度依赖性补偿。*/
}
}
对SHTP.nc的分析可以将传感器感测数据底层传输的各个引脚、传输方式(串行双工)等基本特性充分的了解,当硬件系统变更之后可以在这里作出相应的修改。尤其是对于一些可以通过数学方法进行补偿的系统误差可以作出相应的代码写进calcRealValue()函数中,这段代码中用到的库函数将附在附件当中,有重要的地方在文中直接给出注释了。
SHTDataP.nc
module SHTDataP
{ providesinterface SensorData;
usesinterface SHT;
}//向上层提供接口SensorData,对下层使用接口SHT。
implementation
{ uint8_t*m_value;//指向温湿度数据存放地址。
uint8_tm_len;//数据长度。
command error_tSensorData.read(uint8_t* p_value)
{
m_value = p_value;
return call SHT.read();
}//调用SHT的主动读取数据函数对数据进行读取。
event void SHT.readDone(error_t result,uint16_t temperature, uint16_t humidity)
//温湿度数据读取成功,对其进行计算和显示。
{
float temp,hum;
ADBG(900,"\n\n\n1=====T&H GET=====1");
ADBG(900, "\n\ntemperatureData = %04x\n", temperature);
ADBG(900, "humidity Data =%04x\n", humidity);
callSHT.calcRealValue(&temp, &hum, temperature, humidity);
//调用SHT.calcRealValue()函数对温湿度两组四位十六进制数据进行转化,使之成为能够进行数据处理的实型数据。
ADBG(900, "temp =%f\n", temp);
ADBG(900, "hum =%f\n", hum);
ADBG(900,"2=====T&H GET=====2");
/**********************************************************
在这里我们可以处理各种对时间精度要求较高的各种项目数据。
比如说在1标准大气压下防止锅炉(烧开水的)爆炸。
这里就可以加入以下语句:
If(temp>100) LED_BLUE_OFF;
然后把蓝色LED等改接开关电路,这样就能在传感器检测到温度超标的第一时间停止加热从而防止锅炉爆炸。
这类项目一般属于不构成传感网络的传感器环境监测项目。
***********************************************************/
memcpy(m_value, &temperature, 2);
memcpy((m_value + 2), &humidity, 2);
//到这里用memcpy函数将数据从缓存区取到m_value地址中数据才算是开始传输。
ADBG(900,"DATATRANS1(FEEL->)p_value=%x\n\n\n",(int)m_value);
//输出数据此时的地址。
m_len = 4;//温湿度数据长度是定值4个比特。
signal SensorData.readDone(m_value, m_len, result);
//向上层传递数据。
}
default eventvoid SensorData.readDone(uint8_t* p_value, uint8_t len, error_t result) {}//SHT中声明过的默认事件在这里定义为空。
}
通过对这段代码的解析我们不难发现,除了最上层、最底层和极少数中间层次的一些代码比较复杂,算法比较值得推敲,中间这些代码大多数是对已有的功能进行分类和整理,添加一些类似润滑剂的功能,使那些功能适用于特定的场合。甚至此处仅有的一个输出功能也是我自己加上去的。然而这些代码对于整个结构的构成却是极其关键的。下面这段程序更将这个性质体现出来。
SensorCollection
#include"SensorCollection.h"
configurationSensorCollectionC
{provides interfaceSensorCollection;
}
implementation
{
components SensorCollectionP;
SensorCollection = SensorCollectionP;
#ifdefined(ALL_ASO) || defined(ASO_TH)
componentsSHTDataC;//温湿度传感模块。
SensorCollectionP.SensorData[SENSOR_ID_SHT]-> SHTDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_T)
componentsDS18B20DataC;//单线数字温度传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_DS18B20]-> DS18B20DataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_BDT)
componentsBdtempDataC;//单板当前温度传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_BDTEMP]-> BdtempDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_ALCOHOL)
componentsAlcoholDataC;//酒精传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_ALCOHOL]-> AlcoholDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_LIGHT)
componentsLightDataC;//光照传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_LIGHT]-> LightDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_FLUX)
componentsFLUXDataC;//流量传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_FLUX]-> FLUXDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_BLOOD_PRESSURE)
componentsBloodPressureDataC;//血压传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_BLOODPRESSURE]-> BloodPressureDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_ALTITUDE)
componentsAltitudeDataC;//高度传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_ALTITUDE]-> AltitudeDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_HOARE)
componentsHoareDataC;//霍尔传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_HOARE]-> HoareDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_RAIN)
componentsRainDataC;//雨滴传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_RAIN]-> RainDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_FIRE)
componentsFireDataC;//火焰传感器模块。
SensorCollectionP.SensorData[SENSOR_ID_FIRE]-> FireDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_MAX_SOUND)
componentsMaxSoundDataC;//最大分贝传感器。
SensorCollectionP.SensorData[SENSOR_ID_MAXSOUND]-> MaxSoundDataC;
#endif
/*AD ports 输入模拟量(电压大小)。*/
#ifdefined(ALL_ASO) || defined(ASO_AD0)
componentsnew AdcDataC(ADC_AIN0) as AdcDataC0;
SensorCollectionP.SensorData[SENSOR_ID_AD0]-> AdcDataC0;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD1)
componentsnew AdcDataC(ADC_AIN1) as AdcDataC1;
SensorCollectionP.SensorData[SENSOR_ID_AD1]-> AdcDataC1;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD2)
componentsnew AdcDataC(ADC_AIN2) as AdcDataC2;
SensorCollectionP.SensorData[SENSOR_ID_AD2]-> AdcDataC2;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD3)
componentsnew AdcDataC(ADC_AIN3) as AdcDataC3;
SensorCollectionP.SensorData[SENSOR_ID_AD3]-> AdcDataC3;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD4)
componentsnew AdcDataC(ADC_AIN4) as AdcDataC4;
SensorCollectionP.SensorData[SENSOR_ID_AD4]-> AdcDataC4;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD5)
componentsnew AdcDataC(ADC_AIN5) as AdcDataC5;
SensorCollectionP.SensorData[SENSOR_ID_AD5]-> AdcDataC5;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD6)
componentsnew AdcDataC(ADC_AIN6) as AdcDataC6;
SensorCollectionP.SensorData[SENSOR_ID_AD6]-> AdcDataC6;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD7)
componentsnew AdcDataC(ADC_AIN7) as AdcDataC7;
SensorCollectionP.SensorData[SENSOR_ID_AD7]-> AdcDataC7;
#endif
/*AD补偿 ports */
#ifdefined(ALL_ASO) || defined(ASO_AD0_O)
componentsnew AdcOffsetDataC(ADC_AIN0) as AdcDataC0_O;
SensorCollectionP.SensorData[SENSOR_ID_AD0_O]-> AdcDataC0_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD1_O)
componentsnew AdcOffsetDataC(ADC_AIN1) as AdcDataC1_O;
SensorCollectionP.SensorData[SENSOR_ID_AD1_O]-> AdcDataC1_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD2_O)
componentsnew AdcOffsetDataC(ADC_AIN2) as AdcDataC2_O;
SensorCollectionP.SensorData[SENSOR_ID_AD2_O]-> AdcDataC2_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD3_O)
componentsnew AdcOffsetDataC(ADC_AIN3) as AdcDataC3_O;
SensorCollectionP.SensorData[SENSOR_ID_AD3_O]-> AdcDataC3_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD4_O)
componentsnew AdcOffsetDataC(ADC_AIN4) as AdcDataC4_O;
SensorCollectionP.SensorData[SENSOR_ID_AD4_O]-> AdcDataC4_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD5_O)
componentsnew AdcOffsetDataC(ADC_AIN5) as AdcDataC5_O;
SensorCollectionP.SensorData[SENSOR_ID_AD5_O]-> AdcDataC5_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD6_O)
componentsnew AdcOffsetDataC(ADC_AIN6) as AdcDataC6_O;
SensorCollectionP.SensorData[SENSOR_ID_AD6_O]-> AdcDataC6_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_AD7_O)
componentsnew AdcOffsetDataC(ADC_AIN7) as AdcDataC7_O;
SensorCollectionP.SensorData[SENSOR_ID_AD7_O]-> AdcDataC7_O;
#endif
#ifdefined(ALL_ASO) || defined(ASO_WM)
componentsWaterMeterDataC;//水深。
SensorCollectionP.SensorData[SENSOR_ID_WATER_METER]-> WaterMeterDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_INC)
componentsIncreasingDataC;//计数器。
SensorCollectionP.SensorData[SENSOR_ID_INC]-> IncreasingDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_INNER_TEMP)
componentsInnerTemperatureDataC;//内部温度。
SensorCollectionP.SensorData[SENSOR_ID_INNER_TEMP]-> InnerTemperatureDataC;
#endif
#ifdefined(ALL_ASO) || defined(ASO_GPS)
componentsGpsSensorC;//GSP。
SensorCollectionP.SensorData[SENSOR_ID_GPS]-> GpsSensorC;
#endif
componentsMcuSleepC;
McuSleepC.ModuleReset-> SensorCollectionP;
}
针对温湿度传感器而言,其实有一大段的接口在这里并没有连接上去,然而对整体结构来说这样搭建是至关重要的,对于这个模块而言,它的接口部分,就是文件名以C结尾的代码,比以P结尾的主程序更加重要。
下面是对应的以P结尾的主程序:
#include "SensorCollection.h"
module SensorCollectionP
{ providesinterface SensorCollection;
providesinterface ModuleReset;
uses interfaceSensorData[uint8_t id];
}//提供接口SensorCollection和ModuleReset,使用接口SensorData。
implementation
{
boolm_sensoring = FALSE;//感测数据是否已经抵达。
uint8_t*m_sensor_data = NULL;//数据指针。
uint8_tm_sensor_len = 0;//数据长度。
uint8_t m_sensor_count = 0;//计数。
uint8_tm_sensor_index = 0;//索引。
sensor_id_t*m_sensors = NULL;
boolisValidSensor(uint8_t index)
{
atomic
{
return(m_sensors != NULL && (index< m_sensor_count));
}
}//判断传感器是否有效。
uint8_tgetSensorOffset(uint8_t index)
//获取传感器数据补偿。
{
atomic
{
uint8_toffset = 0;
uint8_ti =0;
if(index > m_sensor_count)
{/*FAIL? */
return 0;
}//获取失败。
for(i=0; i < index; ++i)
{
offset += m_sensors[i].len;
}//补偿=之前的传感器数据长度之和。
returnoffset;
}
}
task voidsensorEnd()
{
atomic
{
if(!m_sensoring)
{
return;
}
}
signalSensorCollection.sensorDone(m_sensor_data, getSensorOffset(m_sensor_count),SUCCESS);//向上传递传感器数据和补偿长度。
atomic m_sensoring = FALSE;
}
task voidsensorNow()
//读数。
{
uint8_t sensor_id = 0;
uint8_t sensor_offset = 0;
atomic
{
if(!m_sensoring)
{
return;
}
if( !isValidSensor(m_sensor_index) )
{/*END*/
post sensorEnd();
return;
}//当不符合要求时直接传送失败的结果。
sensor_id= m_sensors[m_sensor_index].id;//传感器类型获取。
sensor_offset= getSensorOffset(m_sensor_index);//数据补偿获取。
}
if (callSensorData.read[sensor_id](m_sensor_data + sensor_offset) != SUCCESS)
{
atomicm_sensor_index++;
postsensorNow();
}//读取失败时直接对下一个数据进行读取,当这个数据是最后一个数据时,通过对索引的自加使索引超出长度从而判定读数失败,读数成功时数据存放至地址m_sensor_data + sensor_offset为首的,以len为长度的空间中。
/* Waiting for read done. */
}
command error_tSensorCollection.startSensor(uint8_t* data, sensor_id_t* sensor, uint8_tcount)//提供可调用的命令函数,初始化传感器。
{
atomic
{
m_sensor_data= data;//确定数据的存放位置。
m_sensor_count= count;//确定数据个数。
m_sensors= sensor;//确定传感器类型。
/*Start sensor now */
m_sensor_index= 0;
m_sensoring= TRUE;
postsensorNow();//从第0个数据开始读数。
}
return SUCCESS;
}
event voidSensorData.readDone[uint8_t id](uint8_t* p_value, uint8_t len, error_tresult)//定义SensorData中声明过的默认事件readDone。
{
atomic
{
if(!m_sensoring)
{
return;
}
m_sensor_index++;
}
post sensorNow();//按顺序读数。
}
default eventvoid SensorCollection.sensorDone(uint8_t* data, uint8_t len, error_t result) {}//声明默认的外部定义事件。
default commanderror_t SensorData.read[uint8_t id](uint8_t* p_value)
{
return FAIL;
}//定义SensorData中声明的默认命令。
/*======================================================================*/
command error_tModuleReset.reset(uint8_t reset_level)
{
atomic
{
m_sensoring= FALSE;
returnSUCCESS;
}
}//定义ModuleReset中声明的命令。
}
然而实际上数据在SensorData当中就已经读出来了。这个程序就是对不同的SensorData读出的数据进行总结和整理。然而对于已知类型的传感器应用项目可以跳过这个程序直接接入相应的SensorData。但是对于这个整个树状传感网络而言,这个程序非常重要。对于传感器类型是从外部定义而非自检测的这一瑕疵,我们的程序是写在节点上的,然而在我们的硬件结构中一个节点只能有一种类型的传感器,所以不会产生冲突。虽然确实提供了多类型传感数据上传的机制,然而其实并没有什么用。对于多类型传感器构成节点的系统中,这个程序是有问题的,它是按照温湿度、单线数字温度……GSP的顺序一个一个地执行相应的SensorData,而对应的SensorData再到底层去根据引脚上的串口输入的数据进行分析得到的数据,然而不同的SensorData并没有区分串口输入的数据是什么类型传感器产生的,他们只是单纯的处理数据,所以这里就需要对底层的程序进行修改,方式可以是通过不同引脚对不同的类型传感器读数或者设定不同的start电平等等。
AtoSensorCollectionP.nc
#include "SensorCollection.h"
module AtoSensorCollectionP
{
providesinterface AtoSensorCollection;
usesinterface SensorCollection;
}
implementation
{
sensor_id_t sensors[] =
{
#ifdef ASO_TH
{SENSOR_ID_SHT,SENSOR_LEN_SHT},
#endif
(省略)
{255,255}
};//通过外部命令define的ASO类型来确定相应的传感器类型
#definesensor_count (sizeof(sensors) /sizeof(sensors[0]) - 1)
command error_tAtoSensorCollection.startSensor(uint8_t* data)
{
return callSensorCollection.startSensor(data, sensors, sensor_count);
}//向下传递数据存放地址、传感器类型和传感器个数
event void SensorCollection.sensorDone(uint8_t*data, uint8_t len, error_t result)
{
signal AtoSensorCollection.sensorDone(data,len, result);
}//直接将SensorCollection中读到的数据上传
default eventvoid AtoSensorCollection.sensorDone(uint8_t* data, uint8_t len, error_t result){}
}
ANTTreenetNodeP.nc
#include "AtosRoute.h"
#define DBG_LEV 100
module ANTTreenetNodeP
{
uses {
interface Boot;
interface AtosControl as AtosNetControl;
interface AMPacket;
interface Packet;
interface PacketEx;
interface AtoSensorCollection;
interface AMSend;
interface Timer
interface StdControl as SystemHeartControl;
}
}/*使用ATOS网络操作模块、包处理模块、信息采集模块、活动信息发送模块、定时器模块、 系统监控系统模块(看门狗等功能)。*/
implementation
{
message_tm_sensor_msg;//标准格式的信息数据。
/*******************************************
typedef nx_struct message_t {
nx_uint8_theader[sizeof(message_header_t)];
nx_uint8_tdata[TOSH_DATA_LENGTH];
nx_uint8_tfooter[sizeof(message_footer_t)];
nx_uint8_tmetadata[sizeof(message_metadata_t)];
} message_t;
*********************************************/
uint8_tm_sensor_length = 0;//长度。
uint8_t*p_sensor_payload;//传感器数据。
uintsensor_retry = 0;//重启次数。
boolm_sensoring = FALSE;//取数完成标志位。
uint16_ttemperature,humidity;//四位16进制温湿度数据。
task voidenableSensor()
{
callSensorTimer.startPeriodic(CONFIG_SENSOR_RATE);
}//通过定时器方式定时周期性开启传感器,在makefile中设置,此处为1000ms。
task void disableSensor()
{
call SensorTimer.stop();
}//通过关闭定时器停止传感器取数。
task void sensorDataTask()
{ error_t result;
result = call AtoSensorCollection.startSensor(p_sensor_payload);
//取数。
if(result != SUCCESS)
{
if(sensor_retry++ < 3)
{
post sensorDataTask();
}
else
{
atomic m_sensoring = FALSE;
}
}
}//取数失败重试3次,都不成功则确定失败。
event voidBoot.booted()
//主函数、初始化。
{
uint8_t *data_header;
ADBG(DBG_LEV, "\r\n============Boot.booted ==========\r\n");
//输出启动标志。
/* Enable system monitor... */
//call SystemHeartControl.start();开启监控系统,这里为了在串口助手SSCOM32中得到的输出更加的简洁,将其关闭。
/* route header */
data_header = (uint8_t *)callPacket.getPayload(&m_sensor_msg, NULL);
data_header[0] = ANT_NODE_TYPE;
//确定传输数据包的包头地址,并将节点类型写入数据包的第一位。
/* sensor payload */
p_sensor_payload = data_header + 1;
call AtosNetControl.start();
post enableSensor();
//从数据包的第二位开始写入传感器传输来的信息。
}
event voidSensorTimer.fired()
//通过定时器触发数据采集。
{ ADBG(DBG_LEV, "\n\n====== SensorTimerfired %d======\r\n",(int)m_sensoring);//输出定时器触发标志。
atomic
{ if(m_sensoring) return;
m_sensoring= TRUE;
}//取数完成时直接跳回,不进行取数。
sensor_retry = 0;
post sensorDataTask();
//取数未完成时,更改取数完成标志,重置取数次数并启动取数功能。
}
task voidsendMsgTask()
//信息发送。
{ uint8_t i;
LED_BLUE_TOGGLE;//改变蓝色LED灯的明暗状态。
ADBG1(DBG_LEV, "\nsensorpayload:");
for (i=0; i < m_sensor_length; ++i)
{
ADBG(DBG_LEV,"%02x ", (int)p_sensor_payload[i]);
}//用%02x格式分别输出采集到的数据。
if (call AMSend.send(0x0001, &m_sensor_msg, m_sensor_length + 1) !=SUCCESS)//增加一位传感器类型。
{
atomicm_sensoring = FALSE;
}//信息传输失败时将取数完成标志置为未完成,从而进行重新取数。
}
event voidAtoSensorCollection.sensorDone(uint8_t* data, uint8_t len, error_t result)
//对AtoSensorCollection声明过的sensorDone进行定义。
{ float temp,hum;
memcpy(&temperature,(p_sensor_payload),2);
memcpy(&humidity,(p_sensor_payload+2),2);
temp=-39.66+0.01*temperature;
hum=(temp-25)*(0.01+0.00008*(humidity))+(-2.8E-6)*(humidity)+0.0405 *(humidity)-4.0;
//对取到的数据进行简单处理,使之成为相应的4位16进制数据以及能够用来进行计算的实型数据。
ADBG(DBG_LEV,"\n\n\nDATATRANS2(->>>>>>>>NNNNNNNODE)p_value=%x",(int)p_sensor_payload);
//输出数据传输进入节点的标志,并输出存放的地址
ADBG(DBG_LEV, "\n\ntemperature Data = %04x\n", temperature);
ADBG(DBG_LEV, "humidity Data =%04x\n", humidity);
//输出4位16进制格式的数据。
ADBG(900, "temp = %f\n", temp);
ADBG(900, "hum = %f\n", hum);
//输出实型格式数据。
ADBG(DBG_LEV, "Sensor data done, data len = %d,result=%d\n\n\n", (int)len, (int)result);//输出数据长度和采集成功标志。
if(result == SUCCESS)
{
m_sensor_length= len;
postsendMsgTask();
}
else
{
atomicm_sensoring = FALSE;
}//数据采集成功时对数据进行传输,采集失败时将取数完成标志置为未完成,从而进行重新取数。
m_sensoring = FALSE;
}
event voidAMSend.sendDone(message_t* msg, error_t err)
{ADBG(DBG_LEV,"\n\n\nDATATRANS3(NODE->>>>>>>>)msg=%x\n\n\n",(int)msg);
//数据传送结束时输出数据从节点送出的标志。
atomic m_sensoring = FALSE;
//将取数完成标志置为未完成,从而进行重新取数。
}}
AtosRouteSendQueueP.nc
#include "AtosRoute.h"
generic module AtosRouteSendQueueP()
//数据发送队列。
{
providesinterface AtosControl;
providesinterface AtosRouteSend;
uses interfaceQueueEx
uses interfaceAtosRouteSend as SubAtosRouteSend;
uses interfaceAMPacket;
uses interfacePacket;
}
implementation
{
bool m_sending =FALSE;//发送完成标志位。
task voidsendNext()
{
message_t *msg;
MSG_HEADER_T *header;
atomic
{ if(m_sending) return;//数据发送完成标志位为真时直接返回。
if(call SendQueue.empty())
{ return; }//数据发送队列为空时直接返回。
/* 获取信息。*/
msg= call SendQueue.dequeue();//出队操作。
if( call SubAtosRouteSend.send(
callAMPacket.destination(msg), msg, call Packet.payloadLength(msg)
) == SUCCESS)
{/*SUCCESS, Wait SubAtosRouteSend.sendDone */
atomic m_sending = TRUE;
}//发送成功,将数据发送完成标志位置1.
else
{ signal AtosRouteSend.sendDone(msg,ERR_ROUTE_SEND_QUEUE_FAIL);
atomic m_sending = FALSE;
post sendNext();
}//发送失败,向上传送失败的结果,并传送队列中的下一个信息。
}
}
intindexOfMessage(uint8_t route_resource_id)
{/* 通过resource id在队列中寻找信息的索引。*/
atomic
{
uint8_ti;
uint8_tsize = call SendQueue.size();//取队列长度。
for(i=0; i < size; ++i)
{ message_t *msg = (message_t *)callSendQueue.element(i); if((msg != NULL) && (RF_MSG_GET_ROUTE_RESOURCEID(msg) ==route_resource_id))
{
returni;
}
}//逐个寻找对应的信息,找到时返回序号。
return-1;
}//未找到返回-1。
}
error_tenqueueMessage(message_t *msg)
//入队操作。
{ atomic
{ if(indexOfMessage( RF_MSG_GET_ROUTE_RESOURCEID(msg) ) >= 0)
{ return ERR_ROUTE_SEND_QUEUE_EXISTS;
}//有相同数据存在于队列中,返回相同数据已存在错误。
if(call SendQueue.enqueue(msg) != SUCCESS)
{ return ERR_ROUTE_SEND_QUEUE_FULL;
}//入队。失败时返回队列溢出错误。
postsendNext();//继续发送操作。
returnSUCCESS;
}
}
event voidSubAtosRouteSend.sendDone(message_t *msg, error_t result)
//定义SubAtosRouteSend中声明的事件sendDone。
{ atomic
{ if(!m_sending) return; }//如果发送未完成直接返回。
signal AtosRouteSend.sendDone(msg, result);
//向上传送数据传输完成标志msg, result。
atomic m_sending = FALSE;
post sendNext();//继续发送操作。
}
commanderror_t AtosRouteSend.send(am_addr_t addr, message_t *msg, uint8_t len)
{//向上层提供数据发送功能。
atomic
{ returnenqueueMessage(msg); }//入队,等待发送。
}
default eventvoid AtosRouteSend.sendDone(message_t *msg, error_t result)
{//声明默认事件AtosRouteSend.sendDone。
}
boolm_started = FALSE;
commanderror_t AtosControl.start()
{ atomic
{ m_sending= FALSE;
callSendQueue.clear();//清空队列。
m_started = TRUE;
returnSUCCESS;
}
}//启动发送。
command error_tAtosControl.stop()
{
atomic
{
m_started= FALSE;
m_sending= FALSE;
callSendQueue.clear();//队列清空。
returnSUCCESS;
}
}//关闭发送。
command boolAtosControl.isStarted()
{
atomic return m_started;
}//判断发送是否启动。
}
这个程序在数据传输模块中比较有代表性,它运用到了消息队列的方式,通过消息队列来对要发送的消息进行查重等操作,上层穿过来的数据通过入队和出队操作进出缓存区。
ANTTreenetBaseP.nc
#include "message.h"
#include "AtosRoute.h"
#include "AtpCmdPacket.h"
#define DBG_LEV 20
module ANTTreenetBaseP
{ uses
{ interface Boot;
interface AMPacket;
interface Packet;
interface PacketEx;
interface AtosControl as AtosNetControl;
interface Intercept;
interface AtpCmdComm;
interface StdControl as SystemHeartControl;
}/*使用包处理模块、ATOS传感网络控制模块、信息接收模块、系统监控模块。*/
}
implementation
{ message_tm_msg;
message_tcmd_msg;
task voidinitTask()
{ call AtosNetControl.start();
}//启动传感网络控制。
event voidBoot.booted() {
ADBG(100,"#########Boot.bootedmyaddr=%x#########\n", (int)call AMPacket.address());//输出开始标志,以及数据包地址。
call SystemHeartControl.start();//开启监控系统。
post initTask();//打开传感网络控制。
}
uint16_tprepareReport(message_t * msg, uint8_t *report, atosroute_data_header_t*data_header, void *_payload, uint16_t len)
{//数据汇总。
struct
{ uint16_t sensor_type;
uint16_tsource;
uint16_torg;
uint16_tseq;
uint8_thopcount;
} payload_head;//数据头格式定义。
uint8_t *payload = (uint8_t *)_payload;//数据地址。
uint8_t payload_len = len - 1;//数据长度。
float temp,hum; //实型温湿度数据。
uint16_ttemperature,humidity;//4位16进制温湿度数据
if (len < 1)
{ return0; }//未收到数据,直接返回。
// payload中最开始的一位是传感器类型。
payload_head.sensor_type = payload[0];
/* 对数据头格式进行整理 */
payload_head.org = data_header->orgi_addr;
payload_head.source =data_header->second_addr;
if (payload_head.source == 0)
{ payload_head.source= payload_head.org; }
payload_head.seq = 0xff02;
payload_head.hopcount =data_header->hop_real;
// 将整理好的数据头写入report。
memcpy(report, &payload_head, sizeof(payload_head));
// 跳过传感器类型,将数据写入report。
memcpy_so(report, sizeof(payload_head), 128, payload + 1, payload_len);
memcpy(&temperature, (payload + 1),2);
memcpy(&humidity,(payload + 3),2);
temp=-39.66+0.01*temperature;
hum=(temp-25)*(0.01+0.00008*(humidity))+(-2.8E-6)*(humidity)+0.0405*(humidity)-4.0;
/*对取到的数据进行简单处理,使之成为相应的4位16进制数据以及能够用来进行计算的实型数据。*/
ADBG(DBG_LEV,"\n\n\n DATATRANS4(->>>>>>>>>>BBBBBBASE)payload=%x\n",(int)payload);
ADBG(DBG_LEV,"len=%d\n",(int)len);
//输出数据传输进入基站的标志,并输出存放的地址,以及数据长度。
ADBG(DBG_LEV, "\n\ntemperature Data = %04x\n",temperature);
ADBG(DBG_LEV, "humidity Data = %04x\n",humidity);
//输出4位16进制格式的数据。
ADBG(900, "temp = %f\n", temp);
ADBG(900, "hum = %f\n", hum);
//输出实型格式数据。
return len + sizeof(payload_head) - 1;//返回report长度。
}
event boolIntercept.forward(message_t* msg, void* payload, uint16_t len){//数据接收。
uint8_t i;
uint8_t report_payload[128];
uint16_t report_len;
atp_cmd_packet_t cmd_packet;
atosroute_data_header_t *data_header =(atosroute_data_header_t *)call PacketEx.getPacketHeader(msg);//数据头地址。
cmd_packet.cmd = 0x2430;//硬件型号。
cmd_packet.len = prepareReport(msg,cmd_packet.data, data_header, payload, len);//整理数据到report中。
callAtpCmdComm.sendCmdPacket(&cmd_packet);//打开协议转换模块。
LED_BLUE_TOGGLE;//蓝色LED灯明暗状态改变。
return FALSE;
}
event voidAtpCmdComm.sendCmdPacketDone(atp_cmd_packet_t *packet, error_t error) { }
event voidAtpCmdComm.receivedCmdPacket(atp_cmd_packet_t *packet, error_t error) { }//定义协议转换模块中声明的事件。
}
能看完的都是棒棒的,但是文章里应该有很多地方是有问题的,因为是三年前写的东西了,希望大家批评,我会修改的,O(∩_∩)O谢谢!