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

来源:互联网 发布:php redis 订阅发布 编辑:程序博客网 时间:2024/06/09 23:21

本文将在前面mini2440建立的domoticz、mosquitto为服务器的基础上,以Android做远程客户端来实现对STM32芯片的开发板的LED进行灯控制,当然,这是一个基本功能的实现,如果长期使用的话,还需要做很多其他的工作(例如断网重连,重连多少次重启系统等等,还要有个可以在SD卡上读入的硬件配置信息等等)。

首先展示一下开发板这边情况,硬件方面是在2011年买的“金牛开发板”,主控芯片是STM32F107VC,PHY芯片是DP83848,烧固件的工具是随开发板带来的Jlink,从JTAG口烧录进去。

软件方面,使用RTThread-2.1.0作为操作系统,该系统自带移植的嵌入式TCP/IP协议栈LwIP。
本文中使用的LwIP协议栈版本是1.4.1,是rtthread默认bsp中默认的版本。
bsp工程文件位置:rt-thread\bsp\stm32f107\project.uvproj
bsp已经实现了PHY芯片DP83848的驱动,所以就省了很多事。

使用的集成开发环境是:MDK 4.11 版本

本人在工程中是用了对c99的支持(主要是为了调试)。

本来看介绍,还以为RTThread-2.1.0已经移植好了MQTT客户端程序,但实际上,只是把代码放过来,还没有做什么移植。

所以,要实现本文的目标,就需要对MQTT客户端做个移植。

mqtt的客户端程序在rtthread软件包的位置是:
rt-thread\components\external\paho-mqtt

不知道这个是什么版本的,总之,本人是用在mini2440上已经验证过的版本,就是官方在GitHub上的最新版本,封包解包的那部分用MQTTPacket,可以不用改动,直接就用。

MQTTClient-C这个模块部分,用自己的实现。
放到rt-thread\components\external\paho-mqtt\MQTTClient-C\samples\domoticz目录下。

主要实现有三个部分:

1、对socket的读写的基本操作简单封装。相关内容放到MQTTRTThread.h和MQTTRTThread.c中。

2、对MQTT连接和消息的解析详细过程。相关内容放到MQTTClient.h和MQTTClient.c中。

3、对整个过程的控制,使用rtthread的一个线程实现。相关内容放到DomoticzThread.h和DomoticzThread.c中。

源码放在后面附上。

接下来要说的内容就是把曾在mini2440上用过的domoticz消息解析框架移植过来。
果然keil的arm编译器跟GCC是不同的,要做一些改动。改好的源码在后面附上。
这一部分放在rt-thread\bsp\stm32f107\applications目录下。

在STM32开发板上,也控制了两个LED作为例程。
效果跟mini2440上的是一样的,以后有时间再上图吧。

后面有时间把整个工程打包上传上来。


以下是上述介绍的源码:
一、MQTTClient-C部分:

1、MQTTRTThread.h

#include <rtthread.h>  //----------------------------------------------------------typedef struct Timer{    int left_ms;    rt_timer_t timer_ptr;} Timer;void TimerInit(Timer*);char TimerIsExpired(Timer*);void TimerCountdownMS(Timer*, int);void TimerCountdown(Timer*, int);int TimerLeftMS(Timer*);void DeleteTimer(Timer*);//-------------------------------------------------------typedef struct Network{    int my_socket;    int (*mqttread) (struct Network*, unsigned char*, int, int);    int (*mqttwrite) (struct Network*, unsigned char*, int, int);} Network;void NetworkInit(Network*);int NetworkConnect(Network*, char*, int);void NetworkDisconnect(Network*);

MQTTRTThread.c

#include "MQTTRTThread.h"#include <lwip/netdb.h>  #include <lwip/sockets.h>  #include <stdio.h>//#define __DEBUG#ifdef __DEBUG#include <stdarg.h>#include <string.h>static const char *getCurrentFileName(){    const char *p = strrchr(__FILE__,'\\');    return ++p;}#define dprintf(fmt,...) rt_kprintf("%s,line:%d,"fmt,getCurrentFileName(),__LINE__,##__VA_ARGS__)#else#define dprintf(fmt,...)#endif//int RTThreadLwIP_read(Network* n, unsigned char* buffer, int len, int timeout_ms);//int RTThreadLwIP_write(Network* n, unsigned char* buffer, int len, int timeout_ms);//因为10ms才调用一次void SetTimerTimeout(void* timer){    Timer* tmr=(Timer*)timer;    if(tmr->left_ms<0)        return;    tmr->left_ms -= 10;    //dprintf("left_ms=%d\n",tmr->left_ms);}void TimerInit(Timer* timer){    timer->left_ms = 0;    timer->timer_ptr = 0;}char TimerIsExpired(Timer* timer){    //dprintf("timer->left_ms=%d\n",timer->left_ms);    return timer->left_ms<=0?1:0;}void TimerCountdownMS(Timer* timer, int timeout){    static int i=0;    if(timer->timer_ptr)    {        rt_timer_stop(timer->timer_ptr);    }    else    {        char str[16]={0};        sprintf(str,"ms%d",i++);        timer->timer_ptr = rt_timer_create(str,SetTimerTimeout, timer,1,/* 定时长度为1个OS Tick 。一个tick是1/RT_TICK_PER_SECOND秒,默认是10ms*/            RT_TIMER_FLAG_PERIODIC);            }    timer->left_ms = timeout;    if (timer->timer_ptr != RT_NULL)         rt_timer_start(timer->timer_ptr);    else    {        dprintf("err:timer->timer_ptr=NULL\n");     }}void TimerCountdown(Timer* timer, int timeout){    static int i=0;    if(timer->timer_ptr)    {        rt_timer_stop(timer->timer_ptr);    }    else    {        char str[16]={0};        sprintf(str,"s%d",i++);        timer->timer_ptr = rt_timer_create(str,SetTimerTimeout, timer,1,/* 定时长度为1个OS Tick 。一个tick是1/RT_TICK_PER_SECOND秒,默认是10ms*/            RT_TIMER_FLAG_PERIODIC);        }    timer->left_ms = timeout*1000;    rt_timer_start(timer->timer_ptr);}int TimerLeftMS(Timer* timer){    return timer->left_ms;}void DeleteTimer(Timer* timer){    if(timer->timer_ptr)    {        rt_timer_delete(timer->timer_ptr);    }}void tcp_time_out(void * data){    int * time = (int*)data;    if(time && *time>0)    {        (*time) -= 10;              }}int RTThreadLwIP_read(Network* n, unsigned char* buffer, int len, int timeout_ms){    int bytes = 0;#if 0 /* Commented @ 2017-Apr-26 1:23:16 */    struct timeval interval ;    interval.tv_sec = timeout_ms / 1000;    interval.tv_usec =  (timeout_ms % 1000) * 1000;    if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0))    {        interval.tv_sec = 0;        interval.tv_usec = 100;    }#endif /* Commented */    int time_out = timeout_ms;    static int i=0;    char str[16]={0};    sprintf(str,"recv_timer_%d",i++);    //dprintf("timeout_ms=%d\n",timeout_ms);    //lwip_setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));    lwip_setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&time_out, sizeof(int));    rt_timer_t timer = rt_timer_create(str, tcp_time_out, &time_out, 1,RT_TIMER_FLAG_PERIODIC);     rt_timer_start(timer);    while (bytes < len)    {        int rc = lwip_recv(n->my_socket, &buffer[bytes], (size_t)(len - bytes), 0);        if (rc == -1)        {            if (errno != ENOTCONN && errno != ECONNRESET)            {                bytes = -1;                break;            }        }        else if(time_out>0)        {            bytes += rc;        }        else        {            bytes = -1;            break;        }    }    rt_timer_delete(timer);    return bytes;}int RTThreadLwIP_write(Network* n, unsigned char* buffer, int len, int timeout_ms){    int rc;#if 0 /* Commented by sunq @ 2017-Apr-27 1:06:05 */    struct timeval tv;    tv.tv_sec = 0;  /* 30 Secs Timeout */    tv.tv_usec = timeout_ms * 1000;  // Not init'ing this can cause strange errors    lwip_setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));#endif /* Commented by sunq */    int time_out = timeout_ms;    static int i=0;    char str[16]={0};    sprintf(str,"write_timer_%d",i++);    lwip_setsockopt(n->my_socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&time_out, sizeof(int));    rt_timer_t timer = rt_timer_create(str, tcp_time_out, &time_out, 1,RT_TIMER_FLAG_PERIODIC);     rt_timer_start(timer);    rc = lwip_write(n->my_socket, buffer, len);    rt_timer_delete(timer);    return rc;}void NetworkInit(Network* n){    n->my_socket = 0;    n->mqttread = RTThreadLwIP_read;    n->mqttwrite = RTThreadLwIP_write;}int NetworkConnect(Network* n, char* addr, int port){    struct hostent *host;      struct in_addr ip_addr;      struct sockaddr_in sockaddr;      int rc = -1;    // 第一步 DNS地址解析      rt_kprintf("calling gethostbyname with: %s\r\n", addr);      host = gethostbyname(addr);      ip_addr.s_addr = *(unsigned long *) host->h_addr_list[0];      rt_kprintf("MQTTThread IP Address:%s\r\n" , inet_ntoa(ip_addr));      sockaddr.sin_family = AF_INET;      sockaddr.sin_port = htons(port);      sockaddr.sin_addr = ip_addr;      rt_memset(&(sockaddr.sin_zero), 0, sizeof(sockaddr.sin_zero));      // 第二步 创建套接字    n->my_socket = socket(AF_INET, SOCK_STREAM, 0);    if (n->my_socket != -1)    {          rt_kprintf("n->my_socket: %d\r\n", n->my_socket);         rc = lwip_connect(n->my_socket, (struct sockaddr*)&sockaddr, sizeof(struct sockaddr));        if (rc == -1)          {              rt_kprintf("Connect fail!\n");              lwip_close(n->my_socket);              //rt_free(recv_data);          }        else        {            rt_kprintf("Connect success!\n");          }    }      return rc;}void NetworkDisconnect(Network* n){    lwip_close(n->my_socket);}

2、MQTTClient.h

/******************************************************************************* * Copyright (c) 2014, 2015 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: *    Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation *    Ian Craggs - documentation and platform specific header *******************************************************************************/#if !defined(__MQTT_CLIENT_C_)#define __MQTT_CLIENT_C_#if defined(__cplusplus) extern "C" {#endif#if defined(WIN32_DLL) || defined(WIN64_DLL)  #define DLLImport __declspec(dllimport)  #define DLLExport __declspec(dllexport)#elif defined(LINUX_SO)  #define DLLImport extern  #define DLLExport  __attribute__ ((visibility ("default")))#else  #define DLLImport  #define DLLExport#endif#include "MQTTPacket.h"#include "MQTTRTThread.h"#include "stdio.h"#if defined(MQTTCLIENT_PLATFORM_HEADER)/* The following sequence of macros converts the MQTTCLIENT_PLATFORM_HEADER value * into a string constant suitable for use with include. */#define xstr(s) str(s)#define str(s) #s#include xstr(MQTTCLIENT_PLATFORM_HEADER)#endif#define MAX_PACKET_ID 65535 /* according to the MQTT specification - do not change! */#if !defined(MAX_MESSAGE_HANDLERS)#define MAX_MESSAGE_HANDLERS 5 /* redefinable - how many subscriptions do you want? */#endifenum QoS { QOS0, QOS1, QOS2 };/* all failure return codes must be negative */enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };/* The Platform specific header must define the Network and Timer structures and functions * which operate on them. *typedef struct Network{    int (*mqttread)(Network*, unsigned char* read_buffer, int, int);    int (*mqttwrite)(Network*, unsigned char* send_buffer, int, int);} Network;*//* The Timer structure must be defined in the platform specific header, * and have the following functions to operate on it.  */extern void TimerInit(Timer*);extern char TimerIsExpired(Timer*);extern void TimerCountdownMS(Timer*, int);extern void TimerCountdown(Timer*, int);extern int TimerLeftMS(Timer*);typedef struct MQTTMessage{    enum QoS qos;    unsigned char retained;    unsigned char dup;    unsigned short id;    void *payload;    size_t payloadlen;} MQTTMessage;typedef struct MessageData{    MQTTMessage* message;    MQTTString* topicName;} MessageData;typedef void (*messageHandler)(MessageData*);typedef struct MQTTClient{    unsigned int next_packetid,      command_timeout_ms;    size_t buf_size,      readbuf_size;    unsigned char *buf,      *readbuf;    unsigned int keepAliveInterval;    char ping_outstanding;    int isconnected;    struct MessageHandlers    {        const char* topicFilter;        void (*fp) (MessageData*);    } messageHandlers[MAX_MESSAGE_HANDLERS];      /* Message handlers are indexed by subscription topic */    void (*defaultMessageHandler) (MessageData*);    Network* ipstack;    Timer ping_timer;#if defined(MQTT_TASK)    Mutex mutex;    Thread thread;#endif } MQTTClient;#define DefaultClient {0, 0, 0, 0, NULL, NULL, 0, 0, 0}/** * Create an MQTT client object * @param client * @param network * @param command_timeout_ms * @param */DLLExport void MQTTClientInit(MQTTClient* client, Network* network, unsigned int command_timeout_ms,        unsigned char* sendbuf, size_t sendbuf_size, unsigned char* readbuf, size_t readbuf_size);/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack *  The nework object must be connected to the network endpoint before calling this *  @param options - connect options *  @return success code */DLLExport int MQTTConnect(MQTTClient* client, MQTTPacket_connectData* options);/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs *  @param client - the client object to use *  @param topic - the topic to publish to *  @param message - the message to send *  @return success code */DLLExport int MQTTPublish(MQTTClient* client, const char*, MQTTMessage*);/** MQTT Subscribe - send an MQTT subscribe packet and wait for suback before returning. *  @param client - the client object to use *  @param topicFilter - the topic filter to subscribe to *  @param message - the message to send *  @return success code */DLLExport int MQTTSubscribe(MQTTClient* client, const char* topicFilter, enum QoS, messageHandler);/** MQTT Subscribe - send an MQTT unsubscribe packet and wait for unsuback before returning. *  @param client - the client object to use *  @param topicFilter - the topic filter to unsubscribe from *  @return success code */DLLExport int MQTTUnsubscribe(MQTTClient* client, const char* topicFilter);/** MQTT Disconnect - send an MQTT disconnect packet and close the connection *  @param client - the client object to use *  @return success code */DLLExport int MQTTDisconnect(MQTTClient* client);/** MQTT Yield - MQTT background *  @param client - the client object to use *  @param time - the time, in milliseconds, to yield for  *  @return success code */DLLExport int MQTTYield(MQTTClient* client, int time);#if defined(MQTT_TASK)/** MQTT start background thread for a client.  After this, MQTTYield should not be called.*  @param client - the client object to use*  @return success code*/DLLExport int MQTTStartTask(MQTTClient* client);#endif#if defined(__cplusplus)     }#endif#endif

MQTTClient.c

/******************************************************************************* * Copyright (c) 2014, 2015 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: *    Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation *******************************************************************************/#include "MQTTClient.h"#include <rtthread.h> //#define __DEBUG#ifdef __DEBUG#include <stdarg.h>#include <string.h>static const char *getCurrentFileName(){    const char *p = strrchr(__FILE__,'\\');    return ++p;}#define dprintf(fmt,...) rt_kprintf("%s,line:%d,"fmt,getCurrentFileName(),__LINE__,##__VA_ARGS__)#else#define dprintf(fmt,...)#endifstatic void NewMessageData(MessageData* md, MQTTString* aTopicName, MQTTMessage* aMessage) {    md->topicName = aTopicName;    md->message = aMessage;}static int getNextPacketId(MQTTClient *c) {    return c->next_packetid = (c->next_packetid == MAX_PACKET_ID) ? 1 : c->next_packetid + 1;}static int sendPacket(MQTTClient* c, int length, Timer* timer){    int rc = FAILURE,         sent = 0;    while (sent < length && !TimerIsExpired(timer))    {        rc = c->ipstack->mqttwrite(c->ipstack, &c->buf[sent], length, TimerLeftMS(timer));        if (rc < 0)  // there was an error writing the data        {dprintf("error!!!!\n");            break;        }        sent += rc;    }    if (sent == length)    {        TimerCountdown(&c->ping_timer, c->keepAliveInterval); // record the fact that we have successfully sent the packet        rc = SUCCESS;    }    else        rc = FAILURE;    return rc;}void MQTTClientInit(MQTTClient* c, Network* network, unsigned int command_timeout_ms,        unsigned char* sendbuf, size_t sendbuf_size, unsigned char* readbuf, size_t readbuf_size){    int i;    c->ipstack = network;    for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)        c->messageHandlers[i].topicFilter = 0;    c->command_timeout_ms = command_timeout_ms;    c->buf = sendbuf;    c->buf_size = sendbuf_size;    c->readbuf = readbuf;    c->readbuf_size = readbuf_size;    c->isconnected = 0;    c->ping_outstanding = 0;    c->defaultMessageHandler = NULL;    c->next_packetid = 1;    TimerInit(&c->ping_timer);    dprintf("\n");}static int decodePacket(MQTTClient* c, int* value, int timeout){    unsigned char i;    int multiplier = 1;    int len = 0;    const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;    *value = 0;    do    {        int rc = MQTTPACKET_READ_ERROR;        if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)        {            rc = MQTTPACKET_READ_ERROR; /* bad data */            goto exit;        }        rc = c->ipstack->mqttread(c->ipstack, &i, 1, timeout);        if (rc != 1)            goto exit;        *value += (i & 127) * multiplier;        multiplier *= 128;    } while ((i & 128) != 0);exit:    return len;}static int readPacket(MQTTClient* c, Timer* timer){    int rc = FAILURE;    MQTTHeader header = {0};    int len = 0;    int rem_len = 0;    /* 1. read the header byte.  This has the packet type in it */    if (c->ipstack->mqttread(c->ipstack, c->readbuf, 1, TimerLeftMS(timer)) != 1)        goto exit;    len = 1;    /* 2. read the remaining length.  This is variable in itself */    decodePacket(c, &rem_len, TimerLeftMS(timer));    dprintf("data len ,,,, rem_len=%d\n",rem_len);    len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */    int data_real_len=0;    /* 3. read the rest of the buffer using a callback to supply the rest of the data */    if (rem_len > 0 && ((data_real_len=c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, TimerLeftMS(timer))) != rem_len))    {           goto exit;    }#if 0 /* Commented @ 2017-Apr-25 1:34:59 */    else    {        dprintf("data len=%d ,,,, data_real_len=%d\n",rem_len,data_real_len);        dprintf("c->readbuf=\n",c->readbuf);        int i;        for(i=0;i<data_real_len;i++)        {            rt_kprintf("%02X ,",c->readbuf[i]);            if(i%10==9)                rt_kprintf("\n");        }    }#endif /* Commented */    header.byte = c->readbuf[0];    rc = header.bits.type;exit:    return rc;}// assume topic filter and name is in correct format// # can only be at end// + and # can only be next to separatorstatic char isTopicMatched(char* topicFilter, MQTTString* topicName){    char* curf = topicFilter;    char* curn = topicName->lenstring.data;    char* curn_end = curn + topicName->lenstring.len;    while (*curf && curn < curn_end)    {        if (*curn == '/' && *curf != '/')            break;        if (*curf != '+' && *curf != '#' && *curf != *curn)            break;        if (*curf == '+')        {   // skip until we meet the next separator, or end of string            char* nextpos = curn + 1;            while (nextpos < curn_end && *nextpos != '/')                nextpos = ++curn + 1;        }        else if (*curf == '#')            curn = curn_end - 1;    // skip until end of string        curf++;        curn++;    };    return (curn == curn_end) && (*curf == '\0');}int deliverMessage(MQTTClient* c, MQTTString* topicName, MQTTMessage* message){    int i;    int rc = FAILURE;    // we have to find the right message handler - indexed by topic    for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)    {        if (c->messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(topicName, (char*)c->messageHandlers[i].topicFilter) ||                isTopicMatched((char*)c->messageHandlers[i].topicFilter, topicName)))        {            if (c->messageHandlers[i].fp != NULL)            {                MessageData md;                NewMessageData(&md, topicName, message);                c->messageHandlers[i].fp(&md);                rc = SUCCESS;            }        }    }    if (rc == FAILURE && c->defaultMessageHandler != NULL)     {        MessageData md;        NewMessageData(&md, topicName, message);        c->defaultMessageHandler(&md);        rc = SUCCESS;    }       return rc;}int keepalive(MQTTClient* c){     int rc = FAILURE;    if (c->keepAliveInterval == 0)    {        rc = SUCCESS;        goto exit;    }    if (TimerIsExpired(&c->ping_timer))    {        if (!c->ping_outstanding)        {            Timer timer;            int len;            TimerInit(&timer);            TimerCountdownMS(&timer, 1000);            len = MQTTSerialize_pingreq(c->buf, c->buf_size);            rc = sendPacket(c, len, &timer);            if (len > 0 && rc == SUCCESS) // send the ping packet            {                c->ping_outstanding = 1;            }            else if(rc== FAILURE)            {                c->ping_outstanding = 1;            }            DeleteTimer(&timer);        }    }exit:    return rc;}int cycle(MQTTClient* c, Timer* timer){    // read the socket, see what work is due    unsigned short packet_type = readPacket(c, timer);    int len = 0,        rc = SUCCESS;    switch (packet_type)    {        case CONNACK:        case PUBACK:        case SUBACK:            break;        case PUBLISH:        {            MQTTString topicName;            MQTTMessage msg;            int intQoS;            if (MQTTDeserialize_publish(&msg.dup, &intQoS, &msg.retained, &msg.id, &topicName,               (unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->readbuf, c->readbuf_size) != 1)                goto exit;            msg.qos = (enum QoS)intQoS;            deliverMessage(c, &topicName, &msg);            if (msg.qos != QOS0)            {                  if (msg.qos == QOS1)                    len = MQTTSerialize_ack(c->buf, c->buf_size, PUBACK, 0, msg.id);                else if (msg.qos == QOS2)                    len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREC, 0, msg.id);                if (len <= 0)                    rc = FAILURE;                else                    rc = sendPacket(c, len, timer);                if (rc == FAILURE)                    goto exit; // there was a problem            }            break;        }        case PUBREC:        {            unsigned short mypacketid;            unsigned char dup, type;            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)                rc = FAILURE;            else if ((len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREL, 0, mypacketid)) <= 0)                rc = FAILURE;            else if ((rc = sendPacket(c, len, timer)) != SUCCESS) // send the PUBREL packet                rc = FAILURE; // there was a problem            if (rc == FAILURE)                goto exit; // there was a problem            break;        }        case PUBCOMP:            break;        case PINGRESP:            c->ping_outstanding = 0;            break;    }    keepalive(c);    dprintf("\n");  exit:    if (rc == SUCCESS)        rc = packet_type;    return rc;}int MQTTYield(MQTTClient* c, int timeout_ms){      int rc = SUCCESS;       Timer timer;    TimerInit(&timer);    TimerCountdownMS(&timer, timeout_ms);    //dprintf("c->readbuf=%p,c->readbuf_size=%u\n",c->readbuf,c->readbuf_size);     do    {         if (cycle(c, &timer) == FAILURE)        {            rc = FAILURE;            break;        }    } while (!TimerIsExpired(&timer));    DeleteTimer(&timer);    return rc;}void MQTTRun(void* parm){    Timer timer;    MQTTClient* c = (MQTTClient*)parm;    TimerInit(&timer);    while (1)    {#if defined(MQTT_TASK)        MutexLock(&c->mutex);#endif        TimerCountdownMS(&timer, 500); /* Don't wait too long if no traffic is incoming */        cycle(c, &timer);#if defined(MQTT_TASK)        MutexUnlock(&c->mutex);#endif    } }#if defined(MQTT_TASK)int MQTTStartTask(MQTTClient* client){    return ThreadStart(&client->thread, &MQTTRun, client);}#endifint waitfor(MQTTClient* c, int packet_type, Timer* timer){    int rc = FAILURE;    do    {        if (TimerIsExpired(timer))            break; // we timed out    }    while ((rc = cycle(c, timer)) != packet_type);      return rc;}int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options){    Timer connect_timer;    int rc = FAILURE;    MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;    int len = 0;#if defined(MQTT_TASK)    MutexLock(&c->mutex);#endif    if (c->isconnected) /* don't send connect packet again if we are already connected */        goto exit;    TimerInit(&connect_timer);    TimerCountdownMS(&connect_timer, c->command_timeout_ms);    if (options == 0)        options = &default_options; /* set default options if none were supplied */    c->keepAliveInterval = options->keepAliveInterval;    TimerCountdown(&c->ping_timer, c->keepAliveInterval);    if ((len = MQTTSerialize_connect(c->buf, c->buf_size, options)) <= 0)        goto exit;    if ((rc = sendPacket(c, len, &connect_timer)) != SUCCESS)  // send the connect packet        goto exit; // there was a problem    // this will be a blocking call, wait for the connack    if (waitfor(c, CONNACK, &connect_timer) == CONNACK)    {        unsigned char connack_rc = 255;        unsigned char sessionPresent = 0;        if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, c->readbuf, c->readbuf_size) == 1)            rc = connack_rc;        else            rc = FAILURE;    }    else        rc = FAILURE;exit:    if (rc == SUCCESS)        c->isconnected = 1;#if defined(MQTT_TASK)    MutexUnlock(&c->mutex);#endif    DeleteTimer(&connect_timer);    return rc;}int MQTTSubscribe(MQTTClient* c, const char* topicFilter, enum QoS qos, messageHandler messageHandler){     int rc = FAILURE;      Timer timer;    int len = 0;    MQTTString topic = MQTTString_initializer;    topic.cstring = (char *)topicFilter;#if defined(MQTT_TASK)    MutexLock(&c->mutex);#endif    if (!c->isconnected)        goto exit;    TimerInit(&timer);    TimerCountdownMS(&timer, c->command_timeout_ms);    len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic, (int*)&qos);    if (len <= 0)        goto exit;    if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet        goto exit;             // there was a problem    if (waitfor(c, SUBACK, &timer) == SUBACK)      // wait for suback     {        int count = 0, grantedQoS = -1;        unsigned short mypacketid;        if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, c->readbuf, c->readbuf_size) == 1)            rc = grantedQoS; // 0, 1, 2 or 0x80         if (rc != 0x80)        {            int i;            for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)            {                if (c->messageHandlers[i].topicFilter == 0)                {                    c->messageHandlers[i].topicFilter = topicFilter;                    c->messageHandlers[i].fp = messageHandler;                    rc = 0;                    break;                }            }        }    }    else         rc = FAILURE;exit:#if defined(MQTT_TASK)    MutexUnlock(&c->mutex);#endif    DeleteTimer(&timer);    return rc;}int MQTTUnsubscribe(MQTTClient* c, const char* topicFilter){       int rc = FAILURE;    Timer timer;     int len = 0;           MQTTString topic = MQTTString_initializer;    topic.cstring = (char *)topicFilter;#if defined(MQTT_TASK)    MutexLock(&c->mutex);#endif    if (!c->isconnected)        goto exit;    TimerInit(&timer);    TimerCountdownMS(&timer, c->command_timeout_ms);    if ((len = MQTTSerialize_unsubscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic)) <= 0)        goto exit;    if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet        goto exit; // there was a problem    if (waitfor(c, UNSUBACK, &timer) == UNSUBACK)    {        unsigned short mypacketid;  // should be the same as the packetid above        if (MQTTDeserialize_unsuback(&mypacketid, c->readbuf, c->readbuf_size) == 1)            rc = 0;     }    else        rc = FAILURE;exit:#if defined(MQTT_TASK)    MutexUnlock(&c->mutex);#endif    DeleteTimer(&timer);    return rc;}int MQTTPublish(MQTTClient* c, const char* topicName, MQTTMessage* message){    int rc = FAILURE;    Timer timer;    int len = 0;           MQTTString topic = MQTTString_initializer;    topic.cstring = (char *)topicName;#if defined(MQTT_TASK)    MutexLock(&c->mutex);#endif    if (!c->isconnected)        goto exit;    TimerInit(&timer);    TimerCountdownMS(&timer, c->command_timeout_ms);    if (message->qos == QOS1 || message->qos == QOS2)        message->id = getNextPacketId(c);    len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id,               topic, (unsigned char*)message->payload, message->payloadlen);    if (len <= 0)        goto exit;    if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet        goto exit; // there was a problem    if (message->qos == QOS1)    {        if (waitfor(c, PUBACK, &timer) == PUBACK)        {            unsigned short mypacketid;            unsigned char dup, type;            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)                rc = FAILURE;        }        else            rc = FAILURE;    }    else if (message->qos == QOS2)    {        if (waitfor(c, PUBCOMP, &timer) == PUBCOMP)        {            unsigned short mypacketid;            unsigned char dup, type;            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1)                rc = FAILURE;        }        else            rc = FAILURE;    }exit:#if defined(MQTT_TASK)    MutexUnlock(&c->mutex);#endif    DeleteTimer(&timer);    return rc;}int MQTTDisconnect(MQTTClient* c){      int rc = FAILURE;    Timer timer;     // we might wait for incomplete incoming publishes to complete    int len = 0;#if defined(MQTT_TASK)    MutexLock(&c->mutex);#endif    TimerInit(&timer);    TimerCountdownMS(&timer, c->command_timeout_ms);    len = MQTTSerialize_disconnect(c->buf, c->buf_size);    if (len > 0)        rc = sendPacket(c, len, &timer);            // send the disconnect packet    c->isconnected = 0;#if defined(MQTT_TASK)    MutexUnlock(&c->mutex);#endif    DeleteTimer(&timer);    return rc;}

3、DomoticzThread.h:

#ifndef __DOMOTICZ_THREAD_H__#define  __DOMOTICZ_THREAD_H__extern void domoticz_thread_init(void);#endif

DomoticzThread.c:

#include <rtthread.h>#include "MQTTClient.h"//#include "led.h"#include "DomoticzMessageParser.h"//#define __DEBUG#ifdef __DEBUG#include <stdarg.h>#include <string.h>static const char *getCurrentFileName(){    const char *p = strrchr(__FILE__,'\\');    return ++p;}#define dprintf(fmt,...) rt_kprintf("%s,line:%d,"fmt,getCurrentFileName(),__LINE__,##__VA_ARGS__)#else#define dprintf(fmt,...)#endifstruct opts_struct{    char* clientid;    int nodelimiter;    char* delimiter;    enum QoS qos;    char* username;    char* password;    char* host;    int port;    int showtopics;} opts ={    (char*)"subscriber on STM32", 0, (char*)"\n", QOS2, NULL, NULL, (char*)"192.168.1.230", 1883, 0};int is_over;void quit_domoticz_thread(void){    is_over = 1;}void messageArrived(MessageData* md){    MQTTMessage* message = md->message;#if 0 /* Commented @ 2017-Apr-23 1:18:29 */    if (opts.showtopics)        rt_kprintf("%.*s\t", md->topicName->lenstring.len, md->topicName->lenstring.data);    if (opts.nodelimiter)        rt_kprintf("%.*s", (int)message->payloadlen, (char*)message->payload);    else        rt_kprintf("%.*s%s", (int)message->payloadlen, (char*)message->payload, opts.delimiter);#endif /* Commented */    dprintf("payloadlen=%d,%s\n",(int)message->payloadlen,(char*)message->payload);    dprintf("strlen(payload)=%d\n",strlen((char*)message->payload));        //fflush(stdout);    ParseDomoticzMessage((char*)message->payload);}void set_host(char *host){    opts.host = host;}void domoticz_thread_entry(void* parameter){    int rc = 0;    unsigned char buf[512]={0};//buf[100];    unsigned char readbuf[512]={0};//readbuf[100];    char* topic = "domoticz/out";    Network n;    MQTTClient c;    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;      is_over = 0;    rt_kprintf("domoticz_thread_entry\n");//================== Added 2017-Apr-26 2:36:53 start ==================    RegisterHardware(Create_LED0(),2);    RegisterHardware(Create_LED1(),3);    OpenHardwares();    SetupDomoitczMessageParser();       SetEnableParseItem(KEY_IDX);    SetEnableParseItem(KEY_NVALUE);    SetEnableParseItem(KEY_SWITCH_TYPE);    initHardwareSettings();//================== Added 2017-Apr-26 2:36:53  end ===================    NetworkInit(&n);    NetworkConnect(&n, opts.host, opts.port);    rt_kprintf("NetworkConnect ok!\n");    //MQTTClientInit(&c, &n, 1000, buf, 512, readbuf, 512);    dprintf("readbuf=%p,readbuf size=%u\n",readbuf,sizeof(readbuf));    MQTTClientInit(&c, &n, 1000, buf, sizeof(buf), readbuf, sizeof(readbuf));    data.willFlag = 0;    data.MQTTVersion = 3;    data.clientID.cstring = opts.clientid;    data.username.cstring = opts.username;    data.password.cstring = opts.password;    data.keepAliveInterval = 60;    data.cleansession = 1;    rt_kprintf("Connecting to %s %d\n", opts.host, opts.port);    rc = MQTTConnect(&c, &data);    dprintf("Connected %d\n", rc);    if(rc==-1)    {        rt_kprintf("MQTTConnect err!\n");        return;    }    rt_kprintf("Subscribing to %s\n", topic);    rc = MQTTSubscribe(&c, topic, opts.qos, messageArrived);    dprintf("Subscribed %d\n", rc);    while (!is_over)    {        //dprintf("\n");        MQTTYield(&c, 1000);    }    rt_kprintf("Stopping\n");    MQTTDisconnect(&c);    NetworkDisconnect(&n);}void domoticz_thread_init(void){    rt_thread_t domoticz_thread;    domoticz_thread = rt_thread_create("DomoticzThread", domoticz_thread_entry, RT_NULL,    0xf00, 28, 10);    if (domoticz_thread != RT_NULL)        rt_thread_startup(domoticz_thread);}#ifdef RT_USING_FINSH#include <finsh.h>FINSH_FUNCTION_EXPORT(set_host, set domoticz host ip addr);FINSH_FUNCTION_EXPORT(domoticz_thread_init,to run domoticz thread );FINSH_FUNCTION_EXPORT(quit_domoticz_thread,quit domoticz thread );#endif

二、domoticz消息解析和控制例程部分。
1、CommonTypes.h:

/*******************************************************************************filename: CommonTypes.h******************************************************************************/#ifndef COMMON_TYPES_H#define COMMON_TYPES_H#ifdef __cplusplusextern "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 typestypedef 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 __cplusplusextern "C"{#endif#include "CommonTypes.h"//------------------------------------------------------------------------------//common Hardware interfacetypedef struct{     //解析出来的消息参数类型(STRING、INT、UINT中的一种)    ARG_TYPE_t type;    //下面是解析出来的具体消息参数数据    union     {        char strArg[MSG_MAX_LEN];        int iVar;        unsigned int uiVar;    }param;}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 --*/

2、HardwareControl.h:

/******************************************************************************* filename: HardwareControl.h******************************************************************************/#ifndef HARDWARE_CONTROL_H#define HARDWARE_CONTROL_H#ifdef __cplusplusextern "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(void);extern void initHardwareSettings(void);extern void CloseHardwares(void);extern int RegisterHardware(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 <assert.h>//------------------------------------------------------------------------------//GetHardWare interfaceHardware* g_HardwareContainer[HARDWARE_MAX_NUM];/******************************************************************************* 函数名: RegisterHardware* 功能描述:向硬件容器注册一个索引号为idx的硬件* 参数1 :Hardware *hardware [I]:该硬件的指针* 参数2 :int idx [I]:要分配的索引号* 返回值: int ,成功则返回1,失败则返回错误号* 创建时间:2017-Apr-17 22:08:58* 修改时间:2017-Apr-17 22:08:58* 版本记录:* 其他说明:为了使用方便应该做一个配置文件以适配硬件信息******************************************************************************/int RegisterHardware(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;}//------------------------------------------------------------------------------//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 --*/

3、DomoticzMessageParser.h:

/******************************************************************************* filename: DomoticzMessageParser.h******************************************************************************/#ifndef DOMOTICZ_MESSAGE_PARSER_H#define DOMOTICZ_MESSAGE_PARSER_H#ifdef __cplusplusextern "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(void);extern void SetEnableParseItem(int item);extern void SetDisableParseItem(int item);extern int ParseDomoticzMessage(char* str);//------------------------------------------------------------------------------//hardware settingsextern void initHardWareSettings(void);#ifdef __cplusplus}#endif#endif /* #ifndef DOMOTICZ_MESSAGE_PARSER_H *//*-- File end --*/

DomoticzMessageParser.c:

/******************************************************************************* filename: DomoticzMessageParser.c******************************************************************************//*-- #include --*/#include "DomoticzMessageParser.h"#include "HardwareControl.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//#0int 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;}//#1int 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.param.strArg)>0)    {        dprintf("name=%s\n",pParser->bindHardware->parseArg.param.strArg);        pParser->bindHardware->parseArg.type = KeyWords[KEY_NAME].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#2int 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.param.strArg)>0)    {        dprintf("id=%s\n",pParser->bindHardware->parseArg.param.strArg);        pParser->bindHardware->parseArg.type = KeyWords[KEY_ID].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#3int 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.param.uiVar))>0)    {        dprintf("uint=%u\n",pParser->bindHardware->parseArg.param.uiVar);        pParser->bindHardware->parseArg.type = KeyWords[KEY_UINT].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#4int 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.param.strArg)>0)    {        dprintf("dtype=%s\n",pParser->bindHardware->parseArg.param.strArg);        pParser->bindHardware->parseArg.type = KeyWords[KEY_DTYPE].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#5int 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.param.strArg)>0)    {        dprintf("name=%s\n",pParser->bindHardware->parseArg.param.strArg);        pParser->bindHardware->parseArg.type = KeyWords[KEY_STYPE].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#6int 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.param.iVar))>0)    {        dprintf("nvalue=%d\n",pParser->bindHardware->parseArg.param.iVar);        pParser->bindHardware->parseArg.type = KeyWords[KEY_NVALUE].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#7int 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.param.strArg)>0)    {        dprintf("svalue1=%s\n",pParser->bindHardware->parseArg.param.strArg);        pParser->bindHardware->parseArg.type = KeyWords[KEY_SVALUE1].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#8int 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.param.strArg)>0)    {        dprintf("svalue2=%s\n",pParser->bindHardware->parseArg.param.strArg);        pParser->bindHardware->parseArg.type = KeyWords[KEY_SVALUE2].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#9int 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.param.uiVar))>0)    {        dprintf("battery=%u\n",pParser->bindHardware->parseArg.param.uiVar);        pParser->bindHardware->parseArg.type = KeyWords[KEY_BATTERY].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;    }    return 0;}//#10int 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.param.iVar))>0)    {        dprintf("RSSI=%d\n",pParser->bindHardware->parseArg.param.iVar);        pParser->bindHardware->parseArg.type = KeyWords[KEY_RSSI].type;        funcParseCallback(&(pParser->bindHardware->parseArg));        return 1;           }    return 0;}//#11int 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.param.strArg)>0)    {        dprintf("switchType=%s\n",pParser->bindHardware->parseArg.param.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 --*/

4、例程部分。
1)led硬件驱动:
led.h

/* * File      : led.h * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2009, RT-Thread Development Team * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rt-thread.org/license/LICENSE * * Change Logs: * Date           Author       Notes * 2009-01-05     Bernard      the first version */#ifndef __LED_H__#define __LED_H__#include <rtthread.h>void rt_hw_led_init(void);void rt_hw_led_on(rt_uint32_t led);void rt_hw_led_off(rt_uint32_t led);#endif

led.c:

/** File      : led.c* This file is part of RT-Thread RTOS* COPYRIGHT (C) 2009, RT-Thread Development Team** The license and distribution terms for this file may be* found in the file LICENSE in this distribution or at* http://www.rt-thread.org/license/LICENSE** Change Logs:* Date           Author       Notes* 2009-01-05     Bernard      the first version*/#include <rtthread.h>#include <stm32f10x.h>#define led1_rcc                    RCC_APB2Periph_GPIOD#define led1_gpio                   GPIOD#define led1_pin                    (GPIO_Pin_2)#define led2_rcc                    RCC_APB2Periph_GPIOD#define led2_gpio                   GPIOD#define led2_pin                    (GPIO_Pin_3)void rt_hw_led_init(void){    GPIO_InitTypeDef GPIO_InitStructure;    RCC_APB2PeriphClockCmd(led1_rcc|led2_rcc,ENABLE);    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    GPIO_InitStructure.GPIO_Pin   = led1_pin;    GPIO_Init(led1_gpio, &GPIO_InitStructure);    GPIO_InitStructure.GPIO_Pin   = led2_pin;    GPIO_Init(led2_gpio, &GPIO_InitStructure);}void rt_hw_led_on(rt_uint32_t n){    switch (n)    {    case 0:        GPIO_SetBits(led1_gpio, led1_pin);        break;    case 1:        GPIO_SetBits(led2_gpio, led2_pin);        break;    default:        break;    }}void rt_hw_led_off(rt_uint32_t n){    switch (n)    {    case 0:        GPIO_ResetBits(led1_gpio, led1_pin);        break;    case 1:        GPIO_ResetBits(led2_gpio, led2_pin);        break;    default:        break;    }}void led_toggle(rt_uint32_t n){    switch (n)    {    case 0:        GPIO_WriteBit(led1_gpio, led1_pin, (BitAction)((GPIO_ReadOutputDataBit(led1_gpio,led1_pin))^1) );        break;    case 1:        GPIO_WriteBit(led2_gpio, led2_pin, (BitAction)((GPIO_ReadOutputDataBit(led2_gpio,led2_pin))^1) );        break;    default:        break;    }}void rt_led_disp_thread_entry(void* parameter){    rt_uint32_t i=0;    while(1)    {        i=0x50000;        do{        i--;        }while(i>0);        led_toggle(0);        i=0x50000;        do{        i--;        }while(i>0);        led_toggle(1);    }}void rt_led_disp_init(void){    rt_thread_t init_led_thread;    init_led_thread = rt_thread_create("led_disp", rt_led_disp_thread_entry, RT_NULL,    128, 28, 10);    if (init_led_thread != RT_NULL)    rt_thread_startup(init_led_thread);}static rt_uint8_t led_inited = 0;void led(rt_uint32_t led, rt_uint32_t value){    /* init led configuration if it's not inited. */    if (!led_inited)    {        rt_hw_led_init();        led_inited = 1;    }    if ( led == 0 )    {        /* set led status */        switch (value)        {        case 0:            rt_hw_led_off(0);            break;        case 1:            rt_hw_led_on(0);            break;        default:            break;        }    }    if ( led == 1 )    {        /* set led status */        switch (value)        {        case 0:            rt_hw_led_off(1);            break;        case 1:            rt_hw_led_on(1);            break;        default:            break;        }    }}#ifdef RT_USING_FINSH#include <finsh.h>FINSH_FUNCTION_EXPORT(led, set led[0 - 1] on[1] or off[0])FINSH_FUNCTION_EXPORT(led_toggle, toggle the leds)#endif

2)例程:
LED0.h

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

LED0.c:

#include "led.h"#include "LED0.h"static Hardware LED0;static int led_no = 0;static int status=0;int LED0_Open(){    return 0;}void LED0_Init(){    rt_hw_led_off(led_no);    status = 0;}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)    {        status = arg->param.iVar;        if(status==1)            rt_hw_led_on(led_no);        else if(status ==0)            rt_hw_led_off(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)    {        //dprintf("%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;}int getLED0Status(){    return status;}

LED1.h:

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

LED1.c

#include "led.h"#include "LED1.h"static Hardware LED1;static int led_no = 1;int LED1_Open(){    return 0;}void LED1_Init(){    rt_hw_led_off(led_no);}void LED1_Close(){}int LED1_NVALUE_ParserCallbackImpl(ParserArg* arg){    //dprintf("LED0_IDX_ParserCallbackImpl is called!\n");    if(arg && arg->type==INT)    {        if(arg->param.iVar==1)            rt_hw_led_on(led_no);        else if(arg->param.iVar ==0)            rt_hw_led_off(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;}

5、在应用启动上加入我们的启动调用,位置在rt-thread\bsp\stm32f107\applications\application.c中。
改后的代码如下:

application.c:

/* * File      : application.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006 - 2013, RT-Thread Development Team * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rt-thread.org/license/LICENSE * * Change Logs: * Date           Author       Notes * 2009-01-05     Bernard      the first version *//** * @addtogroup STM32 *//*@{*/#include <board.h>#include <rtthread.h>#ifdef RT_USING_DFS#include <dfs_fs.h>#include <dfs_init.h>#include <dfs_elm.h>#endif#ifdef RT_USING_LWIP#include <stm32_eth.h>#include <netif/ethernetif.h>extern int lwip_system_init(void);#endif#ifdef RT_USING_FINSH#include <shell.h>#include <finsh.h>#endif//================== Added  2017-Apr-20 5:01:52 start ==================#include "led.h"#include "../../../components/external/paho-mqtt/MQTTClient-C/samples/domoticz/DomoticzThread.h" //================== Added  2017-Apr-20 5:01:52  end ===================void rt_init_thread_entry(void* parameter){    {        extern void rt_platform_init(void);        rt_platform_init();    }    /* Filesystem Initialization */#if defined(RT_USING_DFS) && defined(RT_USING_DFS_ELMFAT)    /* initialize the device file system */    dfs_init();    /* initialize the elm chan FatFS file system*/    elm_init();    /* mount sd card fat partition 1 as root directory */    if (dfs_mount("sd0", "/", "elm", 0, 0) == 0)    {        rt_kprintf("File System initialized!\n");    }    else    {        rt_kprintf("File System initialzation failed!\n");    }#endif /* RT_USING_DFS && RT_USING_DFS_ELMFAT */#ifdef RT_USING_LWIP    /* initialize lwip stack */    /* register ethernetif device */    eth_system_device_init();//================== Added  2017-Apr-20 5:49:03 start ==================    rt_hw_led_init();    rt_hw_led_off(0);    rt_hw_led_off(1);//================== Added  2017-Apr-20 5:49:03  end ===================    /* initialize lwip system */    lwip_system_init();    rt_kprintf("TCP/IP initialized!\n");//================== Added  2017-Apr-20 5:49:03 start ==================    domoticz_thread_init();//================== Added  2017-Apr-20 5:49:03  end ===================#endif#ifdef RT_USING_FINSH    /* initialize finsh */    finsh_system_init();    finsh_set_device(RT_CONSOLE_DEVICE_NAME);#endif}int rt_application_init(void){    rt_thread_t tid;    tid = rt_thread_create("init",        rt_init_thread_entry, RT_NULL,        2048, RT_THREAD_PRIORITY_MAX/3, 20);    if (tid != RT_NULL) rt_thread_startup(tid);    return 0;}/*@}*/
0 0
原创粉丝点击