24 Commits
v1.7 ... v1.9.1

Author SHA1 Message Date
0d850cd8d7 update readme 2012-11-11 15:13:02 +00:00
cc8d0c3913 update readme 2012-11-11 15:09:23 +00:00
50fc79dc42 update readme 2012-11-11 15:05:27 +00:00
8b00d6ed07 update readme 2012-11-11 14:50:11 +00:00
f13dc5e166 add test cases 2012-11-11 14:17:50 +00:00
f8cd24f0ef rewrite testsuite in python 2012-11-08 21:45:41 +00:00
5ffa607322 Copy payload before republishing in mqtt_publish_in_callback example 2012-11-04 14:07:56 +00:00
cadf3d1b38 add test framework 2012-11-02 23:36:25 +00:00
ecedcb804f Update CHANGES.txt for 1.9 2012-10-28 23:07:06 +00:00
569ada9937 Write each packet with a single call to _client->write() 2012-10-28 00:14:33 +01:00
97e9614780 Unable to set keep alive to over 32 seconds (signed int vs unsigned long) 2012-10-27 22:10:38 +01:00
6f23967ee1 License update 2012-09-13 22:38:08 +01:00
a971ed4a2c Allow any instance of Client to be passed in.
Added new example for publishing in callback.
2012-09-13 22:35:48 +01:00
2d43044338 Merge pull request #11 from jobytaffey/9607eefa0fbdda698efe538c5537340b58eb3812
Allow setting will, allow message to be stored in flash
2012-09-13 12:40:33 -07:00
9607eefa0f Added publish_P, for publishing a message from PROGMEM.
Message can be any size.
2012-07-13 16:41:31 +01:00
62693d406c Allow setting of a will without also setting a username and password. 2012-07-13 16:34:45 +01:00
7e53f612f2 Merge pull request #8 from WilHall/master
Implemented user/pass authentication
2012-04-04 13:52:49 -07:00
Wil
87fb3a9895 Implemented user/pass auth as per MQTT v3.1 specs 2012-04-04 03:44:45 -04:00
18fe49c070 Merge pull request #7 from dpslwk/master
Minor Update for example to 1.8 api
2012-03-13 12:24:50 -07:00
cc7e1c45c7 Update example to be 1.8 compatible 2012-03-13 18:29:43 +00:00
ee378802a9 Merge branch 'master' of github.com:knolleary/pubsubclient
Conflicts:
	PubSubClient/PubSubClient.cpp
	PubSubClient/PubSubClient.h
2012-03-08 22:11:28 +00:00
4d2e5994bc Various changes for v1.8 including larger message support and tidied up types. 2012-03-08 21:54:24 +00:00
6f0db7db0f Merge pull request #5 from mcollina/dns
Added DNS support.
2012-01-28 13:22:16 -08:00
f328d0849f Added DNS support. 2012-01-25 14:36:16 +01:00
14 changed files with 707 additions and 111 deletions

15
PubSubClient/CHANGES.txt Normal file → Executable file
View File

@ -1,3 +1,18 @@
1.9
* Do not split MQTT packets over multiple calls to _client->write()
* API change: All constructors now require an instance of Client
to be passed in.
* Fixed example to match 1.8 api changes - dpslwk
* Added username/password support - WilHall
* Added publish_P - publishes messages from PROGMEM - jobytaffey
1.8
* KeepAlive interval is configurable in PubSubClient.h
* Maximum packet size is configurable in PubSubClient.h
* API change: Return boolean rather than int from various functions
* API change: Length parameter in message callback changed
from int to unsigned int
* Various internal tidy-ups around types
1.7
* Improved keepalive handling
* Updated to the Arduino-1.0 API

2
PubSubClient/LICENSE.txt Normal file → Executable file
View File

@ -1,4 +1,4 @@
Copyright (c) 2008-2009 Nicholas O'Leary
Copyright (c) 2008-2012 Nicholas O'Leary
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

283
PubSubClient/PubSubClient.cpp Normal file → Executable file
View File

@ -5,76 +5,125 @@
*/
#include "PubSubClient.h"
#include "EthernetClient.h"
#include "string.h"
#include <string.h>
PubSubClient::PubSubClient() : _client() {
PubSubClient::PubSubClient(Client& client) {
this->_client = &client;
}
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, void (*callback)(char*,uint8_t*,int)) : _client() {
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, void (*callback)(char*,uint8_t*,unsigned int), Client& client) {
this->_client = &client;
this->callback = callback;
this->ip = ip;
this->port = port;
}
int PubSubClient::connect(char *id) {
return connect(id,0,0,0,0);
PubSubClient::PubSubClient(char* domain, uint16_t port, void (*callback)(char*,uint8_t*,unsigned int), Client& client) {
this->_client = &client;
this->callback = callback;
this->domain = domain;
this->port = port;
}
int PubSubClient::connect(char *id, char* willTopic, uint8_t willQos, uint8_t willRetain, char* willMessage) {
boolean PubSubClient::connect(char *id) {
return connect(id,NULL,NULL,0,0,0,0);
}
boolean PubSubClient::connect(char *id, char *user, char *pass) {
return connect(id,user,pass,0,0,0,0);
}
boolean PubSubClient::connect(char *id, char* willTopic, uint8_t willQos, uint8_t willRetain, char* willMessage)
{
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage);
}
boolean PubSubClient::connect(char *id, char *user, char *pass, char* willTopic, uint8_t willQos, uint8_t willRetain, char* willMessage) {
if (!connected()) {
if (_client.connect(this->ip, this->port)) {
int result = 0;
if (domain != NULL) {
result = _client->connect(this->domain, this->port);
} else {
result = _client->connect(this->ip, this->port);
}
if (result) {
nextMsgId = 1;
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p',MQTTPROTOCOLVERSION};
uint8_t length = 0;
int j;
// Leave room in the buffer for header and variable length field
uint16_t length = 5;
unsigned int j;
for (j = 0;j<9;j++) {
buffer[length++] = d[j];
}
uint8_t v;
if (willTopic) {
buffer[length++] = 0x06|(willQos<<3)|(willRetain<<5);
v = 0x06|(willQos<<3)|(willRetain<<5);
} else {
buffer[length++] = 0x02;
v = 0x02;
}
buffer[length++] = 0;
buffer[length++] = (KEEPALIVE/1000);
if(user != NULL) {
v = v|0x80;
if(pass != NULL) {
v = v|(0x80>>1);
}
}
buffer[length++] = v;
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
length = writeString(id,buffer,length);
if (willTopic) {
length = writeString(willTopic,buffer,length);
length = writeString(willMessage,buffer,length);
}
write(MQTTCONNECT,buffer,length);
lastOutActivity = millis();
lastInActivity = millis();
while (!_client.available()) {
long t= millis();
if (t-lastInActivity > KEEPALIVE) {
_client.stop();
return 0;
if(user != NULL) {
length = writeString(user,buffer,length);
if(pass != NULL) {
length = writeString(pass,buffer,length);
}
}
uint8_t len = readPacket();
write(MQTTCONNECT,buffer,length-5);
lastInActivity = lastOutActivity = millis();
while (!_client->available()) {
unsigned long t = millis();
if (t-lastInActivity > MQTT_KEEPALIVE*1000UL) {
_client->stop();
return false;
}
}
uint16_t len = readPacket();
if (len == 4 && buffer[3] == 0) {
lastInActivity = millis();
pingOutstanding = false;
return 1;
return true;
}
}
_client.stop();
_client->stop();
}
return 0;
return false;
}
uint8_t PubSubClient::readByte() {
while(!_client.available()) {}
return _client.read();
while(!_client->available()) {}
return _client->read();
}
uint8_t PubSubClient::readPacket() {
uint8_t len = 0;
uint16_t PubSubClient::readPacket() {
uint16_t len = 0;
buffer[len++] = readByte();
uint8_t multiplier = 1;
uint8_t length = 0;
uint16_t length = 0;
uint8_t digit = 0;
do {
digit = readByte();
@ -83,9 +132,9 @@ uint8_t PubSubClient::readPacket() {
multiplier *= 128;
} while ((digit & 128) != 0);
for (int i = 0;i<length;i++)
for (uint16_t i = 0;i<length;i++)
{
if (len < MAX_PACKET_SIZE) {
if (len < MQTT_MAX_PACKET_SIZE) {
buffer[len++] = readByte();
} else {
readByte();
@ -96,31 +145,32 @@ uint8_t PubSubClient::readPacket() {
return len;
}
int PubSubClient::loop() {
boolean PubSubClient::loop() {
if (connected()) {
long t = millis();
if ((t - lastInActivity > KEEPALIVE) || (t - lastOutActivity > KEEPALIVE)) {
unsigned long t = millis();
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
if (pingOutstanding) {
_client.stop();
return 0;
_client->stop();
return false;
} else {
_client.write(MQTTPINGREQ);
_client.write((uint8_t)0);
buffer[0] = MQTTPINGREQ;
buffer[1] = 0;
_client->write(buffer,2);
lastOutActivity = t;
lastInActivity = t;
pingOutstanding = true;
}
}
if (_client.available()) {
uint8_t len = readPacket();
if (_client->available()) {
uint16_t len = readPacket();
if (len > 0) {
lastInActivity = t;
uint8_t type = buffer[0]&0xF0;
if (type == MQTTPUBLISH) {
if (callback) {
uint8_t tl = (buffer[2]<<3)+buffer[3];
uint16_t tl = (buffer[2]<<8)+buffer[3];
char topic[tl+1];
for (int i=0;i<tl;i++) {
for (uint16_t i=0;i<tl;i++) {
topic[i] = buffer[4+i];
}
topic[tl] = 0;
@ -129,92 +179,159 @@ int PubSubClient::loop() {
callback(topic,payload,len-4-tl);
}
} else if (type == MQTTPINGREQ) {
_client.write(MQTTPINGRESP);
_client.write((uint8_t)0);
buffer[0] = MQTTPINGRESP;
buffer[1] = 0;
_client->write(buffer,2);
} else if (type == MQTTPINGRESP) {
pingOutstanding = false;
}
}
}
return 1;
return true;
}
return 0;
return false;
}
int PubSubClient::publish(char* topic, char* payload) {
return publish(topic,(uint8_t*)payload,strlen(payload));
boolean PubSubClient::publish(char* topic, char* payload) {
return publish(topic,(uint8_t*)payload,strlen(payload),false);
}
int PubSubClient::publish(char* topic, uint8_t* payload, uint8_t plength) {
return publish(topic, payload, plength, 0);
boolean PubSubClient::publish(char* topic, uint8_t* payload, unsigned int plength) {
return publish(topic, payload, plength, false);
}
int PubSubClient::publish(char* topic, uint8_t* payload, uint8_t plength, uint8_t retained) {
boolean PubSubClient::publish(char* topic, uint8_t* payload, unsigned int plength, boolean retained) {
if (connected()) {
uint8_t length = writeString(topic,buffer,0);
int i;
// Leave room in the buffer for header and variable length field
uint16_t length = 5;
length = writeString(topic,buffer,length);
uint16_t i;
for (i=0;i<plength;i++) {
buffer[length++] = payload[i];
}
uint8_t header = MQTTPUBLISH;
if (retained != 0) {
if (retained) {
header |= 1;
}
write(header,buffer,length);
return 1;
return write(header,buffer,length-5);
}
return 0;
return false;
}
boolean PubSubClient::publish_P(char* topic, uint8_t* PROGMEM payload, unsigned int plength, boolean retained) {
uint8_t llen = 0;
uint8_t digit;
int rc;
uint16_t tlen;
int pos = 0;
int i;
uint8_t header;
unsigned int len;
if (!connected()) {
return false;
}
tlen = strlen(topic);
header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
buffer[pos++] = header;
len = plength + 2 + tlen;
do {
digit = len % 128;
len = len / 128;
if (len > 0) {
digit |= 0x80;
}
buffer[pos++] = digit;
llen++;
} while(len>0);
pos = writeString(topic,buffer,pos);
rc += _client->write(buffer,pos);
for (i=0;i<plength;i++) {
rc += _client->write((char)pgm_read_byte_near(payload + i));
}
int PubSubClient::write(uint8_t header, uint8_t* buf, uint8_t length) {
_client.write(header);
_client.write(length);
_client.write(buf,length);
lastOutActivity = millis();
return 0;
return rc == len + 1 + plength;
}
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
uint8_t lenBuf[4];
uint8_t llen = 0;
uint8_t digit;
uint8_t pos = 0;
uint8_t rc;
uint8_t len = length;
do {
digit = len % 128;
len = len / 128;
if (len > 0) {
digit |= 0x80;
}
lenBuf[pos++] = digit;
llen++;
} while(len>0);
buf[4-llen] = header;
for (int i=0;i<llen;i++) {
buf[5-llen+i] = lenBuf[i];
}
rc = _client->write(buf+(4-llen),length+1+llen);
lastOutActivity = millis();
return (rc == 1+llen+length);
}
void PubSubClient::subscribe(char* topic) {
boolean PubSubClient::subscribe(char* topic) {
if (connected()) {
uint8_t length = 2;
// Leave room in the buffer for header and variable length field
uint16_t length = 7;
nextMsgId++;
buffer[0] = nextMsgId >> 8;
buffer[1] = nextMsgId - (buffer[0]<<8);
if (nextMsgId == 0) {
nextMsgId = 1;
}
buffer[0] = (nextMsgId >> 8);
buffer[1] = (nextMsgId & 0xFF);
length = writeString(topic, buffer,length);
buffer[length++] = 0; // Only do QoS 0 subs
write(MQTTSUBSCRIBE,buffer,length);
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
}
return false;
}
void PubSubClient::disconnect() {
_client.write(MQTTDISCONNECT);
_client.write((uint8_t)0);
_client.stop();
lastInActivity = millis();
lastOutActivity = millis();
buffer[0] = MQTTDISCONNECT;
buffer[1] = 0;
_client->write(buffer,2);
_client->stop();
lastInActivity = lastOutActivity = millis();
}
uint8_t PubSubClient::writeString(char* string, uint8_t* buf, uint8_t pos) {
uint16_t PubSubClient::writeString(char* string, uint8_t* buf, uint16_t pos) {
char* idp = string;
uint8_t i = 0;
uint16_t i = 0;
pos += 2;
while (*idp) {
buf[pos++] = *idp++;
i++;
}
buf[pos-i-2] = 0;
buf[pos-i-1] = i;
buf[pos-i-2] = (i >> 8);
buf[pos-i-1] = (i & 0xFF);
return pos;
}
int PubSubClient::connected() {
int rc = (int)_client.connected();
if (!rc) _client.stop();
boolean PubSubClient::connected() {
int rc = (int)_client->connected();
if (!rc) _client->stop();
return rc;
}

59
PubSubClient/PubSubClient.h Normal file → Executable file
View File

@ -7,13 +7,15 @@
#ifndef PubSubClient_h
#define PubSubClient_h
#include "Ethernet.h"
#include "EthernetClient.h"
#include <Arduino.h>
#include "Client.h"
#define MAX_PACKET_SIZE 128
#define KEEPALIVE 15000 // max value = 255000
// MQTT_MAX_PACKET_SIZE : Maximum packet size
#define MQTT_MAX_PACKET_SIZE 128
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#define MQTT_KEEPALIVE 15
// from mqtt-v3r1
#define MQTTPROTOCOLVERSION 3
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
@ -31,33 +33,42 @@
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
#define MQTTReserved 15 << 4 // Reserved
#define MQTTQOS0 (0 << 1)
#define MQTTQOS1 (1 << 1)
#define MQTTQOS2 (2 << 1)
class PubSubClient {
private:
EthernetClient _client;
uint8_t buffer[MAX_PACKET_SIZE];
uint8_t nextMsgId;
long lastOutActivity;
long lastInActivity;
Client* _client;
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
uint16_t nextMsgId;
unsigned long lastOutActivity;
unsigned long lastInActivity;
bool pingOutstanding;
void (*callback)(char*,uint8_t*,int);
uint8_t readPacket();
void (*callback)(char*,uint8_t*,unsigned int);
uint16_t readPacket();
uint8_t readByte();
int write(uint8_t header, uint8_t* buf, uint8_t length);
uint8_t writeString(char* string, uint8_t* buf, uint8_t pos);
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
uint16_t writeString(char* string, uint8_t* buf, uint16_t pos);
uint8_t *ip;
char* domain;
uint16_t port;
public:
PubSubClient();
PubSubClient(uint8_t *, uint16_t, void(*)(char*,uint8_t*,int));
int connect(char *);
int connect(char*, char*, uint8_t, uint8_t, char*);
PubSubClient(Client& client);
PubSubClient(uint8_t *, uint16_t, void(*)(char*,uint8_t*,unsigned int),Client& client);
PubSubClient(char*, uint16_t, void(*)(char*,uint8_t*,unsigned int),Client& client);
boolean connect(char *);
boolean connect(char *, char *, char *);
boolean connect(char *, char *, uint8_t, uint8_t, char *);
boolean connect(char *, char *, char *, char *, uint8_t, uint8_t, char*);
void disconnect();
int publish(char *, char *);
int publish(char *, uint8_t *, uint8_t);
int publish(char *, uint8_t *, uint8_t, uint8_t);
void subscribe(char *);
int loop();
int connected();
boolean publish(char *, char *);
boolean publish(char *, uint8_t *, unsigned int);
boolean publish(char *, uint8_t *, unsigned int, boolean);
boolean publish_P(char *, uint8_t PROGMEM *, unsigned int, boolean);
boolean subscribe(char *);
boolean loop();
boolean connected();
};

View File

@ -0,0 +1,39 @@
/*
Basic MQTT example with Authentication
- connects to an MQTT server, providing username
and password
- publishes "hello world" to the topic "outTopic"
- subscribes to the topic "inTopic"
*/
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
byte server[] = { 172, 16, 0, 2 };
byte ip[] = { 172, 16, 0, 100 };
void callback(char* topic, byte* payload, unsigned int length) {
// handle message arrived
}
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
void setup()
{
Ethernet.begin(mac, ip);
if (client.connect("arduinoClient", "testuser", "testpass")) {
client.publish("outTopic","hello world");
client.subscribe("inTopic");
}
}
void loop()
{
client.loop();
}

View File

@ -15,11 +15,12 @@ byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
byte server[] = { 172, 16, 0, 2 };
byte ip[] = { 172, 16, 0, 100 };
void callback(char* topic, byte* payload,int length) {
void callback(char* topic, byte* payload, unsigned int length) {
// handle message arrived
}
PubSubClient client(server, 1883, callback);
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
void setup()
{

View File

@ -0,0 +1,61 @@
/*
Publishing in the callback
- connects to an MQTT server
- subscribes to the topic "inTopic"
- when a message is received, republishes it to "outTopic"
This example shows how to publish messages within the
callback function. The callback function header needs to
be declared before the PubSubClient constructor and the
actual callback defined afterwards.
This ensures the client reference in the callback function
is valid.
*/
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
byte server[] = { 172, 16, 0, 2 };
byte ip[] = { 172, 16, 0, 100 };
// Callback function header
void callback(char* topic, byte* payload, unsigned int length);
EthernetClient ethClient;
PubSubClient client(server, 1883, callback, ethClient);
// Callback function
void callback(char* topic, byte* payload, unsigned int length) {
// In order to republish this payload, a copy must be made
// as the orignal payload buffer will be overwritten whilst
// constructing the PUBLISH packet.
// Allocate the correct amount of memory for the payload copy
byte* p = (byte*)malloc(length);
// Copy the payload to the new buffer
memcpy(p,payload,length);
client.publish("outTopic", p, length);
// Free the memory
free(p);
}
void setup()
{
Ethernet.begin(mac, ip);
if (client.connect("arduinoClient")) {
client.publish("outTopic","hello world");
client.subscribe("inTopic");
}
}
void loop()
{
client.loop();
}

4
tests/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.build
tmpbin
logs
*.pyc

60
tests/README.md Normal file
View File

@ -0,0 +1,60 @@
## Arduino Client for MQTT Test Suite
This is a regression test suite for the `PubSubClient` library.
It is a work-in-progress and is subject to complete refactoring as the whim takes
me.
Without a suitable arduino plugged in, the test suite will only check the
example sketches compile cleanly against the library.
With an arduino plugged in, each sketch that has a corresponding python
test case is built, uploaded and then the tests run.
## Dependencies
- Python 2.7+
- [INO Tool](http://inotool.org/) - this provides command-line build/upload of Arduino sketches
## Running
The test suite _does not_ run an MQTT server - it is assumed to be running already.
$ python testsuite.py
A summary of activity is printed to the console. More comprehensive logs are written
to the `logs` directory.
## What it does
For each sketch in the library's `examples` directory, e.g. `mqtt_basic.ino`, the suite looks for a matching test case
`testcases/mqtt_basic.py`.
The test case must follow these conventions:
- sub-class `unittest.TestCase`
- provide the class methods `setUpClass` and `tearDownClass` (TODO: make this optional)
- all test method names begin with `test_`
The suite will call the `setUpClass` method _before_ uploading the sketch. This
allows any test setup to be performed before the sketch runs - such as connecting
a client and subscribing to topics.
## Settings
The file `testcases/settings.py` is used to config the test environment.
- `server_ip` - the IP address of the broker the client should connect to (the broker port is assumed to be 1883).
- `arduino_ip` - the IP address the arduino should use (when not testing DHCP).
Before each sketch is compiled, these values are automatically substituted in. To
do this, the suite looks for lines that _start_ with the following:
byte server[] = {
byte ip[] = {
and replaces them with the appropriate values.

View File

View File

@ -0,0 +1,43 @@
import unittest
import settings
import time
import mosquitto
import serial
def on_message(mosq, obj, msg):
obj.message_queue.append(msg)
class mqtt_basic(unittest.TestCase):
message_queue = []
@classmethod
def setUpClass(self):
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self)
self.client.connect(settings.server_ip)
self.client.on_message = on_message
self.client.subscribe("outTopic",0)
@classmethod
def tearDownClass(self):
self.client.disconnect()
def test_one(self):
i=30
while len(self.message_queue) == 0 and i > 0:
self.client.loop()
time.sleep(0.5)
i -= 1
self.assertTrue(i>0, "message receive timed-out")
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
msg = self.message_queue[0]
self.assertEqual(msg.mid,0,"message id not 0")
self.assertEqual(msg.topic,"outTopic","message topic incorrect")
self.assertEqual(msg.payload,"hello world")
self.assertEqual(msg.qos,0,"message qos not 0")
self.assertEqual(msg.retain,False,"message retain flag incorrect")

View File

@ -0,0 +1,64 @@
import unittest
import settings
import time
import mosquitto
import serial
def on_message(mosq, obj, msg):
obj.message_queue.append(msg)
class mqtt_publish_in_callback(unittest.TestCase):
message_queue = []
@classmethod
def setUpClass(self):
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self)
self.client.connect(settings.server_ip)
self.client.on_message = on_message
self.client.subscribe("outTopic",0)
@classmethod
def tearDownClass(self):
self.client.disconnect()
def test_connect(self):
i=30
while len(self.message_queue) == 0 and i > 0:
self.client.loop()
time.sleep(0.5)
i -= 1
self.assertTrue(i>0, "message receive timed-out")
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
msg = self.message_queue.pop(0)
self.assertEqual(msg.mid,0,"message id not 0")
self.assertEqual(msg.topic,"outTopic","message topic incorrect")
self.assertEqual(msg.payload,"hello world")
self.assertEqual(msg.qos,0,"message qos not 0")
self.assertEqual(msg.retain,False,"message retain flag incorrect")
def test_publish(self):
self.assertEqual(len(self.message_queue), 0, "message queue not empty")
payload = "abcdefghij"
self.client.publish("inTopic",payload)
i=30
while len(self.message_queue) == 0 and i > 0:
self.client.loop()
time.sleep(0.5)
i -= 1
self.assertTrue(i>0, "message receive timed-out")
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
msg = self.message_queue.pop(0)
self.assertEqual(msg.mid,0,"message id not 0")
self.assertEqual(msg.topic,"outTopic","message topic incorrect")
self.assertEqual(msg.payload,payload)
self.assertEqual(msg.qos,0,"message qos not 0")
self.assertEqual(msg.retain,False,"message retain flag incorrect")

View File

@ -0,0 +1,2 @@
server_ip = "172.16.0.2"
arduino_ip = "172.16.0.100"

179
tests/testsuite.py Normal file
View File

@ -0,0 +1,179 @@
#!/usr/bin/env python
import os
import os.path
import sys
import shutil
from subprocess import call
import importlib
import unittest
import re
from testcases import settings
class Workspace(object):
def __init__(self):
self.root_dir = os.getcwd()
self.build_dir = os.path.join(self.root_dir,"tmpbin");
self.log_dir = os.path.join(self.root_dir,"logs");
self.tests_dir = os.path.join(self.root_dir,"testcases");
self.examples_dir = os.path.join(self.root_dir,"../PubSubClient/examples")
self.examples = []
self.tests = []
if not os.path.isdir("../PubSubClient"):
raise Exception("Cannot find PubSubClient library")
try:
import ino
except:
raise Exception("ino tool not installed")
def init(self):
if os.path.isdir(self.build_dir):
shutil.rmtree(self.build_dir)
os.mkdir(self.build_dir)
if os.path.isdir(self.log_dir):
shutil.rmtree(self.log_dir)
os.mkdir(self.log_dir)
os.chdir(self.build_dir)
call(["ino","init"])
shutil.copytree("../../PubSubClient","lib/PubSubClient")
filenames = []
for root, dirs, files in os.walk(self.examples_dir):
filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
filenames.sort()
for e in filenames:
self.examples.append(Sketch(self,e))
filenames = []
for root, dirs, files in os.walk(self.tests_dir):
filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
filenames.sort()
for e in filenames:
self.tests.append(Sketch(self,e))
def clean(self):
shutil.rmtree(self.build_dir)
class Sketch(object):
def __init__(self,wksp,fn):
self.w = wksp
self.filename = fn
self.basename = os.path.basename(self.filename)
self.build_log = os.path.join(self.w.log_dir,"%s.log"%(os.path.basename(self.filename),))
self.build_err_log = os.path.join(self.w.log_dir,"%s.err.log"%(os.path.basename(self.filename),))
self.build_upload_log = os.path.join(self.w.log_dir,"%s.upload.log"%(os.path.basename(self.filename),))
def build(self):
sys.stdout.write(" Build: ")
sys.stdout.flush()
# Copy sketch over, replacing IP addresses as necessary
fin = open(self.filename,"r")
lines = fin.readlines()
fin.close()
fout = open(os.path.join(self.w.build_dir,"src","sketch.ino"),"w")
for l in lines:
if re.match(r"^byte server\[\] = {",l):
fout.write("byte server[] = { %s };\n"%(settings.server_ip.replace(".",", "),))
elif re.match(r"^byte ip\[\] = {",l):
fout.write("byte ip[] = { %s };\n"%(settings.arduino_ip.replace(".",", "),))
else:
fout.write(l)
fout.flush()
fout.close()
# Run build
fout = open(self.build_log, "w")
ferr = open(self.build_err_log, "w")
rc = call(["ino","build"],stdout=fout,stderr=ferr)
fout.close()
ferr.close()
if rc == 0:
sys.stdout.write("pass")
sys.stdout.write("\n")
return True
else:
sys.stdout.write("fail")
sys.stdout.write("\n")
with open(self.build_err_log) as f:
for line in f:
print " ",line,
return False
def upload(self):
sys.stdout.write(" Upload: ")
sys.stdout.flush()
fout = open(self.build_upload_log, "w")
rc = call(["ino","upload"],stdout=fout,stderr=fout)
fout.close()
if rc == 0:
sys.stdout.write("pass")
sys.stdout.write("\n")
return True
else:
sys.stdout.write("fail")
sys.stdout.write("\n")
with open(self.build_upload_log) as f:
for line in f:
print " ",line,
return False
def test(self):
# import the matching test case, if it exists
try:
basename = os.path.basename(self.filename)[:-4]
i = importlib.import_module("testcases."+basename)
except:
sys.stdout.write(" Test: no tests found")
sys.stdout.write("\n")
return
c = getattr(i,basename)
testmethods = [m for m in dir(c) if m.startswith("test_")]
testmethods.sort()
tests = []
for m in testmethods:
tests.append(c(m))
result = unittest.TestResult()
c.setUpClass()
if self.upload():
sys.stdout.write(" Test: ")
sys.stdout.flush()
for t in tests:
t.run(result)
print "%d/%d"%(result.testsRun-len(result.failures)-len(result.errors),result.testsRun)
if not result.wasSuccessful():
if len(result.failures) > 0:
for f in result.failures:
print "-- %s"%(str(f[0]),)
print f[1]
if len(result.errors) > 0:
print " Errors:"
for f in result.errors:
print "-- %s"%(str(f[0]),)
print f[1]
c.tearDownClass()
if __name__ == '__main__':
run_tests = True
w = Workspace()
w.init()
for e in w.examples:
print "--------------------------------------"
print "[%s]"%(e.basename,)
if e.build() and run_tests:
e.test()
for e in w.tests:
print "--------------------------------------"
print "[%s]"%(e.basename,)
if e.build() and run_tests:
e.test()
w.clean()