为了充分利用domoticz平台的对MQTT客户端的控制功能,现在,受控设备端代码的核心任务转移到了对domoticz/out主题的MQTT消息解析上。本文将设计一个简单框架来实现对其消息的解析和功能回调。

一、对消息参数名字稍作研究。

domoticz/out的MQTT消息格式参考:
https://www.domoticz.com/wiki/MQTT
根据官方资料,并没有指明参数个数。
实际上看domoticz-3.5877版本源代码,可以在domoticz-3.5877/hardware/MQTT.cpp中看出,
domoticz/out消息大概有两类:
1、普通设备信息。
在void MQTT::SendDeviceInfo(const int m_HwdID, const unsigned long long DeviceRowIdx, const std::string &DeviceName, const unsigned char *pRXCommand)
中进行封装。

我们的开关设备也就在这里封装的。

从这段代码中可以看到:

10个参数名字是固定的

["idx"]

["id"]

["unit"]

["name"]

["dtype"]

["stype"]

["switchType"]

["RSSI"]

["Battery"]

["nvalue"]

还有个参数名字不固定:

"svalue"

它可以是以后缀1为开头:"svalue1"或者"svalue2"、……"svalue(n)"

这意味着参数数量总体是不固定的。

我们实际的开关量收到的消息如下:

{"Battery" : 255,"RSSI" : 12,"dtype" : "Light/Switch","id" : "00014051","idx" : 1,"name" : "LED鐏,"nvalue" : 1,"stype" : "Switch","svalue1" : "0","switchType" : "On/Off","unit" : 1
}

收到了"svalue1"

要写出一个灵活的框架来适应svalue数量可变,还是有点难度的,先不做这么复杂的。

后文中将写一个能够接收名字固定的参数,再加上"svalue1""svalue2",共处理12个参数类型。(或许有时间写个对"svalue"参数数量可变的处理函数?)

当然,domoticz源码是公开的,我们也可以在c++级别源代码上添加自己所需要的参数。

2、场景消息。

void MQTT::SendSceneInfo(const unsigned long long SceneIdx, const std::string &SceneName)

中进行封装的。

暂时没用到,暂不分析。

详情请参考相关资料和源代码。

二、编写一个简单的消息解析框架。

框架设计如下:

图中红色箭头的过程就是我们主要要实现的过程。

右边两个虚边框就是我们设计的框架。

具体实现代码如下:

CommonTypes.h:

/******************************************************************************
*filename: CommonTypes.h
******************************************************************************/#ifndef COMMON_TYPES_H
#define COMMON_TYPES_H#ifdef __cplusplus
extern "C"
{
#endif//------------------------------------------------------------------------------
//common defines#define KEY_IDX     0
#define KEY_NAME    1
#define KEY_ID      2
#define KEY_UINT    3
#define KEY_DTYPE   4
#define KEY_STYPE   5
#define KEY_NVALUE  6
#define KEY_SVALUE1 7
#define KEY_SVALUE2 8
#define KEY_BATTERY 9
#define KEY_RSSI    10
#define KEY_SWITCH_TYPE 11#define MSG_MAX_LEN 128
#define KEY_WORDS_NUM 12//------------------------------------------------------------------------------
//common types
typedef enum
{STRING=0,INT=1,UINT=2
}ARG_TYPE_t;#ifdef __cplusplus
}
#endif#endif /* #ifndef COMMON_TYPES_H */
/*-- File end --*/

HardwareInterface.h:

/******************************************************************************
*filename: HardwareInterface.h
******************************************************************************/
#ifndef HARDWARE_INTERFACE_H
#define HARDWARE_INTERFACE_H#ifdef __cplusplus
extern "C"
{
#endif#include "CommonTypes.h"//------------------------------------------------------------------------------
//common Hardware interface
typedef struct
{ //解析出来的消息参数类型(STRING、INT、UINT中的一种)ARG_TYPE_t type;//下面是解析出来的具体消息参数数据union{char strArg[MSG_MAX_LEN];int iVar;unsigned int uiVar;};
}ParserArg;typedef struct
{//----------------respons parser interface----------------------------------//int(*IDX_ParserCallback)(ParserArg* arg);//无需处理idxint(*NAME_ParserCallback)(ParserArg* arg);int(*ID_ParserCallback)(ParserArg* arg);int(*UINT_ParserCallback)(ParserArg* arg);int(*DTYPE_ParserCallback)(ParserArg* arg);int(*STYPE_ParserCallback)(ParserArg* arg);int(*NVALUE_ParserCallback)(ParserArg* arg);int(*SVALUE1_ParserCallback)(ParserArg* arg);int(*SVALUE2_ParserCallback)(ParserArg* arg);int(*BATTERY_ParserCallback)(ParserArg* arg);int(*RSSI_ParserCallback)(ParserArg* arg);int(*SWITCH_TYPE_ParserCallback)(ParserArg* arg);ParserArg parseArg;int RegisterIDX;//--------------device base operation---------------------------------------//must be implementint (*Open)();void (*Init)();void (*Close)();}Hardware;typedef int(*ParserCallback)(ParserArg* arg);#ifdef __cplusplus
}
#endif#endif /* #ifndef HARDWARE_INTERFACE_H */
/*-- File end --*/

HardwareControl.h:

/******************************************************************************
* filename: HardwareControl.h
******************************************************************************/#ifndef HARDWARE_CONTROL_H
#define HARDWARE_CONTROL_H
#ifdef __cplusplus
extern "C"
{
#endif#include "HardwareInterface.h"#define HARDWARE_MAX_NUM 32#define REGISTER_SUCCESED 1
#define REGISTER_ERR1 -1 //索引号已经被使用
#define REGISTER_ERR2 -2 //容器已满,不能注册extern int OpenHardwares();
extern void initHardwareSettings();
extern void CloseHardwares();extern int RegisterHaraware(Hardware *hardware,int idx);
extern Hardware* GetHardware(int idx);
extern int UnregisterHaraware(int idx);#ifdef __cplusplus
}
#endif#endif /* #ifndef HARDWARE_CONTROL_H */
/*-- File end --*/

HardwareControl.c:

/******************************************************************************
* filename: HardwareControl.c
******************************************************************************/
#include "HardwareControl.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>//------------------------------------------------------------------------------
//GetHardWare interface
Hardware* g_HardwareContainer[HARDWARE_MAX_NUM];/******************************************************************************
* 函数名: RegisterHaraware
* 功能描述:向硬件容器注册一个索引号为idx的硬件
* 参数1 :Hardware *hardware [I]:该硬件的指针
* 参数2 :int idx [I]:要分配的索引号
* 返回值: int ,成功则返回1,失败则返回错误号
* 创建时间:2017-Apr-17 22:08:58
* 修改时间:2017-Apr-17 22:08:58
* 版本记录:
* 其他说明:为了使用方便应该做一个配置文件以适配硬件信息
******************************************************************************/int RegisterHaraware(Hardware *hardware,int idx)
{int i;assert(hardware);for(i=0;i<HARDWARE_MAX_NUM;i++){if(g_HardwareContainer[i]){if(g_HardwareContainer[i]->RegisterIDX==idx)return REGISTER_ERR1;elsecontinue;}else{g_HardwareContainer[i] = hardware ;g_HardwareContainer[i]->RegisterIDX = idx;return 1;  }}return REGISTER_ERR2;
}/******************************************************************************
* 函数名: GetHardWare
* 功能描述: 根据索引号获取相应的硬件设备指针
* 参数1 :int idx [I]:设备索引号
* 返回值: 成功则返回对应硬件指针,失败返回0(NULL)
* 创建时间:2017-Apr-16 18:52:10
* 修改时间:2017-Apr-16 18:52:10
* 版本记录:
******************************************************************************/
Hardware* GetHardware(int idx)
{int i;for(i=0;i<HARDWARE_MAX_NUM;i++){if(g_HardwareContainer[i] && g_HardwareContainer[i]->RegisterIDX==idx)return g_HardwareContainer[i];}return 0;
}/******************************************************************************
* 函数名: UnregisterHaraware
* 功能描述:取消索引号为idx的硬件注册
* 参数1 :int idx [I]:要取消注册的硬件的idx号
* 返回值: 成功则返回取消注册的位置,失败返回-1
* 创建时间:2017-Apr-17 22:06:25
* 修改时间:2017-Apr-17 22:06:25
* 版本记录:
******************************************************************************/
int UnregisterHaraware(int idx)
{int i;for(i=0;i<HARDWARE_MAX_NUM;i++){if(g_HardwareContainer[i] && g_HardwareContainer[i]->RegisterIDX==idx)g_HardwareContainer[i] = 0;return i;}return -1;
}//------------------------------------------------------------------------------
//initionalizeint OpenHardwares()
{int i;int count=0;for(i=0;i<HARDWARE_MAX_NUM;i++){if(g_HardwareContainer[i]){if(!g_HardwareContainer[i]->Open)return -i;//如果该硬件接口没有实现Open,则返回它在容器中的位置的相反数(<=0)else{g_HardwareContainer[i]->Open();count++;}}}return count;//如果成功返回执行Open的设备数量
}void initHardwareSettings()
{int i;for(i=0;i<HARDWARE_MAX_NUM;i++){if(g_HardwareContainer[i] && g_HardwareContainer[i]->Init){g_HardwareContainer[i]->Init();}}
}void CloseHardwares()
{int i;for(i=0;i<HARDWARE_MAX_NUM;i++){if(g_HardwareContainer[i] && g_HardwareContainer[i]->Close)g_HardwareContainer[i]->Close();}
}/*-- File end --*/

DomoticzMessageParser.h:

/******************************************************************************
* filename: DomoticzMessageParser.h
******************************************************************************/#ifndef DOMOTICZ_MESSAGE_PARSER_H
#define DOMOTICZ_MESSAGE_PARSER_H
#ifdef __cplusplus
extern "C"
{
#endif#include "HardwareInterface.h"typedef struct{char *str;//分割字符串后,消息存入buf中时所需对比的关键字char *parseStr;//解析消息时所使用的匹配字符串ARG_TYPE_t type;//该消息对应的类型(STRING、INT、UINT中的一种)
}KeyWord_t;extern int GetKeywordIndex(const char* str);//------------------------------------------------------------------------------
//common DomoitczMessageParser interfacetypedef struct DomoitczMessageParser DomoitczMessageParser;struct DomoitczMessageParser
{int(*IDX_Handler)(DomoitczMessageParser* pParser, const char* message);int(*NAME_Handler)(DomoitczMessageParser* pParser, const char* message);int(*ID_Handler)(DomoitczMessageParser* pParser, const char* message);int(*UINT_Handler)(DomoitczMessageParser* pParser, const char* message);int(*DTYPE_Handler)(DomoitczMessageParser* pParser, const char* message);int(*STYPE_Handler)(DomoitczMessageParser* pParser, const char* message);int(*NVALUE_Handler)(DomoitczMessageParser* pParser, const char* message);int(*SVALUE1_Handler)(DomoitczMessageParser* pParser, const char* message);int(*SVALUE2_Handler)(DomoitczMessageParser* pParser, const char* message);int(*BATTERY_Handler)(DomoitczMessageParser* pParser, const char* message);int(*RSSI_Handler)(DomoitczMessageParser* pParser, const char* message);int(*SWITCH_TYPE_Handler)(DomoitczMessageParser* pParser, const char* message);int (*FillArgStr)(DomoitczMessageParser* pParser,const char* value);char MsgBuf[KEY_WORDS_NUM][MSG_MAX_LEN];  Hardware* bindHardware;
};typedef   int(*DomoitczMessageParserHandler)(DomoitczMessageParser* pParser, const char* message);extern DomoitczMessageParser g_DMP;
extern DomoitczMessageParser* g_pParser;extern void SetupDomoitczMessageParser();extern void SetEnableParseItem(int item);
extern void SetDisableParseItem(int item);extern int ParseDomoticzMessage(char* str);
//------------------------------------------------------------------------------
//hardware settings
extern void initHardWareSettings();#ifdef __cplusplus
}
#endif#endif /* #ifndef DOMOTICZ_MESSAGE_PARSER_H */
/*-- File end --*/

DomoticzMessageParser.c:

/******************************************************************************
* filename: DomoticzMessageParser.c
******************************************************************************//*-- #include --*/
#include "DomoticzMessageParser.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdarg.h>#ifdef _DEBUG
#define dprintf(msg,...)  printf("%s,line:%d,"msg,__FILE__,__LINE__,##__VA_ARGS__)
#else
#define dprintf(msg,...)
#endifKeyWord_t KeyWords[KEY_WORDS_NUM+1]=
{{"idx","   \"idx\" : %d,",INT},{"name","   \"name\" : \"%s\",",STRING},{"id","   \"id\" : \"%s\",",STRING},{"unit","   \"unit\" : %u",UINT},{"dtype","   \"dtype\" : \"%s\",",STRING},{"stype","   \"stype\" : \"%s\",",STRING},{"nvalue","   \"nvalue\" : %d,",INT},{"svalue1","   \"svalue1\" : \"%s\",",STRING},{"svalue2","   \"svalue2\" : \"%s\",",STRING},{"Battery","   \"Battery\" : %u,",UINT},{"RSSI","   \"RSSI\" : %d,",INT},{"switchType","   \"switchType\" : \"%s\",",STRING},{"unknown","unknown",STRING}//防止越界访问
};/******************************************************************************
* 函数名: GetKeywordIndex
* 功能描述: 根据关键字获取含该关键字的消息在KeyWords的位置索引号
* 参数1 :const char* str [I]:要查询的具体关键字字符串
* 返回值: 消息在KeyWords的位置索引号
* 创建时间:2017-Apr-16 19:09:26
* 修改时间:2017-Apr-16 19:09:26
* 版本记录:
******************************************************************************/
int GetKeywordIndex(const char* str)
{int i;for(i=0;i<KEY_WORDS_NUM;i++){if(strstr(str,KeyWords[i].str)){return i;}}return KEY_WORDS_NUM;
}//------------------------------------------------------------------------------
//DomoitczMessageParser interface implemention
//#0
int IDX_HandlerImpl(DomoitczMessageParser* pParser, const char* message)
{int idx;if(!pParser)return 0;      if(sscanf(message,KeyWords[KEY_IDX].parseStr,&idx)>0){dprintf("idx=%d\n",idx);       pParser->bindHardware = GetHardware(idx);//根据设备索引号搜索硬件设备//pParser->bindHardware->IDX_ParserCallback(&(pParser->bindHardware->parseArg));return pParser->bindHardware?1:0;     }return 0;
}//#1
int NAME_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->NAME_ParserCallback)return 0; funcParseCallback = pParser->bindHardware->NAME_ParserCallback;if(sscanf(message,KeyWords[KEY_NAME].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("name=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_NAME].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#2
int ID_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->ID_ParserCallback)return 0;   funcParseCallback = pParser->bindHardware->ID_ParserCallback; if(sscanf(message,KeyWords[KEY_ID].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("id=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_ID].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#3
int UINT_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->UINT_ParserCallback)return 0; funcParseCallback = pParser->bindHardware->UINT_ParserCallback;   if(sscanf(message,KeyWords[KEY_UINT].parseStr,&(pParser->bindHardware->parseArg.uiVar))>0){dprintf("uint=%u\n",pParser->bindHardware->parseArg.uiVar);pParser->bindHardware->parseArg.type = KeyWords[KEY_UINT].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#4
int DTYPE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->DTYPE_ParserCallback)return 0;    funcParseCallback = pParser->bindHardware->DTYPE_ParserCallback;      if(sscanf(message,KeyWords[KEY_DTYPE].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("dtype=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_DTYPE].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#5
int STYPE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->STYPE_ParserCallback)return 0;    funcParseCallback = pParser->bindHardware->STYPE_ParserCallback;  if(sscanf(message,KeyWords[KEY_STYPE].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("name=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_STYPE].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#6
int NVALUE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser || !pParser->bindHardware || !pParser->bindHardware->NVALUE_ParserCallback)return 0;funcParseCallback = pParser->bindHardware->NVALUE_ParserCallback;      if(sscanf(message,KeyWords[KEY_NVALUE].parseStr,&(pParser->bindHardware->parseArg.iVar))>0){dprintf("nvalue=%d\n",pParser->bindHardware->parseArg.iVar);pParser->bindHardware->parseArg.type = KeyWords[KEY_NVALUE].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#7
int SVALUE1_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->SVALUE1_ParserCallback)return 0;  funcParseCallback = pParser->bindHardware->SVALUE1_ParserCallback;        if(sscanf(message,KeyWords[KEY_SVALUE1].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("svalue1=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_SVALUE1].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#8
int SVALUE2_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->SVALUE2_ParserCallback)return 0;  funcParseCallback = pParser->bindHardware->SVALUE2_ParserCallback;if(sscanf(message,KeyWords[KEY_SVALUE2].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("svalue2=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_SVALUE2].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#9
int BATTERY_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->BATTERY_ParserCallback)return 0;  funcParseCallback = pParser->bindHardware->BATTERY_ParserCallback;if(sscanf(message,KeyWords[KEY_BATTERY].parseStr,&(pParser->bindHardware->parseArg.uiVar))>0){dprintf("battery=%u\n",pParser->bindHardware->parseArg.uiVar);pParser->bindHardware->parseArg.type = KeyWords[KEY_BATTERY].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;}return 0;
}//#10
int RSSI_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->RSSI_ParserCallback)return 0; funcParseCallback = pParser->bindHardware->RSSI_ParserCallback;if(sscanf(message,KeyWords[KEY_RSSI].parseStr,&(pParser->bindHardware->parseArg.iVar))>0){dprintf("RSSI=%d\n",pParser->bindHardware->parseArg.iVar);pParser->bindHardware->parseArg.type = KeyWords[KEY_RSSI].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;      }return 0;
}//#11
int SWITCH_TYPE_HandlerImpl(DomoitczMessageParser* pParser,const char* message)
{ParserCallback funcParseCallback;if(!pParser ||!pParser->bindHardware ||!pParser->bindHardware->SWITCH_TYPE_ParserCallback)return 0;  funcParseCallback = pParser->bindHardware->SWITCH_TYPE_ParserCallback;if(sscanf(message,KeyWords[KEY_SWITCH_TYPE].parseStr,pParser->bindHardware->parseArg.strArg)>0){dprintf("switchType=%s\n",pParser->bindHardware->parseArg.strArg);pParser->bindHardware->parseArg.type = KeyWords[KEY_SWITCH_TYPE].type;funcParseCallback(&(pParser->bindHardware->parseArg));return 1;      }return 0;
}/******************************************************************************
* 函数名: FillArgStrImpl
* 功能描述: .
* 参数1 :DomoitczMessageParser* pParser [I]:param description.
* 参数2 :const char* value [I]:param description.
* 返回值: int return variable description.
* 创建时间:2017-Apr-16 19:16:58
* 修改时间:2017-Apr-16 19:16:58
* 版本记录:
******************************************************************************/
int FillArgStrImpl(DomoitczMessageParser* pParser,const char* value)
{int key;if(!pParser)return -1;key = GetKeywordIndex(value);if(key>=KEY_WORDS_NUM)return -1;strcpy(pParser->MsgBuf[key],value);return key;
}//------------------------------------------------------------------------------
//Setup DomoitczMessageParserstatic int CALL_PARSER_FUNC_FLAG = 0;DomoitczMessageParser g_DMP;
DomoitczMessageParser* g_pParser = &g_DMP;static DomoitczMessageParserHandler HandlerPool[KEY_WORDS_NUM];/******************************************************************************
* 函数名: SetupDomoitczMessageParser
* 功能描述: 构建消息解析器
* 参数1 :DomoitczMessageParser* pDMP [I]:要构建的domoticz消息解析器指针
* 参数2 :Hardware* bindHardware [I]:初始化消息解析器解析回调对象
* 返回值:
* 创建时间:2017-Apr-16 19:13:29
* 修改时间:2017-Apr-16 19:13:29
* 版本记录:
******************************************************************************/
void SetupDomoitczMessageParser()
{g_pParser->IDX_Handler = IDX_HandlerImpl;g_pParser->NAME_Handler = NAME_HandlerImpl;g_pParser->ID_Handler = ID_HandlerImpl;g_pParser->UINT_Handler = UINT_HandlerImpl;g_pParser->DTYPE_Handler = DTYPE_HandlerImpl;g_pParser->STYPE_Handler = STYPE_HandlerImpl;g_pParser->NVALUE_Handler = NVALUE_HandlerImpl;g_pParser->SVALUE1_Handler = SVALUE1_HandlerImpl;g_pParser->SVALUE2_Handler = SVALUE2_HandlerImpl;g_pParser->BATTERY_Handler = BATTERY_HandlerImpl;g_pParser->RSSI_Handler = RSSI_HandlerImpl;g_pParser->SWITCH_TYPE_Handler = SWITCH_TYPE_HandlerImpl;g_pParser->bindHardware = 0;g_pParser->FillArgStr = FillArgStrImpl;HandlerPool[KEY_IDX] = IDX_HandlerImpl;HandlerPool[KEY_NAME] = NAME_HandlerImpl;    HandlerPool[KEY_ID] =   ID_HandlerImpl; HandlerPool[KEY_UINT] = UINT_HandlerImpl;   HandlerPool[KEY_DTYPE] = DTYPE_HandlerImpl;HandlerPool[KEY_STYPE] = STYPE_HandlerImpl;HandlerPool[KEY_NVALUE] = NVALUE_HandlerImpl; HandlerPool[KEY_SVALUE1] =  SVALUE1_HandlerImpl;HandlerPool[KEY_SVALUE2] = SVALUE2_HandlerImpl  ;HandlerPool[KEY_BATTERY] =     BATTERY_HandlerImpl;HandlerPool[KEY_RSSI] = RSSI_HandlerImpl;HandlerPool[KEY_SWITCH_TYPE] = SWITCH_TYPE_HandlerImpl;}// 将str字符以spl分割,存于g_pParser->MsgBuf中,并返回子字符串数量
int split(char* str, const char* delim)
{int n = 0;char *result = NULL;assert(g_pParser);result = strtok(str, delim);while( result != NULL ){       g_pParser->FillArgStr(g_pParser,result);dprintf("result=%s\n",result);result = strtok(NULL, delim);}return n;
}/******************************************************************************
* 函数名: SetEnableParseItem
* 功能描述: 设置CALLPARSER_FUNC_FLAG的item位置上的标志位为1
* 参数1 :int item [I]:要置1的位置,由右往左数0,1,2,3,...。
* 该参数不能超过KEY_WORDS_NUM,且不能超过31,否则视为无效。
*
* 返回值:
* 创建时间:2017-Apr-16 20:15:51
* 修改时间:2017-Apr-16 20:15:51
* 版本记录:
******************************************************************************/
void SetEnableParseItem(int item)
{assert(item<32);if(item>=0 && item<KEY_WORDS_NUM){CALL_PARSER_FUNC_FLAG |= 1<<item;}
}/******************************************************************************
* 函数名: SetEnableParseItem
* 功能描述: 设置CALLPARSER_FUNC_FLAG的item位置上的标志位为0
* 参数1 :int item [I]:要清零的位置,由右往左数0,1,2,3,...。
* 该参数不能超过KEY_WORDS_NUM,且不能超过31,否则视为无效。
*
* 返回值:
* 创建时间:2017-Apr-16 20:15:51
* 修改时间:2017-Apr-16 20:15:51
* 版本记录:
******************************************************************************/
void SetDisableParseItem(int item)
{assert(item<32);if(item>=0 && item<KEY_WORDS_NUM){CALL_PARSER_FUNC_FLAG &= ~(1<<item);}
}/******************************************************************************
* 函数名: ParseDomoticzMessage
* 功能描述: 解析消息,并回调与消息相应的硬件处理函数
* 参数1 :char* str [I]:要解析的目标消息字符串
* 返回值: int
* 创建时间:2017-Apr-16 19:18:17
* 修改时间:2017-Apr-16 19:18:17
* 版本记录:
******************************************************************************/
int ParseDomoticzMessage(char* str)
{   int nCount ;//printf("---------------------------------------\n");int i;int CallFlag ;nCount = split(str,"\n");//SetDisableParseItem(KEY_SWITCH_TYPE);      CallFlag = CALL_PARSER_FUNC_FLAG;//dprintf("CALL_PARSER_FUNC_FLAG=0x%X\n",CALL_PARSER_FUNC_FLAG);for(i=0;i<KEY_WORDS_NUM && i<32;i++){if(CallFlag&0x1){HandlerPool[i](g_pParser,g_pParser->MsgBuf[i]);//dprintf("i=%d\n",i);}CallFlag>>=1;}//g_pParser->IDX_Handler(g_pParser,g_pParser->MsgBuf[KEY_IDX]);//g_pParser->NVALUE_Handler(g_pParser,g_pParser->MsgBuf[KEY_NVALUE]);return 1;
}/*-- File end --*/

上面是框架,下面来两个例子(LED0、LED1):

LED0.h:

/******************************************************************************
* filename: LED0.h
******************************************************************************/
#ifndef LED0_H
#define LED0_H#include "HardwareInterface.h"extern Hardware* Create_LED0();#endif /* #ifndef LED0_H */
/*-- File end --*/

LED0.c:

#include "LED0.h"extern int led_fd;static Hardware LED0;
static int on;
static int led_no = 0;/*
因为LED0~LED3均使用的led_fd文件描述符,应该统一执行打开、关闭,所以下面实现为
空操作
*/
int LED0_Open()
{}void LED0_Init()
{ioctl(led_fd, 0, led_no);
}void LED0_Close()
{}/******************************************************************************
* 函数名: LED0_NVALUE_ParserCallbackImpl
* 功能描述: 在DomoiticzMessageParser进行解析"nvalue"消息参数后,
* 被回调以执行相应功能
*
* 参数1 :ParserArg* arg [I]:已经解析的消息参数
* 返回值: 成功返回1,失败返回0
* 创建时间:2017-Apr-16 18:50:27
* 修改时间:2017-Apr-16 18:50:27
* 版本记录:
******************************************************************************/
int LED0_NVALUE_ParserCallbackImpl(ParserArg* arg)
{//printf("LED0_IDX_ParserCallbackImpl is called!\n");if(arg && arg->type==INT){on = arg->iVar;ioctl(led_fd, on, led_no);return 1;}return 0;
}int LED0_SWITCH_TYPE_ParserCallbackImpl(ParserArg* arg)
{//printf("LED0_SWITCH_TYPE_ParserCallbackImpl is called!\n");  if(arg && arg->type==STRING){printf("%s\n",arg->strArg);  return 1;}return 0;
}Hardware* Create_LED0()
{LED0.Open = LED0_Open;LED0.Init= LED0_Init;LED0.Close= LED0_Close;LED0.NVALUE_ParserCallback = LED0_NVALUE_ParserCallbackImpl;LED0.SWITCH_TYPE_ParserCallback = LED0_SWITCH_TYPE_ParserCallbackImpl;return &LED0;
}

LED1.h:

/******************************************************************************
* filename: LED1.h
******************************************************************************/
#ifndef LED1_H
#define LED1_H#include "HardwareInterface.h"extern Hardware* Create_LED1();#endif /* #ifndef LED0_H */
/*-- File end --*/

LED1.c:

#include "LED1.h"extern int led_fd;static Hardware LED1;
static int on;
static int led_no = 1;int LED1_Open()
{}void LED1_Init()
{ioctl(led_fd, 0, led_no);
}void LED1_Close()
{}int LED1_NVALUE_ParserCallbackImpl(ParserArg* arg)
{//printf("LED0_IDX_ParserCallbackImpl is called!\n");if(arg && arg->type==INT){on = arg->iVar;ioctl(led_fd, on, led_no);return 1;}return 0;
}Hardware* Create_LED1()
{LED1.Open = LED1_Open;LED1.Init= LED1_Init;LED1.Close= LED1_Close;LED1.NVALUE_ParserCallback = LED1_NVALUE_ParserCallbackImpl;return &LED1;
}

stuoutsub.c改后的代码:

/******************************************************************************** Copyright (c) 2012, 2013 IBM Corp.** All rights reserved. This program and the accompanying materials* are made available under the terms of the Eclipse Public License v1.0* and Eclipse Distribution License v1.0 which accompany this distribution. ** The Eclipse Public License is available at *   http://www.eclipse.org/legal/epl-v10.html* and the Eclipse Distribution License is available at *   http://www.eclipse.org/org/documents/edl-v10.php.** Contributors:*    Ian Craggs - initial contribution*    Ian Craggs - change delimiter option from char to string*    Al Stockdill-Mander - Version using the embedded C client*******************************************************************************//*stdout subscribercompulsory parameters:topic to subscribe todefaulted parameters:--host localhost--port 1883--qos 2--delimiter \n--clientid stdout_subscriber--userid none--password nonefor example:stdoutsub topic/of/interest --host iot.eclipse.org*/
#include <stdio.h>
#include "MQTTClient.h"#include <stdio.h>
#include <signal.h>
#include <memory.h>#include <sys/time.h>//================== Added 2017-Apr-16 16:57:21 start ==================
#include <sys/ioctl.h>
#include "DomoticzMessageParser.h"
#include "LED0.h"
#include "LED1.h"
//================== Added 2017-Apr-16 16:57:21  end ===================volatile int toStop = 0;#include <stdarg.h>
#define dprintf(msg,args...)  printf("%s,line:%d,"msg,__FILE__,__LINE__,##args)void usage()
{printf("MQTT stdout subscriber\n");printf("Usage: stdoutsub topicname <options>, where options are:\n");printf("  --host <hostname> (default is localhost)\n");printf("  --port <port> (default is 1883)\n");printf("  --qos <qos> (default is 2)\n");printf("  --delimiter <delim> (default is \\n)\n");printf("  --clientid <clientid> (default is hostname+timestamp)\n");printf("  --username none\n");printf("  --password none\n");printf("  --showtopics <on or off> (default is on if the topic has a wildcard, else off)\n");exit(-1);
}void cfinish(int sig)
{signal(SIGINT, NULL);toStop = 1;
}struct opts_struct
{char* clientid;int nodelimiter;char* delimiter;enum QoS qos;char* username;char* password;char* host;int port;int showtopics;
} opts =
{(char*)"stdout-subscriber", 0, (char*)"\n", QOS2, NULL, NULL, (char*)"localhost", 1883, 0
};void getopts(int argc, char** argv)
{int count = 2;while (count < argc){if (strcmp(argv[count], "--qos") == 0){if (++count < argc){if (strcmp(argv[count], "0") == 0)opts.qos = QOS0;else if (strcmp(argv[count], "1") == 0)opts.qos = QOS1;else if (strcmp(argv[count], "2") == 0)opts.qos = QOS2;elseusage();}elseusage();}else if (strcmp(argv[count], "--host") == 0){if (++count < argc)opts.host = argv[count];elseusage();}else if (strcmp(argv[count], "--port") == 0){if (++count < argc)opts.port = atoi(argv[count]);elseusage();}else if (strcmp(argv[count], "--clientid") == 0){if (++count < argc)opts.clientid = argv[count];elseusage();}else if (strcmp(argv[count], "--username") == 0){if (++count < argc)opts.username = argv[count];elseusage();}else if (strcmp(argv[count], "--password") == 0){if (++count < argc)opts.password = argv[count];elseusage();}else if (strcmp(argv[count], "--delimiter") == 0){if (++count < argc)opts.delimiter = argv[count];elseopts.nodelimiter = 1;}else if (strcmp(argv[count], "--showtopics") == 0){if (++count < argc){if (strcmp(argv[count], "on") == 0)opts.showtopics = 1;else if (strcmp(argv[count], "off") == 0)opts.showtopics = 0;elseusage();}elseusage();}count++;}}void messageArrived(MessageData* md)
{MQTTMessage* message = md->message;if (opts.showtopics)printf("%.*s\t", md->topicName->lenstring.len, md->topicName->lenstring.data);if (opts.nodelimiter)printf("%.*s", (int)message->payloadlen, (char*)message->payload);elseprintf("%.*s%s", (int)message->payloadlen, (char*)message->payload, opts.delimiter);//fflush(stdout);
//================== Added 2017-Apr-16 16:56:59 start ==================ParseDomoticzMessage((char*)message->payload);
//================== Added 2017-Apr-16 16:56:59  end ===================
}int led_fd;int main(int argc, char** argv)
{   int rc = 0;unsigned char buf[100];unsigned char readbuf[100];if (argc < 2)usage();char* topic = argv[1];if (strchr(topic, '#') || strchr(topic, '+'))opts.showtopics = 1;if (opts.showtopics)printf("topic is %s\n", topic);getopts(argc, argv); Network n;//Client c;MQTTClient c;signal(SIGINT, cfinish);signal(SIGTERM, cfinish);//================== Added 2017-Apr-16 16:56:44 start ==================led_fd = open("/dev/leds0", 0);if (led_fd < 0) {led_fd = open("/dev/leds", 0);}if (led_fd < 0) {perror("open device leds");exit(1);}RegisterHaraware(Create_LED0(),1);RegisterHaraware(Create_LED1(),2);OpenHardwares();SetupDomoitczMessageParser();  SetEnableParseItem(KEY_IDX);SetEnableParseItem(KEY_NVALUE);SetEnableParseItem(KEY_SWITCH_TYPE);initHardwareSettings();
//================== Added 2017-Apr-16 16:56:44  end ===================NetworkInit(&n);NetworkConnect(&n, opts.host, opts.port);MQTTClientInit(&c, &n, 1000, buf, 100, readbuf, 100);MQTTPacket_connectData data = MQTTPacket_connectData_initializer;       data.willFlag = 0;data.MQTTVersion = 3;data.clientID.cstring = opts.clientid;data.username.cstring = opts.username;data.password.cstring = opts.password;data.keepAliveInterval = 10;data.cleansession = 1;printf("Connecting to %s %d\n", opts.host, opts.port);rc = MQTTConnect(&c, &data);printf("Connected %d\n", rc);printf("Subscribing to %s\n", topic);rc = MQTTSubscribe(&c, topic, opts.qos, messageArrived);printf("Subscribed %d\n", rc);while (!toStop){MQTTYield(&c, 1000); }printf("Stopping\n");MQTTDisconnect(&c);NetworkDisconnect(&n); //================== Added 2017-Apr-16 16:57:50 start ==================CloseHardwares();close(led_fd);exit(0);
//================== Added 2017-Apr-16 16:57:50  end ===================    return 0;
}

Makefile:

TOPDIR = ../..CC:=arm-linux-gccINCDIR :=-I/usr/local/arm/paho.mqtt.embedded-c/includeCOMPILE.c   = $(CC)  $(CFLAGS) $(INCDIR) -MMD -c
LINK.c      = $(CC)  $(LDFLAGS) -lpthread -lrt
.PHONY: all
%.o:%.c$(COMPILE.c) $< -o $@SRC_FILE := \MQTTClient.c \MQTTLinux.c \DomoticzMessageParser.c \HardwareControl.c \LED0.c \LED1.c \stdoutsub.c
OBJS := $(addsuffix .o, $(basename $(SRC_FILE)))
DEPS :=$(OBJS:.o=.d)all:mqttmqtt:$(OBJS)$(LINK.c) $(OBJS) -lpaho-embed-mqtt3c -L/usr/local/arm/paho.mqtt.embedded-c/lib  -o $@install:cp mqtt /work/rootfs/usr/my/clean:rm -f $(OBJS) mqttdistclean:cleanrm -f $(DEPS)-include $(DEPS)

然后执行:

make

make install

可执行文件已经拷贝到NFS的目标板目录上去了。

实现的效果跟前一篇文章基本上是一样的,只不过用了一个简单框架,来提高扩展性和灵活性,就不贴实际效果图了。

这个框架的代码在GCC上编译是OK,理论上支持C99的编译器都应该没问题其他编译器没有试过

框架本身理论上可以用在其他嵌入式操作系统上,不过没试过。

用它来做个探索,方便后续的STM32平台的开发。

基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(九)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(二)相关推荐

  1. 基于Domoticz智能家居系统(十三)Domoticz-3.8153在Tiny6410开发板上的移植

    Domoticz-3.8153在Tiny6410开发板上的移植 本文将在友善之臂Tiny6410开发板上移植Domoticz-3.8153,起因是去年在mini2440上移植的3.5877版本编译出来 ...

  2. 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(八)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(一)

    本篇将在上一篇交叉编译paho.mqtt.embedded-c官方的源代码例程基础上,稍作修改,尝试实现domoticz+mosquitto联合做服务器,使用Android客户端APP或浏览器对min ...

  3. 基于Domoticz智能家居系统(十四)用ESP8266做MQTT客户端实验

    基于Domoticz智能家居系统(十四)用ESP8266做MQTT客户端实验 用ESP8266做MQTT客户端 一些前期的准备 第一步 设置ESP8266开发板的BSP的搜索引擎链接 第二步 下载安装 ...

  4. 基于Domoticz智能家居系统(十七)DIY一款基于MySensors的ESP8266+NRF24L01的MQTT(WIFI)和RF无线网关(三)一个简单MySensors客户端的验证实验

    DIY一款基于MySensors的ESP8266+NRF24L01的MQTT(WIFI)和RF无线网关(三)一个简单MySensors客户端的验证实验 一个简单MySensors客户端的验证实验 一. ...

  5. 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(十)使用domoticz+mosquitto+Android客户端实现控制STM32板上的LED(一)

    本文将在前面mini2440建立的domoticz.mosquitto为服务器的基础上,以Android做远程客户端来实现对STM32芯片的开发板的LED进行灯控制,当然,这是一个基本功能的实现,如果 ...

  6. 基于Domoticz智能家居系统(十六)DIY一款基于MySensors的ESP8266+NRF24L01的MQTT(WIFI)和RF无线网关(二)正式DIY

    DIY一款基于MySensors的ESP8266+NRF24L01的MQTT(WIFI)和RF无线网关(二)正式DIY 正式DIY 一.本文参考的国外DIY项目 二.本文采用的模块和连接线路 1.ES ...

  7. 快速打造一套可以语音控制的智能家居系统

    快速打造一套可以语音控制的智能家居系统 随着智能家居产业的蓬勃发展,照明.安防.工控等主要制造商已经推出多款智能家居产品,通过无线传感技术来增加使用舒适度.但因消费端需求逐渐成熟,集中控制和语音控制多 ...

  8. 一套智能家居控制系统多少钱,智能家居系统是怎么配置的?

    随着智能家居概念的不断落地,越来越多的消费者已经开始从认识智能家居走向使用智能家居,那么如何做好住宅智能家居系统的配置和价格预算就成为了很多用户所关心的问题.智能家居控制系统多少钱一套?如何配置智能家 ...

  9. linux搭建web服务器论文答辩开场白,基于ARM9嵌入式Linux系统Web Server服务器计算机设计与科学专业毕业答辩.ppt...

    基于ARM9嵌入式Linux系统Web Server服务器计算机设计与科学专业毕业答辩 2009-5-31 计算机科学与技术专业 2009届本科生毕业论文答辩 基于ARM9的嵌入式Linux系统的We ...

最新文章

  1. PEAR, PECL和Perl的区别
  2. 【c++】25.事件驱动的详解
  3. php提示密码错误的代码_php 实现密码错误三次锁定账号10分钟
  4. 最后8小时 | 最新智能驾驶视觉技术行业研究报告出炉!圈内从业者、投资人不可错过...
  5. 同实例下复制表的2种方法
  6. const 和readonly
  7. Java基础入门笔记-构造方法的继承
  8. 文档预览 OfficeWebViewer:在浏览器中查看Office文档
  9. android 获取是否连接wifi热点,android - 如何知道您是否已连接到Wifi热点/ Wifi / Wifi Direct - 堆栈内存溢出...
  10. 图像算法二:【图像几何变换】平移、镜像、转置、缩放、旋转、插值
  11. CentOS 7 安装OpenOffice并实现WordToPDF(Java调用)
  12. ecshop快速购买
  13. 中国地图以及各个省份json数据
  14. 多元素过渡理解和一点透
  15. 我劝你不要再留QQ邮箱了
  16. 怎样转换WPS格式,WPS怎么转换PPT格式
  17. PHP之流程控制(四)
  18. linux 点亮屏幕,按电源键屏幕唤醒和屏幕睡眠流程(从上层到kernel)
  19. Floyd (弗洛伊德)算法简述
  20. 【微信小程序丨第一篇】初识微信小程序开发

热门文章

  1. 第二次作业:《国际贸易学》—自由贸易理论
  2. 手机录音如何转换成文字?学会这个简单方法,让你事半功倍!
  3. Array和Arrays
  4. 取消IE8打开HTTPS带图片内容后提示安全警告
  5. 网络安全-网站后台的寻找+网页JS文件信息收集
  6. java播放器使用教程_[Java教程]Java音乐播放器
  7. 信号完整性分析-笔记
  8. Debian7升级glibc至2.15
  9. Windows11任务栏无法透明化解决办法
  10. 知识图谱-KGE-第三方库:OpenKE库【清华开源】