基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(九)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(二)
2017-04-17 00:45
976 查看
为了充分利用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)"
这意味着参数数量总体是不固定的。
我们实际的开关量收到的消息如下:
收到了"svalue1"。
要写出一个灵活的框架来适应svalue数量可变,还是有点难度的,先不做这么复杂的。
后文中将写一个能够接收名字固定的参数,再加上"svalue1" 、"svalue2" ,共处理12个参数类型。(或许有时间写个对"svalue"参数数量可变的处理函数?)
当然,domoticz源码是公开的,我们也可以在c++级别源代码上添加自己所需要的参数。
2、场景消息。
在
void MQTT::SendSceneInfo(const unsigned long long SceneIdx, const std::string &SceneName)
中进行封装的。
暂时没用到,暂不分析。
详情请参考相关资料和源代码。
二、编写一个简单的消息解析框架。
框架设计如下:
图中红色箭头的过程就是我们主要要实现的过程。
右边两个虚边框就是我们设计的框架。
具体实现代码如下:
CommonTypes.h:
HardwareInterface.h:
HardwareControl.h:
HardwareControl.c:
DomoticzMessageParser.h:
DomoticzMessageParser.c:
上面是框架,下面来两个例子(LED0、LED1):
LED0.h:
LED0.c:
LED1.h:
LED1.c:
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 subscriber
compulsory parameters:
topic to subscribe to
defaulted parameters:
--host localhost
--port 1883
--qos 2
--delimiter \n
--clientid stdout_subscriber
--userid none
--password none
for 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;
else
usage();
}
else
usage();
}
else if (strcmp(argv[count], "--host") == 0)
{
if (++count < argc)
opts.host = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--port") == 0)
{
if (++count < argc)
opts.port = atoi(argv[count]);
else
usage();
}
else if (strcmp(argv[count], "--clientid") == 0)
{
if (++count < argc)
opts.clientid = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--username") == 0)
{
if (++count < argc)
opts.username = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--password") == 0)
{
if (++count < argc)
opts.password = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--delimiter") == 0)
{
if (++count < argc)
opts.delimiter = argv[count];
else
opts.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;
else
usage();
}
else
usage();
}
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);
else
printf("%.*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:
然后执行:
make
make install
可执行文件已经拷贝到NFS的目标板目录上去了。
实现的效果跟前一篇文章基本上是一样的,只不过用了一个简单框架,来提高扩展性和灵活性,就不贴实际效果图了。
这个框架的代码在GCC上编译是OK的,理论上支持C99的编译器都应该没问题,其他编译器没有试过。
框架本身理论上可以用在其他嵌入式操作系统上,不过没试过。
用它来做个探索,方便后续的STM32平台的开发。
一、对消息参数名字稍作研究。
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 ******************************************************************************/ #ifnd 4000 ef 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);//无需处理idx int(*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 implement int (*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; else continue; } 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; } //------------------------------------------------------------------------------ //initionalize int 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 interface typedef 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,...) #endif KeyWord_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->p f4e4 arseArg)); 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 DomoitczMessageParser static 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 subscriber
compulsory parameters:
topic to subscribe to
defaulted parameters:
--host localhost
--port 1883
--qos 2
--delimiter \n
--clientid stdout_subscriber
--userid none
--password none
for 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;
else
usage();
}
else
usage();
}
else if (strcmp(argv[count], "--host") == 0)
{
if (++count < argc)
opts.host = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--port") == 0)
{
if (++count < argc)
opts.port = atoi(argv[count]);
else
usage();
}
else if (strcmp(argv[count], "--clientid") == 0)
{
if (++count < argc)
opts.clientid = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--username") == 0)
{
if (++count < argc)
opts.username = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--password") == 0)
{
if (++count < argc)
opts.password = argv[count];
else
usage();
}
else if (strcmp(argv[count], "--delimiter") == 0)
{
if (++count < argc)
opts.delimiter = argv[count];
else
opts.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;
else
usage();
}
else
usage();
}
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);
else
printf("%.*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-gcc INCDIR :=-I/usr/local/arm/paho.mqtt.embedded-c/include COMPILE.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:mqtt mqtt:$(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) mqtt distclean:clean rm -f $(DEPS) -include $(DEPS)
然后执行:
make
make install
可执行文件已经拷贝到NFS的目标板目录上去了。
实现的效果跟前一篇文章基本上是一样的,只不过用了一个简单框架,来提高扩展性和灵活性,就不贴实际效果图了。
这个框架的代码在GCC上编译是OK的,理论上支持C99的编译器都应该没问题,其他编译器没有试过。
框架本身理论上可以用在其他嵌入式操作系统上,不过没试过。
用它来做个探索,方便后续的STM32平台的开发。
相关文章推荐
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(八)使用domoticz+mosquitto+Android客户端实现控制mini2440上的LED(一)
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(十一)使用domoticz+mosquitto+Android客户端实现控制STM32板上的LED(二)
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(六)使用domoticz联合arm上的mosquitto实现Android客户端远程控制
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(四)交叉编译OpenSSL、c-ares、e2fsprogs和mosquitto
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(七)交叉编译paho.mqtt.embedded-c库和嵌入式linux样例程序
- 《基于Android微博整合客户端的设计与实现》毕业设计论文开题报告
- 《基于Android微博整合客户端的设计与实现》毕业设计中期考核表
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(三)编译并安装cmake和git工具
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(五)交叉编译并安装zlib、curl、boost和domoticz
- 基于mini2440嵌入式linux上整合一套Domoticz智能家居系统(一)前言
- 《基于Android微博整合客户端的设计与实现》毕业设计论文任务书
- android4.0平台通过IOCTL控制LED-基于x210v3开发板
- 基于ichartjs图形库在android上使用HTML动态实现3D柱形图
- 基于XMPP实现的Openfire的配置安装+Android客户端的实现
- 基于XMPP实现android客户端与服务器的交互
- Android之UI学习篇十:使用TabHost实现微博客户端界面
- android 使用shell command实现对设备控制,实现按坐标点击
- struts2 使用注解、反射、拦截器实现基于方法的权限控制
- 基于XMPP实现的Openfire的配置安装+Android客户端的实现
- 使用Mina框架开发 QQ Android 客户端(3) 登陆功能的实现