本文将在前面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上的是一样的,以后有时间再上图吧。
后面有时间把整个工程打包上传上来。
以下是上述介绍的源码:
dprintf.h
#ifndef __DPRINTF_H__
#define __DPRINTF_H__
#ifdef __cplusplus
extern "C"{
#endif
#ifdef __DEBUG
#include <stdarg.h>
#include <string.h>
//just get file name without the path
static const char *getCurrentFileName(const char * strFileName)
{
const char *p = strrchr(strFileName,'\\');
return p==NULL?strFileName:++p;
}
#define dprintf(fmt,...) rt_kprintf("%s,line:%d,"fmt,getCurrentFileName(__FILE__),__LINE__,##__VA_ARGS__)
#else
#define dprintf(fmt,...)
#endif
#ifdef __cplusplus
}
#endif
#endif
一、MQTTClient-C部分:
1、MQTTRTThread.h
#include <rtthread.h>
//----------------------------------------------------------
typedef struct Timer
{
rt_tick_t end_tick;
} Timer;
void TimerInit(Timer*);
char TimerIsExpired(Timer*);
void TimerCountdownMS(Timer*, unsigned int);
void TimerCountdown(Timer*, unsigned 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
#include "dprintf.h"
void TimerInit(Timer* timer)
{
timer->end_tick = 0;
}
char TimerIsExpired(Timer* timer)
{
return timer->end_tick <= rt_tick_get();
}
void TimerCountdownMS(Timer* timer, unsigned int timeout)
{
timer->end_tick = rt_tick_get() + timeout*RT_TICK_PER_SECOND/1000;
}
void TimerCountdown(Timer* timer, unsigned int timeout)
{
timer->end_tick = rt_tick_get() + timeout*RT_TICK_PER_SECOND;
}
int TimerLeftMS(Timer* timer)
{
return timer->end_tick - rt_tick_get() ;
}
int RTThreadLwIP_read(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
int bytes = 0;
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;
}
lwip_setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval));
while (bytes < len)
{
int rc = recv(n->my_socket, &buffer[bytes], (size_t)(len - bytes), MSG_DONTWAIT);
if (rc == -1)
{//dprintf("errno=%d\n",errno);
//if (errno != ENOTCONN && errno != ECONNRESET)
if (errno != ETIMEDOUT && errno != ECONNRESET)
{
bytes = -1;
}
break;
}
else if (rc == 0)
{
bytes = 0;
break;
}
else
bytes += rc;
}
return bytes;
}
int RTThreadLwIP_write(Network* n, unsigned char* buffer, int len, int timeout_ms)
{
int rc;
struct timeval tv;
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
if (tv.tv_sec < 0 || (tv.tv_sec == 0 && tv.tv_usec <= 0))
{
tv.tv_sec = 0;
tv.tv_usec = 100;
}
lwip_setsockopt(n->my_socket, SOL_SOCKET, SO_SNDTIMEO, (void *)&tv,sizeof(struct timeval));
rc = lwip_write(n->my_socket, buffer, len);
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, 2017 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
* Ian Craggs - add setMessageHandler function
*******************************************************************************/
#if !defined(MQTT_CLIENT_H)
#define MQTT_CLIENT_H
#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? */
#endif
enum QoS { QOS0, QOS1, QOS2, SUBFAIL=0x80 };
/* 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*, unsigned int);
extern void TimerCountdown(Timer*, unsigned 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 struct MQTTConnackData
{
unsigned char rc;
unsigned char sessionPresent;
} MQTTConnackData;
typedef struct MQTTSubackData
{
enum QoS grantedQoS;
} MQTTSubackData;
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 ping_timeout_times;
int isconnected;
int cleansession;
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 last_sent, last_received;
#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 MQTTConnectWithResults(MQTTClient* client, MQTTPacket_connectData* options,
MQTTConnackData* data);
/** 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 SetMessageHandler - set or remove a per topic message handler
* @param client - the client object to use
* @param topicFilter - the topic filter set the message handler for
* @param messageHandler - pointer to the message handler function or NULL to remove
* @return success code
*/
DLLExport int MQTTSetMessageHandler(MQTTClient* c, const char* topicFilter, messageHandler messageHandler);
/** 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 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
* @param data - suback granted QoS returned
* @return success code
*/
DLLExport int MQTTSubscribeWithResults(MQTTClient* client, const char* topicFilter, enum QoS, messageHandler, MQTTSubackData* data);
/** 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);
/** MQTT isConnected
* @param client - the client object to use
* @return truth value indicating whether the client is connected to the server
*/
DLLExport int MQTTIsConnected(MQTTClient* client);
DLLExport int keepalive(MQTTClient* client);
void MQTTCleanSession(MQTTClient* c);
#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, 2017 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 - fix for #96 - check rem_len in readPacket
* Ian Craggs - add ability to set message handler separately #6
*******************************************************************************/
#include "MQTTClient.h"
#include <rtthread.h>
#include <lwip/sockets.h>
//#define __DEBUG
#include "dprintf.h"
static 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->last_sent, 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->cleansession = 0;
c->ping_outstanding = 0;
c->ping_timeout_times = 0;
c->defaultMessageHandler = NULL;
c->next_packetid = 1;
TimerInit(&c->last_sent);
TimerInit(&c->last_received);
#if defined(MQTT_TASK)
MutexInit(&c->mutex);
#endif
}
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)
{
MQTTHeader header = {0};
int len = 0;
int rem_len = 0;
/* 1. read the header byte. This has the packet type in it */
int rc = c->ipstack->mqttread(c->ipstack, c->readbuf, 1, TimerLeftMS(timer));
if (rc != 1)
goto exit;
len = 1;
/* 2. read the remaining length. This is variable in itself */
decodePacket(c, &rem_len, TimerLeftMS(timer));
len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
if (rem_len > (c->readbuf_size - len))
{
rc = BUFFER_OVERFLOW;
goto exit;
}
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
if (rem_len > 0 && (rc = c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, TimerLeftMS(timer)) != rem_len)) {
rc = 0;
goto exit;
}
header.byte = c->readbuf[0];
rc = header.bits.type;
if (c->keepAliveInterval > 0)
TimerCountdown(&c->last_received, c->keepAliveInterval); // record the fact that we have successfully received a packet
exit:
return rc;
}
// assume topic filter and name is in correct format
// # can only be at end
// + and # can only be next to separator
static 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 = SUCCESS;
if (c->keepAliveInterval == 0)
goto exit;
//dprintf("TimerLeftMS(&c->last_sent)=%d,TimerLeftMS(&c->last_received)=%d\n",TimerLeftMS(&c->last_sent),TimerLeftMS(&c->last_received));
if (TimerIsExpired(&c->last_sent) || TimerIsExpired(&c->last_received))
{
TimerCountdown(&c->last_sent, c->keepAliveInterval);
TimerCountdown(&c->last_received, c->keepAliveInterval);
//if (c->ping_outstanding)
// rc = FAILURE; /* PINGRESP not received in keepalive interval */
//else
{dprintf("sent ping!!!!\n");
Timer timer;
TimerInit(&timer);
TimerCountdownMS(&timer, 1000);
int len = MQTTSerialize_pingreq(c->buf, c->buf_size);
if (len > 0 && (rc = sendPacket(c, len, &timer)) == SUCCESS) // send the ping packet
{ dprintf("sent ping OK!!\n");
c->ping_outstanding = 1;
}
c->ping_timeout_times++;
}
}
exit:
return rc;
}
void MQTTCleanSession(MQTTClient* c)
{
int i = 0;
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
c->messageHandlers[i].topicFilter = NULL;
}
void MQTTCloseSession(MQTTClient* c)
{
c->ping_outstanding = 0;
c->isconnected = 0;
if (c->cleansession)
MQTTCleanSession(c);
}
int cycle(MQTTClient* c, Timer* timer)
{
int len = 0,
rc = SUCCESS;
int packet_type = readPacket(c, timer); /* read the socket, see what work is due */
switch (packet_type)
{
default:
/* no more data to read, unrecoverable. Or read packet fails due to unexpected network error */
if(errno != 0 && errno!=EAGAIN)
{
rc = packet_type;
dprintf("errno=%d\n",errno);
}
goto exit;
case 0: /* timed out reading packet */
break;
case CONNACK:
case PUBACK:
case SUBACK:
case UNSUBACK:
break;
case PUBLISH:
{
MQTTString topicName;
MQTTMessage msg;
int intQoS;
msg.payloadlen = 0; /* this is a size_t, but deserialize publish sets this as int */
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){dprintf("\n");
goto exit;} // there was a problem
}
break;
}
case PUBREC:
case PUBREL:
{
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,
(packet_type == PUBREC) ? PUBREL : PUBCOMP, 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;
c->ping_timeout_times = 0;//接收到应答包清空计数器
break;
case BUFFER_OVERFLOW:
rt_kprintf("readPacket() err: BUFFER_OVERFLOW!\n");
break;
}
exit:
if (keepalive(c) != SUCCESS) {dprintf("\n");
//check only keepalive FAILURE status so that previous FAILURE status can be considered as FAULT
rc = FAILURE;
}
if (rc == SUCCESS)
rc = packet_type;
else if (c->isconnected){dprintf("\n");
MQTTCloseSession(c);}
return rc;
}
int MQTTYield(MQTTClient* c, int timeout_ms)
{
int rc = SUCCESS;
Timer timer;
TimerInit(&timer);
TimerCountdownMS(&timer, timeout_ms);
do
{ //dprintf("TimerLeftMS(timer)=%d\n",TimerLeftMS(&timer));
if (cycle(c, &timer) < 0)
{
rc = FAILURE;
break;
}
} while (!TimerIsExpired(&timer));
return rc;
}
int MQTTIsConnected(MQTTClient* client)
{
return client->isconnected;
}
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);
}
#endif
int waitfor(MQTTClient* c, int packet_type, Timer* timer)
{
int rc = FAILURE;
do
{
rt_thread_delay(10);//不延时或延时太短会出错
if (TimerIsExpired(timer))
break; // we timed out
rc = cycle(c, timer);
}
while (rc != packet_type && rc >= 0);
return rc;
}
int MQTTConnectWithResults(MQTTClient* c, MQTTPacket_connectData* options, MQTTConnackData* data)
{
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;
c->cleansession = options->cleansession;
TimerCountdown(&c->last_received, 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)
{
data->rc = 0;
data->sessionPresent = 0;
if (MQTTDeserialize_connack(&data->sessionPresent, &data->rc, c->readbuf, c->readbuf_size) == 1)
rc = data->rc;
else
rc = FAILURE;
}
else
rc = FAILURE;
exit:
if (rc == SUCCESS)
{dprintf("rc == SUCCESS,c->isconnected=1\n");
c->isconnected = 1;
c->ping_outstanding = 0;
}
#if defined(MQTT_TASK)
MutexUnlock(&c->mutex);
#endif
return rc;
}
int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options)
{
MQTTConnackData data;
return MQTTConnectWithResults(c, options, &data);
}
int MQTTSetMessageHandler(MQTTClient* c, const char* topicFilter, messageHandler messageHandler)
{
int rc = FAILURE;
int i = -1;
/* first check for an existing matching slot */
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
{
if (c->messageHandlers[i].topicFilter != NULL && strcmp(c->messageHandlers[i].topicFilter, topicFilter) == 0)
{
if (messageHandler == NULL) /* remove existing */
{
c->messageHandlers[i].topicFilter = NULL;
c->messageHandlers[i].fp = NULL;
}
rc = SUCCESS; /* return i when adding new subscription */
break;
}
}
/* if no existing, look for empty slot (unless we are removing) */
if (messageHandler != NULL) {
if (rc == FAILURE)
{
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
{
if (c->messageHandlers[i].topicFilter == NULL)
{
rc = SUCCESS;
break;
}
}
}
if (i < MAX_MESSAGE_HANDLERS)
{
c->messageHandlers[i].topicFilter = topicFilter;
c->messageHandlers[i].fp = messageHandler;
}
}
return rc;
}
int MQTTSubscribeWithResults(MQTTClient* c, const char* topicFilter, enum QoS qos,
messageHandler messageHandler, MQTTSubackData* data)
{
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){dprintf("\n");
goto exit;}
TimerInit(&timer);
TimerCountdownMS(&timer, c->command_timeout_ms);
dprintf("topic.cstring=%s\n",topic.cstring);
len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic, (int*)&qos);
if (len <= 0){dprintf("len=%d\n",len);
goto exit;}
if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet
{dprintf("\n");;goto exit;} // there was a problem
if (waitfor(c, SUBACK, &timer) == SUBACK) // wait for suback
{
int count = 0;
unsigned short mypacketid;
data->grantedQoS = QOS0;
if (MQTTDeserialize_suback(&mypacketid, 1, &count, (int*)&data->grantedQoS, c->readbuf, c->readbuf_size) == 1)
{
if (data->grantedQoS != 0x80)
rc = MQTTSetMessageHandler(c, topicFilter, messageHandler);
dprintf("rc=%d\n",rc);
}
}
else{dprintf("\n");
rc = FAILURE;}
exit:
if (rc == FAILURE){dprintf("\n");
MQTTCloseSession(c);}
#if defined(MQTT_TASK)
MutexUnlock(&c->mutex);
#endif
return rc;
}
int MQTTSubscribe(MQTTClient* c, const char* topicFilter, enum QoS qos,
messageHandler messageHandler)
{
MQTTSubackData data;
return MQTTSubscribeWithResults(c, topicFilter, qos, messageHandler, &data);
}
int MQTTUnsubscribe(MQTTClient* c, const char* topicFilter)
{
int rc = FAILURE;
Timer timer;
MQTTString topic = MQTTString_initializer;
topic.cstring = (char *)topicFilter;
int len = 0;
#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)
{
/* remove the subscription message handler associated with this topic, if there is one */
MQTTSetMessageHandler(c, topicFilter, NULL);
}
}
else
rc = FAILURE;
exit:
if (rc == FAILURE)
MQTTCloseSession(c);
#if defined(MQTT_TASK)
MutexUnlock(&c->mutex);
#endif
return rc;
}
int MQTTPublish(MQTTClient* c, const char* topicName, MQTTMessage* message)
{
int rc = FAILURE;
Timer timer;
MQTTString topic = MQTTString_initializer;
topic.cstring = (char *)topicName;
int len = 0;
#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 (rc == FAILURE){dprintf("\n");
MQTTCloseSession(c);}
#if defined(MQTT_TASK)
MutexUnlock(&c->mutex);
#endif
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
MQTTCloseSession(c);
#if defined(MQTT_TASK)
MutexUnlock(&c->mutex);
#endif
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"
#include "LED0.h"
#include "LED1.h"
#include "HardwareControl.h"
//#define __DEBUG
#include "dprintf.h"
struct 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;
}
//================== Added 2017-Apr-27 8:06:53 start ==================
//处理连接的状态机结构
typedef struct
{
enum{
UNCONNECTED = 0,
NETWORK_CONNECTED=1,
MQTT_CONNECTED=2,
SUBSCRIBING_SUCCESS=3,
WAIT_TIME_OUT=4,
//SUBSCRIBING_FAILURE = 4
}state;
int timeout_s;//超时时间,单位为秒
int times_count;//累计连续尝试连接次数,在连接成功后清零
}Connection_t;
void connect_time_out(void * data)
{
Connection_t* con =(Connection_t*)data;
if(con && con->timeout_s>0)
{
con->timeout_s --;
}
}
#define NETWORK_CONNECT_TIMEOUT 5 //5s
#define MAX_NETWORK_CONNECT_TIMES 5
#define MQTT_CONNECT_TIMEOUT 1 //1s
#define MAX_MQTT_CONNECT_TIMES 5
#define SUBSCRIB_TIMEOUT 1 //1s
#define MAX_SUBSCRIB_TIMES 5
#define MAX_NO_PING_RESPONS_TIMES 10
Connection_t connection={UNCONNECTED,NETWORK_CONNECT_TIMEOUT,0};
//================== Added 2017-Apr-27 8:06:53 end ===================
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);
((char*)message->payload)[(int)message->payloadlen]=0;
ParseDomoticzMessage((char*)message->payload);
//dprintf("MSG: qos %d, retained %d, dup %d, packetid %d\n", message->qos, message->retained, message->dup, message->id);
}
void set_host(char *host)
{
opts.host = host;
}
#define MAX_BUF_SIZE 512
void domoticz_thread_entry(void* parameter)
{
int rc = 0;
unsigned char buf[MAX_BUF_SIZE]={0};//buf[100];
unsigned char readbuf[MAX_BUF_SIZE]={0};//readbuf[100];
char* topic = "domoticz/out";
Network n;
MQTTClient c;
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
rt_timer_t timer = rt_timer_create("connect_timer", connect_time_out, &connection, RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC);
is_over = 0;
rt_kprintf("domoticz_thread_entry\n");
//================== Added 2017-Apr-26 2:36:53 start ==================
RegisterHardware(Create_LED0(),1);
RegisterHardware(Create_LED1(),6);
OpenHardwares();
SetupDomoitczMessageParser();
SetEnableParseItem(KEY_IDX);
SetEnableParseItem(KEY_NVALUE);
SetEnableParseItem(KEY_SWITCH_TYPE);
initHardwareSettings();
//================== Added 2017-Apr-26 2:36:53 end ===================
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 = 0;
rt_timer_start(timer);
while(!is_over)
{
switch(connection.state)
{
case UNCONNECTED:
//dprintf("\n");
if(connection.timeout_s>0)
continue;
dprintf("state = UNCONNECTED\n");
rt_kprintf("Connecting to %s:%d\n", opts.host, opts.port);
NetworkInit(&n);
rc = NetworkConnect(&n, opts.host, opts.port);
dprintf("rc=%d\n",rc);
if(rc==SUCCESS)
{//socket ????
connection.state = NETWORK_CONNECTED;
connection.times_count = 0;
rt_kprintf("NetworkConnect ok!\n");
//MQTTClientInit(&c, &n, 1000, buf, sizeof(buf), readbuf, sizeof(readbuf));
MQTTClientInit(&c, &n, 1000, buf,MAX_BUF_SIZE, readbuf, MAX_BUF_SIZE);
}
else if(connection.times_count <MAX_NETWORK_CONNECT_TIMES)
{dprintf("\n");
connection.times_count++;
connection.timeout_s = NETWORK_CONNECT_TIMEOUT;
}
else
{//reboot system,restart
dprintf("\n");
connection.times_count=0;
connection.timeout_s = NETWORK_CONNECT_TIMEOUT*6;
rt_kprintf("we will reconnect after %d senconds\n",connection.timeout_s);
//usleep(30*1000000);//30????
//goto exit;
}
break;
case NETWORK_CONNECTED:
if(connection.timeout_s>0)
continue;
dprintf("\n");
rt_kprintf("state = NETWORK_CONNECTED\n");
rc = MQTTConnect(&c, &data);
dprintf("rc=%d\n",rc);
if(rc == SUCCESS)
{
connection.state = MQTT_CONNECTED;
connection.times_count = 0;
connection.timeout_s = 0;
//printf("MQTTConnected!\n");
rt_kprintf("MQTTConnected! Subscribing to %s\n", topic);
}
else if(connection.times_count <MAX_MQTT_CONNECT_TIMES)
{
//printf("MQTTConnect times=%d, err:%d! \n",connection.times_count,rc);
rt_kprintf("MQTTConnect times=%d, err:%d!\n",connection.times_count,rc);
connection.times_count++;
connection.timeout_s = MQTT_CONNECT_TIMEOUT;
}
else
{//????network??
dprintf("\n");
NetworkDisconnect(&n);
connection.state = UNCONNECTED;
connection.times_count=0;
connection.timeout_s = NETWORK_CONNECT_TIMEOUT*6;
rt_kprintf("we will reconnect after %d senconds\n",connection.timeout_s);
}
break;
case MQTT_CONNECTED:
if(connection.timeout_s>0)
continue;
dprintf("state = MQTT_CONNECTED\n");
rc = MQTTSubscribe(&c, topic, opts.qos, messageArrived);
dprintf("rc=%d\n",rc);
if(rc == SUCCESS)
{
rt_kprintf("Subscribed %s\n", topic);
connection.state = SUBSCRIBING_SUCCESS;
connection.times_count = 0;
}
else if(connection.times_count <MAX_SUBSCRIB_TIMES && MQTTIsConnected(&c)==1)
{
rt_kprintf("MQTTSubscribe times=%d, err:%d! \n",connection.times_count,rc);
connection.times_count++;
connection.timeout_s = MQTT_CONNECT_TIMEOUT;
}
else
{
if(MQTTIsConnected(&c)==1)
MQTTDisconnect(&c);
else
MQTTCleanSession(&c);
NetworkDisconnect(&n);
connection.state = UNCONNECTED;
connection.times_count=0;
connection.timeout_s = NETWORK_CONNECT_TIMEOUT*6;
rt_kprintf("we will reconnect after %d senconds\n",connection.timeout_s);
}
break;
case SUBSCRIBING_SUCCESS:
MQTTYield(&c, 1000);
if(c.ping_timeout_times>=MAX_NO_PING_RESPONS_TIMES+1 || MQTTIsConnected(&c)==0)
{
if(MQTTIsConnected(&c)==1)
MQTTDisconnect(&c);
else
MQTTCleanSession(&c);
NetworkDisconnect(&n);
connection.state = UNCONNECTED;
connection.times_count = 0;
connection.timeout_s = NETWORK_CONNECT_TIMEOUT*6;
rt_kprintf("we will reconnect after %d senconds\n",connection.timeout_s);
}
else
{
connection.times_count = 0;
}
break;
default:
rt_kprintf("satte = default,err!\n");
break;
}
}
exit:
rt_kprintf("Stopping\n");
MQTTDisconnect(&c);
NetworkDisconnect(&n);
rt_timer_delete(timer);
}
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 __cplusplus
extern "C"
{
#endif
//------------------------------------------------------------------------------
//common defines
#define KEY_IDX 0
#define KEY_NAME 1
#define KEY_ID 2
#define KEY_UINT 3
#define KEY_DTYPE 4
#define KEY_STYPE 5
#define KEY_NVALUE 6
#define KEY_SVALUE1 7
#define KEY_SVALUE2 8
#define KEY_BATTERY 9
#define KEY_RSSI 10
#define KEY_SWITCH_TYPE 11
#define MSG_MAX_LEN 128
#define KEY_WORDS_NUM 12
//------------------------------------------------------------------------------
//common types
typedef enum
{
STRING=0,
INT=1,
UINT=2
}ARG_TYPE_t;
#ifdef __cplusplus
}
#endif
#endif /* #ifndef COMMON_TYPES_H */
/*-- File end --*/
HardwareInterface.h:
/******************************************************************************
*filename: HardwareInterface.h
******************************************************************************/
#ifndef HARDWARE_INTERFACE_H
#define HARDWARE_INTERFACE_H
#ifdef __cplusplus
extern "C"
{
#endif
#include "CommonTypes.h"
//------------------------------------------------------------------------------
//common Hardware interface
typedef struct
{
//解析出来的消息参数类型(STRING、INT、UINT中的一种)
ARG_TYPE_t type;
//下面是解析出来的具体消息参数数据
union
{
char strArg[MSG_MAX_LEN];
int iVar;
unsigned int uiVar;
}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 __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(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 interface
Hardware* 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;
}
//------------------------------------------------------------------------------
//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 --*/
3、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(void);
extern void SetEnableParseItem(int item);
extern void SetDisableParseItem(int item);
extern int ParseDomoticzMessage(char* str);
//------------------------------------------------------------------------------
//hardware settings
extern 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,...)
#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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;
}
//#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.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 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 --*/
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;
}
/*@}*/

本文介绍了如何在基于STM32F107的开发板上,利用RTThread操作系统和LwIP协议栈,结合MQTT客户端与Domoticz服务器,实现Android远程控制LED灯。文中详细阐述了硬件配置、软件环境、MQTT客户端移植及源码结构,并提及了STM32与Mini2440的差异处理。
使用domoticz+mosquitto+Android客户端实现控制STM32板上的LED(一)&spm=1001.2101.3001.5002&articleId=70837228&d=1&t=3&u=f69231f3f0eb412d828620c51ac9dbb7)
3436

被折叠的 条评论
为什么被折叠?



