Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
41 changed files with 484 additions and 2597 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
_site/
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
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
|
|
||||||
1.6
|
|
||||||
* Added the ability to publish a retained message
|
|
||||||
|
|
||||||
1.5
|
|
||||||
* Added default constructor
|
|
||||||
* Fixed compile error when used with arduino-0021 or later
|
|
||||||
|
|
||||||
1.4
|
|
||||||
* Fixed connection lost handling
|
|
||||||
|
|
||||||
1.3
|
|
||||||
* Fixed packet reading bug in PubSubClient.readPacket
|
|
||||||
|
|
||||||
1.2
|
|
||||||
* Fixed compile error when used with arduino-0016 or later
|
|
||||||
|
|
||||||
|
|
||||||
1.1
|
|
||||||
* Reduced size of library
|
|
||||||
* Added support for Will messages
|
|
||||||
* Clarified licensing - see LICENSE.txt
|
|
||||||
|
|
||||||
|
|
||||||
1.0
|
|
||||||
* Only Quality of Service (QOS) 0 messaging is supported
|
|
||||||
* The maximum message size, including header, is 128 bytes
|
|
||||||
* The keepalive interval is set to 30 seconds
|
|
||||||
* No support for Will messages
|
|
||||||
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
@ -1,427 +0,0 @@
|
||||||
/*
|
|
||||||
PubSubClient.cpp - A simple client for MQTT.
|
|
||||||
Nicholas O'Leary
|
|
||||||
http://knolleary.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient() {
|
|
||||||
this->_client = NULL;
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
this->domain = NULL;
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, void (*callback)(char*,uint8_t*,unsigned int), Client& client, Stream& stream) {
|
|
||||||
this->_client = &client;
|
|
||||||
this->callback = callback;
|
|
||||||
this->ip = ip;
|
|
||||||
this->port = port;
|
|
||||||
this->domain = NULL;
|
|
||||||
this->stream = &stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient(char* domain, uint16_t port, void (*callback)(char*,uint8_t*,unsigned int), Client& client, Stream& stream) {
|
|
||||||
this->_client = &client;
|
|
||||||
this->callback = callback;
|
|
||||||
this->domain = domain;
|
|
||||||
this->port = port;
|
|
||||||
this->stream = &stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
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};
|
|
||||||
// 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) {
|
|
||||||
v = 0x06|(willQos<<3)|(willRetain<<5);
|
|
||||||
} else {
|
|
||||||
v = 0x02;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(user != NULL) {
|
|
||||||
length = writeString(user,buffer,length);
|
|
||||||
if(pass != NULL) {
|
|
||||||
length = writeString(pass,buffer,length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t llen;
|
|
||||||
uint16_t len = readPacket(&llen);
|
|
||||||
|
|
||||||
if (len == 4 && buffer[3] == 0) {
|
|
||||||
lastInActivity = millis();
|
|
||||||
pingOutstanding = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_client->stop();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t PubSubClient::readByte() {
|
|
||||||
while(!_client->available()) {}
|
|
||||||
return _client->read();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
|
|
||||||
uint16_t len = 0;
|
|
||||||
buffer[len++] = readByte();
|
|
||||||
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
|
|
||||||
uint32_t multiplier = 1;
|
|
||||||
uint16_t length = 0;
|
|
||||||
uint8_t digit = 0;
|
|
||||||
uint16_t skip = 0;
|
|
||||||
uint8_t start = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
digit = readByte();
|
|
||||||
buffer[len++] = digit;
|
|
||||||
length += (digit & 127) * multiplier;
|
|
||||||
multiplier *= 128;
|
|
||||||
} while ((digit & 128) != 0);
|
|
||||||
*lengthLength = len-1;
|
|
||||||
|
|
||||||
if (isPublish) {
|
|
||||||
// Read in topic length to calculate bytes to skip over for Stream writing
|
|
||||||
buffer[len++] = readByte();
|
|
||||||
buffer[len++] = readByte();
|
|
||||||
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
|
|
||||||
start = 2;
|
|
||||||
if (buffer[0]&MQTTQOS1) {
|
|
||||||
// skip message id
|
|
||||||
skip += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t i = start;i<length;i++) {
|
|
||||||
digit = readByte();
|
|
||||||
if (this->stream) {
|
|
||||||
if (isPublish && len-*lengthLength-2>skip) {
|
|
||||||
this->stream->write(digit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len < MQTT_MAX_PACKET_SIZE) {
|
|
||||||
buffer[len] = digit;
|
|
||||||
}
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
|
|
||||||
len = 0; // This will cause the packet to be ignored.
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::loop() {
|
|
||||||
if (connected()) {
|
|
||||||
unsigned long t = millis();
|
|
||||||
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
|
|
||||||
if (pingOutstanding) {
|
|
||||||
_client->stop();
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
buffer[0] = MQTTPINGREQ;
|
|
||||||
buffer[1] = 0;
|
|
||||||
_client->write(buffer,2);
|
|
||||||
lastOutActivity = t;
|
|
||||||
lastInActivity = t;
|
|
||||||
pingOutstanding = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_client->available()) {
|
|
||||||
uint8_t llen;
|
|
||||||
uint16_t len = readPacket(&llen);
|
|
||||||
uint16_t msgId = 0;
|
|
||||||
uint8_t *payload;
|
|
||||||
if (len > 0) {
|
|
||||||
lastInActivity = t;
|
|
||||||
uint8_t type = buffer[0]&0xF0;
|
|
||||||
if (type == MQTTPUBLISH) {
|
|
||||||
if (callback) {
|
|
||||||
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2];
|
|
||||||
char topic[tl+1];
|
|
||||||
for (uint16_t i=0;i<tl;i++) {
|
|
||||||
topic[i] = buffer[llen+3+i];
|
|
||||||
}
|
|
||||||
topic[tl] = 0;
|
|
||||||
// msgId only present for QOS>0
|
|
||||||
if ((buffer[0]&0x06) == MQTTQOS1) {
|
|
||||||
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
|
|
||||||
payload = buffer+llen+3+tl+2;
|
|
||||||
callback(topic,payload,len-llen-3-tl-2);
|
|
||||||
|
|
||||||
buffer[0] = MQTTPUBACK;
|
|
||||||
buffer[1] = 2;
|
|
||||||
buffer[2] = (msgId >> 8);
|
|
||||||
buffer[3] = (msgId & 0xFF);
|
|
||||||
_client->write(buffer,4);
|
|
||||||
lastOutActivity = t;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
payload = buffer+llen+3+tl;
|
|
||||||
callback(topic,payload,len-llen-3-tl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type == MQTTPINGREQ) {
|
|
||||||
buffer[0] = MQTTPINGRESP;
|
|
||||||
buffer[1] = 0;
|
|
||||||
_client->write(buffer,2);
|
|
||||||
} else if (type == MQTTPINGRESP) {
|
|
||||||
pingOutstanding = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(char* topic, char* payload) {
|
|
||||||
return publish(topic,(uint8_t*)payload,strlen(payload),false);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(char* topic, uint8_t* payload, unsigned int plength) {
|
|
||||||
return publish(topic, payload, plength, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(char* topic, uint8_t* payload, unsigned int plength, boolean retained) {
|
|
||||||
if (connected()) {
|
|
||||||
// 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) {
|
|
||||||
header |= 1;
|
|
||||||
}
|
|
||||||
return write(header,buffer,length-5);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish_P(char* topic, uint8_t* PROGMEM payload, unsigned int plength, boolean retained) {
|
|
||||||
uint8_t llen = 0;
|
|
||||||
uint8_t digit;
|
|
||||||
unsigned int rc = 0;
|
|
||||||
uint16_t tlen;
|
|
||||||
unsigned int pos = 0;
|
|
||||||
unsigned 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
lastOutActivity = millis();
|
|
||||||
|
|
||||||
return rc == tlen + 4 + 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::subscribe(char* topic) {
|
|
||||||
return subscribe(topic, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::subscribe(char* topic, uint8_t qos) {
|
|
||||||
if (qos < 0 || qos > 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (connected()) {
|
|
||||||
// Leave room in the buffer for header and variable length field
|
|
||||||
uint16_t length = 5;
|
|
||||||
nextMsgId++;
|
|
||||||
if (nextMsgId == 0) {
|
|
||||||
nextMsgId = 1;
|
|
||||||
}
|
|
||||||
buffer[length++] = (nextMsgId >> 8);
|
|
||||||
buffer[length++] = (nextMsgId & 0xFF);
|
|
||||||
length = writeString(topic, buffer,length);
|
|
||||||
buffer[length++] = qos;
|
|
||||||
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::unsubscribe(char* topic) {
|
|
||||||
if (connected()) {
|
|
||||||
uint16_t length = 5;
|
|
||||||
nextMsgId++;
|
|
||||||
if (nextMsgId == 0) {
|
|
||||||
nextMsgId = 1;
|
|
||||||
}
|
|
||||||
buffer[length++] = (nextMsgId >> 8);
|
|
||||||
buffer[length++] = (nextMsgId & 0xFF);
|
|
||||||
length = writeString(topic, buffer,length);
|
|
||||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PubSubClient::disconnect() {
|
|
||||||
buffer[0] = MQTTDISCONNECT;
|
|
||||||
buffer[1] = 0;
|
|
||||||
_client->write(buffer,2);
|
|
||||||
_client->stop();
|
|
||||||
lastInActivity = lastOutActivity = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t PubSubClient::writeString(char* string, uint8_t* buf, uint16_t pos) {
|
|
||||||
char* idp = string;
|
|
||||||
uint16_t i = 0;
|
|
||||||
pos += 2;
|
|
||||||
while (*idp) {
|
|
||||||
buf[pos++] = *idp++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
buf[pos-i-2] = (i >> 8);
|
|
||||||
buf[pos-i-1] = (i & 0xFF);
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean PubSubClient::connected() {
|
|
||||||
boolean rc;
|
|
||||||
if (_client == NULL ) {
|
|
||||||
rc = false;
|
|
||||||
} else {
|
|
||||||
rc = (int)_client->connected();
|
|
||||||
if (!rc) _client->stop();
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
/*
|
|
||||||
PubSubClient.h - A simple client for MQTT.
|
|
||||||
Nicholas O'Leary
|
|
||||||
http://knolleary.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PubSubClient_h
|
|
||||||
#define PubSubClient_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "Client.h"
|
|
||||||
#include "Stream.h"
|
|
||||||
|
|
||||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size
|
|
||||||
#define MQTT_MAX_PACKET_SIZE 128
|
|
||||||
|
|
||||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds
|
|
||||||
#define MQTT_KEEPALIVE 60
|
|
||||||
|
|
||||||
#define MQTTPROTOCOLVERSION 3
|
|
||||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
|
||||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
|
||||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
|
||||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
|
||||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
|
||||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
|
||||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
|
||||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
|
||||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
|
||||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
|
||||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
|
||||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
|
||||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
|
||||||
#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:
|
|
||||||
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*,unsigned int);
|
|
||||||
uint16_t readPacket(uint8_t*);
|
|
||||||
uint8_t readByte();
|
|
||||||
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;
|
|
||||||
Stream* stream;
|
|
||||||
public:
|
|
||||||
PubSubClient();
|
|
||||||
PubSubClient(uint8_t *, uint16_t, void(*)(char*,uint8_t*,unsigned int),Client& client);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, void(*)(char*,uint8_t*,unsigned int),Client& client, Stream&);
|
|
||||||
PubSubClient(char*, uint16_t, void(*)(char*,uint8_t*,unsigned int),Client& client);
|
|
||||||
PubSubClient(char*, uint16_t, void(*)(char*,uint8_t*,unsigned int),Client& client, Stream&);
|
|
||||||
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();
|
|
||||||
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 subscribe(char *, uint8_t qos);
|
|
||||||
boolean unsubscribe(char *);
|
|
||||||
boolean loop();
|
|
||||||
boolean connected();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
Basic MQTT example
|
|
||||||
|
|
||||||
- connects to an MQTT server
|
|
||||||
- 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")) {
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
client.loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
Example of using a Stream object to store the message payload
|
|
||||||
|
|
||||||
Uses SRAM library: https://github.com/ennui2342/arduino-sram
|
|
||||||
but could use any Stream based class such as SD
|
|
||||||
|
|
||||||
- connects to an MQTT server
|
|
||||||
- publishes "hello world" to the topic "outTopic"
|
|
||||||
- subscribes to the topic "inTopic"
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
#include <SRAM.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 };
|
|
||||||
|
|
||||||
SRAM sram(4, SRAM_1024);
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
sram.seek(1);
|
|
||||||
|
|
||||||
// do something with the message
|
|
||||||
for(uint8_t i=0; i<length; i++) {
|
|
||||||
Serial.write(sram.read());
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// Reset position for the next message to be stored
|
|
||||||
sram.seek(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
EthernetClient ethClient;
|
|
||||||
PubSubClient client(server, 1883, callback, ethClient, sram);
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
if (client.connect("arduinoClient")) {
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
}
|
|
||||||
|
|
||||||
sram.begin();
|
|
||||||
sram.seek(1);
|
|
||||||
|
|
||||||
Serial.begin(9600);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
client.loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
#######################################
|
|
||||||
# Syntax Coloring Map For Ultrasound
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Datatypes (KEYWORD1)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
PubSubClient KEYWORD1
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Methods and Functions (KEYWORD2)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
connect KEYWORD2
|
|
||||||
disconnect KEYWORD2
|
|
||||||
publish KEYWORD2
|
|
||||||
subscribe KEYWORD2
|
|
||||||
loop KEYWORD2
|
|
||||||
connected KEYWORD2
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Constants (LITERAL1)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
4
README
4
README
|
|
@ -1,4 +0,0 @@
|
||||||
A client library for the Arduino Ethernet Shield that provides support for MQTT.
|
|
||||||
Nicholas O'Leary
|
|
||||||
http://knolleary.net/arduino-client-for-mqtt/
|
|
||||||
|
|
||||||
61
_layouts/default.html
Normal file
61
_layouts/default.html
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
|
||||||
|
<head profile="http://gmpg.org/xfn/11">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>Arduino Client for MQTT « knolleary</title>
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Questrial' rel='stylesheet' type='text/css'>
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Lora:400,400italic' rel='stylesheet' type='text/css'>
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
@import url( http://knolleary.net/blog/wp-content/themes/wp-knolleary-theme/style.css );
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<link href='style.css' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
|
|
||||||
|
<script type='text/javascript' src='http://knolleary.net/blog/wp-includes/js/jquery/jquery.js?ver=1.7.2'></script>
|
||||||
|
|
||||||
|
<meta name="HandheldFriendly" content="true" />
|
||||||
|
<meta name="viewport" content="width=device-width, height=device-height, user-scalable=no" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="banner"><div class="nav"><ul><li class="{% if page.title == 'Arduino Client for MQTT' %}active{% endif %}"><a href="./index.html">Arduino Client for MQTT</a></li><!--<li class="{% if page.title == 'Tutorial' %}active{% endif %}"><a href="./tutorial.html">Tutorial</a></li>--><li class="{% if page.title == 'API Docs' %}active{% endif %}"><a href="./api.html">API Docs</a></li></ul><h1><a href="http://knolleary.net/">knolleary</a></h1></div></div>
|
||||||
|
<div id="header"><h1><a href="http://knolleary.net/">knolleary</a></h1><h2>stuff by nick o'leary</h2>
|
||||||
|
<div class="nav">
<ul><li class="{% if page.title == 'Arduino Client for MQTT' %}active{% endif %}"><a href="./index.html">Arduino Client for MQTT</a></li><!--<li class="{% if page.title == 'Tutorial' %}active{% endif %}"><a href="./tutorial.html">Tutorial</a></li>--><li class="{% if page.title == 'API Docs' %}active{% endif %}"><a href="./api.html">API Docs</a></li></ul></div>
|
||||||
|
<div class="clearfloat"></div>
|
||||||
|
</div><!-- end header div -->
|
||||||
|
<div id="content">
|
||||||
|
<!-- end header -->
|
||||||
|
<div class="page type-page status-publish hentry">
|
||||||
|
<div class="storypage">
|
||||||
|
<div class="title"><a href="http://knolleary.net/arduino-client-for-mqtt/" rel="bookmark">{{ page.title }}</a></div>
|
||||||
|
|
||||||
|
<div class="storycontent">
{{ content }}
|
||||||
|
</div><!-- end storycontent -->
|
||||||
|
<div class="clearfloat"></div>
|
||||||
|
</div><!-- end story -->
|
||||||
|
</div><!-- end post -->
|
||||||
|
|
||||||
|
<!-- begin footer -->
|
||||||
|
</div> <!-- end content div -->
|
||||||
|
|
||||||
|
<div id="menu">
|
||||||
|
<div id="menu-container">
|
||||||
|
<div id="menu-col1">
<div class="menu-block mbtop">
</div></div>
|
||||||
|
<div id="menu-col2">
|
||||||
|
<div class="menu-block mbtop">
|
||||||
|
finally<ul><li>The postings on this site are my own and don’t necessarily represent IBM’s positions, strategies or opinions.</li></ul>
|
||||||
|
<ul><li><a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.0/uk/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-sa/2.0/uk/80x15.png" /></a><br />All content on this site is licenced under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/2.0/uk/">Creative Commons Licence</a>.</li></ul>
|
||||||
|
</div> <!-- /menu-block -->
|
||||||
|
</div><!--end col2 -->
|
||||||
|
<div id="menu-col3"><div class="menu-block mbtop">
</div></div>
|
||||||
|
</div> <!-- end menu-container div -->
|
||||||
|
<div class="clearfloat"></div>
|
||||||
|
</div> <!-- end menu div -->
|
||||||
|
<!-- end sidebar -->
|
||||||
|
|
||||||
|
<div id="footer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
278
api.html
Normal file
278
api.html
Normal file
|
|
@ -0,0 +1,278 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: API Docs
|
||||||
|
---
|
||||||
|
<p><i>These docs refer to the latest version of the library on <a href="https://github.com/knolleary/pubsubclient">GitHub</a></i></p>
|
||||||
|
|
||||||
|
<section class="method" id="toc">
|
||||||
|
<h5>Constructors</h5>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#PubSubClient"><span class="methodname">PubSubClient</span> <span class="methodparams">(server, port, callback, client)</span></a></li>
|
||||||
|
<li><a href="#PubSubClient2"><span class="methodname">PubSubClient</span> <span class="methodparams">(serverDNS, port, callback, client)</span></a></li>
|
||||||
|
</ul>
|
||||||
|
<h5>Functions</h5>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#connect1"><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID)</span></a></li>
|
||||||
|
<li><a href="#connect2"><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID, willTopic, willQoS, willRetain, willMessage)</span></a></li>
|
||||||
|
<li><a href="#connect3"><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID, username, password)</span></a></li>
|
||||||
|
<li><a href="#connect4"><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID, username, password, willTopic, willQoS, willRetain, willMessage)</span></a></li>
|
||||||
|
<li><a href="#disconnect"><span class="methodreturn">void</span> <span class="methodname">disconnect</span> <span class="methodparams">()</span></a></li>
|
||||||
|
<li><a href="#publish1"><span class="methodreturn">int</span> <span class="methodname">publish</span> <span class="methodparams">(topic, payload)</span></a></li>
|
||||||
|
<li><a href="#publish2"><span class="methodreturn">int</span> <span class="methodname">publish</span> <span class="methodparams">(topic, payload, length)</span></a></li>
|
||||||
|
<li><a href="#publish3"><span class="methodreturn">int</span> <span class="methodname">publish</span> <span class="methodparams">(topic, payload, length, retained)</span></a></li>
|
||||||
|
<li><a href="#publish4"><span class="methodreturn">int</span> <span class="methodname">publish_P</span> <span class="methodparams">(topic, payload, length, retained)</span></a></li>
|
||||||
|
<li><a href="#subscribe"><span class="methodreturn">boolean</span> <span class="methodname">subscribe</span> <span class="methodparams">(topic)</span></a></li>
|
||||||
|
<li><a href="#loop"><span class="methodreturn">boolean</span> <span class="methodname">loop</span> <span class="methodparams">()</span></a></li>
|
||||||
|
<li><a href="#connected"><span class="methodreturn">int</span> <span class="methodname">connected</span> <span class="methodparams">()</span></a></li>
|
||||||
|
</ul>
|
||||||
|
<h5>Other</h5>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#configoptions">Configuration Options</a></li>
|
||||||
|
<li><a href="#callback">Subscription Callback</a></li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="PubSubClient">
|
||||||
|
<h4><span class="methodname">PubSubClient</span> <span class="methodparams">(server, port, callback, client)</span></h4>
|
||||||
|
<p>Creates a client instance, with the server specified by IP address.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>server : the IP address of the server (array of 4 bytes)</li>
|
||||||
|
<li>port : the port to connect to (int)</li>
|
||||||
|
<li>callback : a pointer to a function called when a message arrives for a subscription created by this client. If no callback is required, set this to 0. See <a href="#callback">Subscription Callback</a>.</li>
|
||||||
|
<li>client : an instance of <code>Client</code>, typically <code>EthernetClient</code>.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="PubSubClient2">
|
||||||
|
<h4><span class="methodname">PubSubClient</span> <span class="methodparams">(serverDNS, port, callback, client)</span></h4>
|
||||||
|
<p>Creates a client instance, with the server specified by DNS name.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>serverDNS : the DNS name of the server (char*)</li>
|
||||||
|
<li>port : the port to connect to (int)</li>
|
||||||
|
<li>callback : a pointer to a function called when a message arrives for a subscription created by this client. If no callback is required, set this to 0. See <a href="#callback">Subscription Callback</a>.</li>
|
||||||
|
<li>client : an instance of <code>Client</code>, typically <code>EthernetClient</code>.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="connect1">
|
||||||
|
<h4><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID)</span></h4>
|
||||||
|
<p>Connects the client.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>clientID : the client ID to use when connecting to the server. As per MQTT, this must be between 1 and 23 characters long.</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – connection failed.</li>
|
||||||
|
<li>true – connection succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="connect2">
|
||||||
|
<h4><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID, willTopic, willQoS, willRetain, willMessage)</span></h4>
|
||||||
|
<p>Connects the client with a Will message specified.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>clientID : the client ID to use when connecting to the server. As per MQTT, this must be between 1 and 23 characters long.</li>
|
||||||
|
<li>willTopic : the topic to be used by the will message (char*)</li>
|
||||||
|
<li>willQoS : the quality of service to be used by the will message (int : 0,1 or 2)</li>
|
||||||
|
<li>willRetain : whether the will should be published with the retain flag (int : 0 or 1)</li>
|
||||||
|
<li>willMessage : the payload of the will message (char*)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – connection failed.</li>
|
||||||
|
<li>true – connection succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="connect3">
|
||||||
|
<h4><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID, username, password)</span></h4>
|
||||||
|
<p>Connects the client with a username and password specified.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>clientID : the client ID to use when connecting to the server. As per MQTT, this must be between 1 and 23 characters long.</li>
|
||||||
|
<li>username : the username to use. If NULL, no username or password is used (char*)</li>
|
||||||
|
<li>password : the password to use. If NULL, no password is used (char*)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – connection failed.</li>
|
||||||
|
<li>true – connection succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="connect4">
|
||||||
|
<h4><span class="methodreturn">boolean</span> <span class="methodname">connect</span> <span class="methodparams">(clientID, username, password, willTopic, willQoS, willRetain, willMessage)</span></h4>
|
||||||
|
<p>Connects the client with a Will message, username and password specified.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>clientID : the client ID to use when connecting to the server. As per MQTT, this must be between 1 and 23 characters long.</li>
|
||||||
|
<li>username : the username to use. If NULL, no username or password is used (char*)</li>
|
||||||
|
<li>password : the password to use. If NULL, no password is used (char*)</li>
|
||||||
|
<li>willTopic : the topic to be used by the will message (char*)</li>
|
||||||
|
<li>willQoS : the quality of service to be used by the will message (int : 0,1 or 2)</li>
|
||||||
|
<li>willRetain : whether the will should be published with the retain flag (int : 0 or 1)</li>
|
||||||
|
<li>willMessage : the payload of the will message (char*)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – connection failed.</li>
|
||||||
|
<li>true – connection succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="disconnect">
|
||||||
|
<h4><span class="methodreturn">void</span> <span class="methodname">disconnect</span> <span class="methodparams">()</span></h4>
|
||||||
|
<p>Disconnects the client.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="publish1">
|
||||||
|
<h4><span class="methodreturn">int</span> <span class="methodname">publish</span> <span class="methodparams">(topic, payload)</span></h4>
|
||||||
|
<p>Publishes a string message to the specified topic.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>topic – the topic to publish to (char*)</li>
|
||||||
|
<li>payload – the message to publish (char*)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – publish failed.</li>
|
||||||
|
<li>true – publish succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="publish2">
|
||||||
|
<h4><span class="methodreturn">int</span> <span class="methodname">publish</span> <span class="methodparams">(topic, payload, length)</span></h4>
|
||||||
|
<p>Publishes a message to the specified topic.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>topic – the topic to publish to (char*)</li>
|
||||||
|
<li>payload – the message to publish (byte array)</li>
|
||||||
|
<li>length – the length of the message (byte)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – publish failed.</li>
|
||||||
|
<li>true – publish succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="publish3">
|
||||||
|
<h4><span class="methodreturn">int</span> <span class="methodname">publish</span> <span class="methodparams">(topic, payload, length, retained)</span></h4>
|
||||||
|
<p>Publishes a message to the specified topic, with the retained flag as specified.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>topic – the topic to publish to (char*)</li>
|
||||||
|
<li>payload – the message to publish (byte array)</li>
|
||||||
|
<li>length – the length of the message (byte)</li>
|
||||||
|
<li>retained – whether the message should be retained (byte)
|
||||||
|
<ul>
|
||||||
|
<li>0 – not retained</li>
|
||||||
|
<li>1 – retained</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – publish failed.</li>
|
||||||
|
<li>true – publish succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="publish4">
|
||||||
|
<h4><span class="methodreturn">int</span> <span class="methodname">publish_P</span> <span class="methodparams">(topic, payload, length, retained)</span></h4>
|
||||||
|
<p>Publishes a message stored in <code>PROGMEN</code> to the specified topic, with the retained flag as specified.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>topic – the topic to publish to (char*)</li>
|
||||||
|
<li>payload – the message to publish (PROGMEM byte array)</li>
|
||||||
|
<li>length – the length of the message (byte)</li>
|
||||||
|
<li>retained – whether the message should be retained (byte)
|
||||||
|
<ul>
|
||||||
|
<li>0 – not retained</li>
|
||||||
|
<li>1 – retained</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – publish failed.</li>
|
||||||
|
<li>true – publish succeeded.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="subscribe">
|
||||||
|
<h4><span class="methodreturn">boolean</span> <span class="methodname">subscribe</span> <span class="methodparams">(topic)</span></h4>
|
||||||
|
<p>Subscribes to messages published to the specified topic.</p>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>topic – the topic to publish to (char*)</li>
|
||||||
|
</ul>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – sending the subscribe failed.</li>
|
||||||
|
<li>true – sending the subscribe succeeded. The request completes asynchronously.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="loop">
|
||||||
|
<h4><span class="methodreturn">boolean</span> <span class="methodname">loop</span> <span class="methodparams">()</span></h4>
|
||||||
|
<p>This should be called regularly to allow the client to process incoming messages and maintain its connection to the server.</p>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – the client is no longer connected</li>
|
||||||
|
<li>true – the client is still connected</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="connected">
|
||||||
|
<h4><span class="methodreturn">int</span> <span class="methodname">connected</span> <span class="methodparams">()</span></h4>
|
||||||
|
<p>Checks whether the client is connected to the server.</p>
|
||||||
|
<h5>Returns</h5>
|
||||||
|
<ul>
|
||||||
|
<li>false – the client is no longer connected</li>
|
||||||
|
<li>true – the client is still connected</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="configoptions">
|
||||||
|
<h4>Configuration Options</h4>
|
||||||
|
<p>The following configuration options can be used to configure the library. They are contained in <code>PubSubClient.h</code>.</p>
|
||||||
|
<dl>
|
||||||
|
<dt><code>MQTT_MAX_PACKET_SIZE</code></dt>
|
||||||
|
<dd>Sets the largest packet size, in bytes, the client will handle. Any packet received that exceeds this size will be ignored.</p>
|
||||||
|
<p>Default: 128 bytes
|
||||||
|
</dd>
|
||||||
|
<dt><code>MQTT_KEEPALIVE</code></dt>
|
||||||
|
<dd>Sets the keepalive interval, in seconds, the client will use. This is used to maintain the connection when no other packets are being<br />
|
||||||
|
sent or received.</p>
|
||||||
|
<p>Default: 15 seconds
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="method" id="callback">
|
||||||
|
<h4>Subscription Callback</h4>
|
||||||
|
<p>If the client is used to subscribe to topics, a callback function must be provided in the constructor. This function
|
||||||
|
is called when new messages arrive at the client.</p>
|
||||||
|
<p>The callback function has the following signature:
|
||||||
|
<pre>
|
||||||
|
void callback(char* topic, byte* payload, unsigned int length)
|
||||||
|
</pre>
|
||||||
|
<h5>Parameters</h5>
|
||||||
|
<ul>
|
||||||
|
<li>topic – the topic the message arrived on (char*)</li>
|
||||||
|
<li>payload – the message payload (byte array)</li>
|
||||||
|
<li>length – the length of the message payload (unsigned int)</li>
|
||||||
|
</ul>
|
||||||
|
<p>Internally, the client uses the same buffer for both inbound and outbound
|
||||||
|
messages. After the callback function returns, or if a call to either <code>publish</code>
|
||||||
|
or <code>subscribe</code> is made from within the callback function, the <code>topic</code>
|
||||||
|
and <code>payload</code> values passed to the function will be overwritten. The application
|
||||||
|
should create its own copy of the values if they are required beyond this.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
103
index.html
Normal file
103
index.html
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Arduino Client for MQTT
|
||||||
|
---
|
||||||
|
<p>This library provides a client for doing simple publish/subscribe messaging with a server that supports MQTT v3.</p>
|
||||||
|
<p>For more information about MQTT, visit <a href="http://mqtt.org">mqtt.org</a>.</p>
|
||||||
|
|
||||||
|
<section id="Download">
|
||||||
|
<h3>Download</h3>
|
||||||
|
<p>The latest version of the library can be downloaded from <a href="https://github.com/knolleary/pubsubclient/tags">GitHub</a>.</p>
|
||||||
|
</section>
|
||||||
|
<section id="Documentation">
|
||||||
|
<h3>Documentation</h3>
|
||||||
|
<p>The library comes with a number of example sketches. See <code>File > Examples > PubSubClient</code> within the Arduino application.</p>
|
||||||
|
<p>Full <a href="api.html">API Documentation</a></p>
|
||||||
|
</section>
|
||||||
|
<section id="License">
|
||||||
|
<h3>License</h3>
|
||||||
|
<p>This library is released under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT License</a>.</p>
|
||||||
|
</section>
|
||||||
|
<section id="ChangeHistory">
|
||||||
|
<h3>Change History</h3>
|
||||||
|
<p>The complete change history is available on <a href="https://github.com/knolleary/pubsubclient/commits/master">GitHub</a>.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>1.9</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Do not split MQTT packets over multiple calls to <code>_client->write()</code></li>
|
||||||
|
<li><b><i>API change</i></b>: All constructors now require an instance of Client to be passed in.</li>
|
||||||
|
<li>Fixed example to match 1.8 api changes - dpslwk</li>
|
||||||
|
<li>Added username/password support - WilHall</li>
|
||||||
|
<li>Added <code>publish_P</code> - publishes messages from <code>PROGMEM</code> - jobytaffey</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.8</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>KeepAlive interval is configurable in <code>PubSubClient.h</code></li>
|
||||||
|
<li>Maximum packet size is configurable in <code>PubSubClient.h</code></li>
|
||||||
|
<li><b><i>API change</i></b>: Return <code>boolean</code> rather than <code>int</code> from various functions</li>
|
||||||
|
<li><b><i>API change</i></b>: Length parameter in message callback changed from <code>int</code> to <code>unsigned int</code>
|
||||||
|
<li>Various internal tidy-ups around types</li>
|
||||||
|
<li>Able to specify server address by DNS name</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.7</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Improved keepalive handling</li>
|
||||||
|
<li>Updated to the Arduino-1.0 API</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.6</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Added ability to publish retained messages</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.5</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Added default constructor</li>
|
||||||
|
<li>Fixed compile error when used with arduino-0021 or later</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.4</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed connection lost handling</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.3</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed packet reading bug</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.2</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Fixed compile error when used with arduino-0016 or later</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.1</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Reduced size of library</li>
|
||||||
|
<li>Added support for Will messages</li?
|
||||||
|
<li>Clarified licensing – see LICENSE.txt</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
<dt>1.0</dt>
|
||||||
|
<dd>
|
||||||
|
<ul>
|
||||||
|
<li>Only Quality of Service (QOS) 0 messaging is supported</li>
|
||||||
|
<li>The maximum message size, including header, is 128 bytes</li>
|
||||||
|
<li>The keepalive interval is set to 30 seconds</li>
|
||||||
|
<li>No support for Will messages</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
</section>
|
||||||
|
|
||||||
35
style.css
Normal file
35
style.css
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
section {
|
||||||
|
padding-top:40px;
|
||||||
|
margin-top: -40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method {
|
||||||
|
border-bottom: 3px solid #eee;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
.methodname {}
|
||||||
|
.methodreturn { font-weight: normal; color: #777;}
|
||||||
|
.methodparams { font-weight: normal; color: #777;}
|
||||||
|
|
||||||
|
li.active {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ChangeHistory * dd { margin-left: 25px;}
|
||||||
|
|
||||||
|
#toc a {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 { font-size: 1.3em; }
|
||||||
|
h5 {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
font-size: 1.0em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content ul {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
4
tests/.gitignore
vendored
4
tests/.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
.build
|
|
||||||
tmpbin
|
|
||||||
logs
|
|
||||||
*.pyc
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
SRC_PATH=./src
|
|
||||||
OUT_PATH=./bin
|
|
||||||
TEST_SRC=$(wildcard ${SRC_PATH}/*_spec.cpp)
|
|
||||||
TEST_BIN= $(TEST_SRC:${SRC_PATH}/%.cpp=${OUT_PATH}/%)
|
|
||||||
VPATH=${SRC_PATH}
|
|
||||||
SHIM_FILES=${SRC_PATH}/lib/*.cpp
|
|
||||||
PSC_FILE=../PubSubClient/PubSubClient.cpp
|
|
||||||
CC=g++
|
|
||||||
CFLAGS=-I${SRC_PATH}/lib -I../PubSubClient
|
|
||||||
|
|
||||||
all: $(TEST_BIN)
|
|
||||||
|
|
||||||
${OUT_PATH}/%: ${SRC_PATH}/%.cpp ${PSC_FILE} ${SHIM_FILES}
|
|
||||||
mkdir -p ${OUT_PATH}
|
|
||||||
${CC} ${CFLAGS} $^ -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@rm -rf ${OUT_PATH}
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
# Arduino Client for MQTT Test Suite
|
|
||||||
|
|
||||||
This is a regression test suite for the `PubSubClient` library.
|
|
||||||
|
|
||||||
There are two parts:
|
|
||||||
|
|
||||||
- Tests that can be compiled and run on any machine
|
|
||||||
- Tests that build the example sketches using the Arduino IDE
|
|
||||||
|
|
||||||
|
|
||||||
It is a work-in-progress and is subject to complete refactoring as the whim takes
|
|
||||||
me.
|
|
||||||
|
|
||||||
|
|
||||||
## Local tests
|
|
||||||
|
|
||||||
These are a set of executables that can be run to test specific areas of functionality.
|
|
||||||
They do not require a real Arduino to be attached, nor the use of the Arduino IDE.
|
|
||||||
|
|
||||||
The tests include a set of mock files to stub out the parts of the Arduino environment the library
|
|
||||||
depends on.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- g++
|
|
||||||
|
|
||||||
### Running
|
|
||||||
|
|
||||||
Build the tests using the provided `Makefile`:
|
|
||||||
|
|
||||||
$ make
|
|
||||||
|
|
||||||
This will create a set of executables in `./bin/`. Run each of these executables to test the corresponding functionality.
|
|
||||||
|
|
||||||
*Note:* the `connect_spec` and `keepalive_spec` tests involve testing keepalive timers so naturally take a few minutes to run through.
|
|
||||||
|
|
||||||
## Arduino tests
|
|
||||||
|
|
||||||
*Note:* INO Tool doesn't currently play nicely with Arduino 1.5. This has broken this test suite.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,228 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_connect_fails_no_network() {
|
|
||||||
IT("fails to connect if underlying client doesn't connect");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(false);
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_fails_on_no_response() {
|
|
||||||
IT("fails to connect if no response received after 15 seconds");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_properly_formatted() {
|
|
||||||
IT("sends a properly formatted connect packet and succeeds");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte expectServer[] = { 172, 16, 0, 2 };
|
|
||||||
shimClient.expectConnect(expectServer,1883);
|
|
||||||
byte connect[] = {0x10,0x1a,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
|
|
||||||
shimClient.expect(connect,28);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_properly_formatted_hostname() {
|
|
||||||
IT("accepts a hostname");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
shimClient.expectConnect((char* const)"localhost",1883);
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client((char* const)"localhost", 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_connect_fails_on_bad_rc() {
|
|
||||||
IT("fails to connect if a bad return code is received");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x01 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_accepts_username_password() {
|
|
||||||
IT("accepts a username and password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = { 0x10,0x26,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x4,0x70,0x61,0x73,0x73};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x28);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_accepts_username_no_password() {
|
|
||||||
IT("accepts a username but no password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = { 0x10,0x20,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0x82,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x22);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",'\0');
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_ignores_password_no_username() {
|
|
||||||
IT("ignores a password but no username");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = {0x10,0x1a,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,28);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",'\0',(char*)"pass");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_with_will() {
|
|
||||||
IT("accepts a will");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = {0x10,0x32,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0xe,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x34);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"willTopic",1,0,(char*)"willMessage");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_with_will_username_password() {
|
|
||||||
IT("accepts a will, username and password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = {0x10,0x42,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0xce,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x8,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x44);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"password",(char*)"willTopic",1,0,(char*)"willMessage");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_disconnect_connect() {
|
|
||||||
IT("connects, disconnects and connects again");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte expectServer[] = { 172, 16, 0, 2 };
|
|
||||||
shimClient.expectConnect(expectServer,1883);
|
|
||||||
byte connect[] = {0x10,0x1a,0x0,0x6,0x4d,0x51,0x49,0x73,0x64,0x70,0x3,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
|
|
||||||
shimClient.expect(connect,28);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
byte disconnect[] = {0xE0,0x00};
|
|
||||||
shimClient.expect(disconnect,2);
|
|
||||||
|
|
||||||
client.disconnect();
|
|
||||||
|
|
||||||
IS_FALSE(client.connected());
|
|
||||||
IS_FALSE(shimClient.connected());
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
shimClient.expect(connect,28);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
test_connect_fails_no_network();
|
|
||||||
test_connect_fails_on_no_response();
|
|
||||||
test_connect_properly_formatted();
|
|
||||||
test_connect_fails_on_bad_rc();
|
|
||||||
test_connect_properly_formatted_hostname();
|
|
||||||
test_connect_accepts_username_password();
|
|
||||||
test_connect_accepts_username_no_password();
|
|
||||||
test_connect_ignores_password_no_username();
|
|
||||||
test_connect_with_will();
|
|
||||||
test_connect_with_will_username_password();
|
|
||||||
test_connect_disconnect_connect();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_keepalive_pings_idle() {
|
|
||||||
IT("keeps an idle connection alive");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
byte pingresp[] = { 0xD0,0x0 };
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
sleep(1);
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_pings_with_outbound_qos0() {
|
|
||||||
IT("keeps a connection alive that only sends qos0");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
TRACE(i<<":");
|
|
||||||
shimClient.expect(publish,16);
|
|
||||||
rc = client.publish((char*)"topic",(char*)"payload");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
sleep(1);
|
|
||||||
if ( i == 15 || i == 31 || i == 47) {
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
byte pingresp[] = { 0xD0,0x0 };
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
}
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_pings_with_inbound_qos0() {
|
|
||||||
IT("keeps a connection alive that only receives qos0");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
TRACE(i<<":");
|
|
||||||
sleep(1);
|
|
||||||
if ( i == 15 || i == 31 || i == 47) {
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
byte pingresp[] = { 0xD0,0x0 };
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
}
|
|
||||||
shimClient.respond(publish,16);
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_no_pings_inbound_qos1() {
|
|
||||||
IT("does not send pings for connections with inbound qos1");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
byte puback[] = {0x40,0x2,0x12,0x34};
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
shimClient.respond(publish,18);
|
|
||||||
shimClient.expect(puback,4);
|
|
||||||
sleep(1);
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_disconnects_hung() {
|
|
||||||
IT("disconnects a hung connection");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
sleep(1);
|
|
||||||
rc = client.loop();
|
|
||||||
}
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
test_keepalive_pings_idle();
|
|
||||||
test_keepalive_pings_with_outbound_qos0();
|
|
||||||
test_keepalive_pings_with_inbound_qos0();
|
|
||||||
test_keepalive_no_pings_inbound_qos1();
|
|
||||||
test_keepalive_disconnects_hung();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef Arduino_h
|
|
||||||
#define Arduino_h
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
|
|
||||||
extern "C"{
|
|
||||||
typedef uint8_t byte ;
|
|
||||||
typedef uint8_t boolean ;
|
|
||||||
|
|
||||||
/* sketch */
|
|
||||||
extern void setup( void ) ;
|
|
||||||
extern void loop( void ) ;
|
|
||||||
uint32_t millis( void );
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PROGMEM
|
|
||||||
#define pgm_read_byte_near(x) *(x)
|
|
||||||
|
|
||||||
#endif // Arduino_h
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
int testCount = 0;
|
|
||||||
int testPasses = 0;
|
|
||||||
const char* testDescription;
|
|
||||||
|
|
||||||
std::list<std::string> failureList;
|
|
||||||
|
|
||||||
int bddtest_test(const char* file, int line, const char* assertion, int result) {
|
|
||||||
if (!result) {
|
|
||||||
LOG("F");
|
|
||||||
std::ostringstream os;
|
|
||||||
os << " ! "<<testDescription<<"\n " <<file << ":" <<line<<" : "<<assertion<<" ["<<result<<"]";
|
|
||||||
failureList.push_back(os.str());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bddtest_start(const char* description) {
|
|
||||||
TRACE(" - "<<description << "\n");
|
|
||||||
testDescription = description;
|
|
||||||
testCount ++;
|
|
||||||
}
|
|
||||||
void bddtest_end() {
|
|
||||||
LOG(".");
|
|
||||||
testPasses ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bddtest_summary() {
|
|
||||||
for (std::list<std::string>::iterator it = failureList.begin(); it != failureList.end(); it++) {
|
|
||||||
LOG("\n");
|
|
||||||
LOG(*it);
|
|
||||||
}
|
|
||||||
LOG("\n" << std::dec << testPasses << "/" << testCount << " tests passed\n");
|
|
||||||
if (testPasses == testCount) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef bddtest_h
|
|
||||||
#define bddtest_h
|
|
||||||
|
|
||||||
int bddtest_test(const char*, int, const char*, int);
|
|
||||||
void bddtest_start(const char*);
|
|
||||||
void bddtest_end();
|
|
||||||
int bddtest_summary();
|
|
||||||
|
|
||||||
#define TEST(x) { if (!bddtest_test(__FILE__, __LINE__, #x, (x))) return false; }
|
|
||||||
|
|
||||||
#define IT(x) { bddtest_start(x); }
|
|
||||||
#define END_IT { bddtest_end();return true;}
|
|
||||||
|
|
||||||
#define FINISH { return bddtest_summary(); }
|
|
||||||
|
|
||||||
#define IS_TRUE(x) TEST(x)
|
|
||||||
#define IS_FALSE(x) TEST(!(x))
|
|
||||||
#define IS_EQUAL(x,y) TEST(x==y)
|
|
||||||
#define IS_NOT_EQUAL(x,y) TEST(x!=y)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#include "Buffer.h"
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
Buffer::Buffer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer::Buffer(uint8_t* buf, size_t size) {
|
|
||||||
this->add(buf,size);
|
|
||||||
}
|
|
||||||
bool Buffer::available() {
|
|
||||||
return this->pos < this->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Buffer::next() {
|
|
||||||
if (this->available()) {
|
|
||||||
return this->buffer[this->pos++];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer::reset() {
|
|
||||||
this->pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer::add(uint8_t* buf, size_t size) {
|
|
||||||
uint16_t i = 0;
|
|
||||||
for (;i<size;i++) {
|
|
||||||
this->buffer[this->length++] = buf[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef buffer_h
|
|
||||||
#define buffer_h
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
class Buffer {
|
|
||||||
private:
|
|
||||||
uint8_t buffer[1024];
|
|
||||||
uint16_t pos;
|
|
||||||
uint16_t length;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Buffer();
|
|
||||||
Buffer(uint8_t* buf, size_t size);
|
|
||||||
|
|
||||||
virtual bool available();
|
|
||||||
virtual uint8_t next();
|
|
||||||
virtual void reset();
|
|
||||||
|
|
||||||
virtual void add(uint8_t* buf, size_t size);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef client_h
|
|
||||||
#define client_h
|
|
||||||
#include "IPAddress.h"
|
|
||||||
|
|
||||||
class Client {
|
|
||||||
public:
|
|
||||||
virtual int connect(IPAddress ip, uint16_t port) =0;
|
|
||||||
virtual int connect(const char *host, uint16_t port) =0;
|
|
||||||
virtual size_t write(uint8_t) =0;
|
|
||||||
virtual size_t write(const uint8_t *buf, size_t size) =0;
|
|
||||||
virtual int available() = 0;
|
|
||||||
virtual int read() = 0;
|
|
||||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
|
||||||
virtual int peek() = 0;
|
|
||||||
virtual void flush() = 0;
|
|
||||||
virtual void stop() = 0;
|
|
||||||
virtual uint8_t connected() = 0;
|
|
||||||
virtual operator bool() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#ifndef IPAddress_h
|
|
||||||
#define IPAddress_h
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
#define IPAddress uint8_t*
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
uint32_t millis(void) {
|
|
||||||
return time(0)*1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShimClient::ShimClient() {
|
|
||||||
this->responseBuffer = new Buffer();
|
|
||||||
this->expectBuffer = new Buffer();
|
|
||||||
this->_allowConnect = true;
|
|
||||||
this->_connected = false;
|
|
||||||
this->_error = false;
|
|
||||||
this->expectAnything = true;
|
|
||||||
this->_received = 0;
|
|
||||||
this->_expectedPort = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ShimClient::connect(IPAddress ip, uint16_t port) {
|
|
||||||
if (this->_allowConnect) {
|
|
||||||
this->_connected = true;
|
|
||||||
}
|
|
||||||
if (this->_expectedPort !=0) {
|
|
||||||
if (memcmp(ip,this->_expectedIP,4) != 0) {
|
|
||||||
TRACE( "ip mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
if (port != this->_expectedPort) {
|
|
||||||
TRACE( "port mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this->_connected;
|
|
||||||
}
|
|
||||||
int ShimClient::connect(const char *host, uint16_t port) {
|
|
||||||
if (this->_allowConnect) {
|
|
||||||
this->_connected = true;
|
|
||||||
}
|
|
||||||
if (this->_expectedPort !=0) {
|
|
||||||
if (strcmp(host,this->_expectedHost) != 0) {
|
|
||||||
TRACE( "host mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
if (port != this->_expectedPort) {
|
|
||||||
TRACE( "port mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return this->_connected;
|
|
||||||
}
|
|
||||||
size_t ShimClient::write(uint8_t b) {
|
|
||||||
this->_received += 1;
|
|
||||||
TRACE(std::hex << (unsigned int)b);
|
|
||||||
if (!this->expectAnything) {
|
|
||||||
if (this->expectBuffer->available()) {
|
|
||||||
uint8_t expected = this->expectBuffer->next();
|
|
||||||
if (expected != b) {
|
|
||||||
this->_error = true;
|
|
||||||
TRACE("!=" << (unsigned int)expected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TRACE("\n"<< std::dec);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
size_t ShimClient::write(const uint8_t *buf, size_t size) {
|
|
||||||
this->_received += size;
|
|
||||||
TRACE( "[" << std::dec << (unsigned int)(size) << "] ");
|
|
||||||
uint16_t i=0;
|
|
||||||
for (;i<size;i++) {
|
|
||||||
if (i>0) {
|
|
||||||
TRACE(":");
|
|
||||||
}
|
|
||||||
TRACE(std::hex << (unsigned int)(buf[i]));
|
|
||||||
|
|
||||||
if (!this->expectAnything) {
|
|
||||||
if (this->expectBuffer->available()) {
|
|
||||||
uint8_t expected = this->expectBuffer->next();
|
|
||||||
if (expected != buf[i]) {
|
|
||||||
this->_error = true;
|
|
||||||
TRACE("!=" << (unsigned int)expected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TRACE("\n"<<std::dec);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
int ShimClient::available() {
|
|
||||||
return this->responseBuffer->available();
|
|
||||||
}
|
|
||||||
int ShimClient::read() { return this->responseBuffer->next(); }
|
|
||||||
int ShimClient::read(uint8_t *buf, size_t size) {
|
|
||||||
uint16_t i = 0;
|
|
||||||
for (;i<size;i++) {
|
|
||||||
buf[i] = this->read();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
int ShimClient::peek() { return 0; }
|
|
||||||
void ShimClient::flush() {}
|
|
||||||
void ShimClient::stop() {
|
|
||||||
this->setConnected(false);
|
|
||||||
}
|
|
||||||
uint8_t ShimClient::connected() { return this->_connected; }
|
|
||||||
ShimClient::operator bool() { return true; }
|
|
||||||
|
|
||||||
|
|
||||||
ShimClient* ShimClient::respond(uint8_t *buf, size_t size) {
|
|
||||||
this->responseBuffer->add(buf,size);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShimClient* ShimClient::expect(uint8_t *buf, size_t size) {
|
|
||||||
this->expectAnything = false;
|
|
||||||
this->expectBuffer->add(buf,size);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShimClient::setConnected(bool b) {
|
|
||||||
this->_connected = b;
|
|
||||||
}
|
|
||||||
void ShimClient::setAllowConnect(bool b) {
|
|
||||||
this->_allowConnect = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShimClient::error() {
|
|
||||||
return this->_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ShimClient::received() {
|
|
||||||
return this->_received;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShimClient::expectConnect(IPAddress ip, uint16_t port) {
|
|
||||||
this->_expectedIP = ip;
|
|
||||||
this->_expectedPort = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShimClient::expectConnect(const char *host, uint16_t port) {
|
|
||||||
this->_expectedHost = host;
|
|
||||||
this->_expectedPort = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
#ifndef shimclient_h
|
|
||||||
#define shimclient_h
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "Client.h"
|
|
||||||
#include "IPAddress.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
|
|
||||||
|
|
||||||
class ShimClient : public Client {
|
|
||||||
private:
|
|
||||||
Buffer* responseBuffer;
|
|
||||||
Buffer* expectBuffer;
|
|
||||||
bool _allowConnect;
|
|
||||||
bool _connected;
|
|
||||||
bool expectAnything;
|
|
||||||
bool _error;
|
|
||||||
uint16_t _received;
|
|
||||||
IPAddress _expectedIP;
|
|
||||||
uint16_t _expectedPort;
|
|
||||||
const char* _expectedHost;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ShimClient();
|
|
||||||
virtual int connect(IPAddress ip, uint16_t port);
|
|
||||||
virtual int connect(const char *host, uint16_t port);
|
|
||||||
virtual size_t write(uint8_t);
|
|
||||||
virtual size_t write(const uint8_t *buf, size_t size);
|
|
||||||
virtual int available();
|
|
||||||
virtual int read();
|
|
||||||
virtual int read(uint8_t *buf, size_t size);
|
|
||||||
virtual int peek();
|
|
||||||
virtual void flush();
|
|
||||||
virtual void stop();
|
|
||||||
virtual uint8_t connected();
|
|
||||||
virtual operator bool();
|
|
||||||
|
|
||||||
virtual ShimClient* respond(uint8_t *buf, size_t size);
|
|
||||||
virtual ShimClient* expect(uint8_t *buf, size_t size);
|
|
||||||
|
|
||||||
virtual void expectConnect(IPAddress ip, uint16_t port);
|
|
||||||
virtual void expectConnect(const char *host, uint16_t port);
|
|
||||||
|
|
||||||
virtual uint16_t received();
|
|
||||||
virtual bool error();
|
|
||||||
|
|
||||||
virtual void setAllowConnect(bool b);
|
|
||||||
virtual void setConnected(bool b);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#include "Stream.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
Stream::Stream() {
|
|
||||||
this->expectBuffer = new Buffer();
|
|
||||||
this->_error = false;
|
|
||||||
this->_written = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Stream::write(uint8_t b) {
|
|
||||||
this->_written++;
|
|
||||||
TRACE(std::hex << (unsigned int)b);
|
|
||||||
if (this->expectBuffer->available()) {
|
|
||||||
uint8_t expected = this->expectBuffer->next();
|
|
||||||
if (expected != b) {
|
|
||||||
this->_error = true;
|
|
||||||
TRACE("!=" << (unsigned int)expected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
TRACE("\n"<< std::dec);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Stream::error() {
|
|
||||||
return this->_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::expect(uint8_t *buf, size_t size) {
|
|
||||||
this->expectBuffer->add(buf,size);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t Stream::length() {
|
|
||||||
return this->_written;
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#ifndef Stream_h
|
|
||||||
#define Stream_h
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
|
|
||||||
class Stream {
|
|
||||||
private:
|
|
||||||
Buffer* expectBuffer;
|
|
||||||
bool _error;
|
|
||||||
uint16_t _written;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Stream();
|
|
||||||
virtual size_t write(uint8_t);
|
|
||||||
|
|
||||||
virtual bool error();
|
|
||||||
virtual void expect(uint8_t *buf, size_t size);
|
|
||||||
virtual uint16_t length();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#ifndef trace_h
|
|
||||||
#define trace_h
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define LOG(x) {std::cout << x << std::flush; }
|
|
||||||
#define TRACE(x) {if (getenv("TRACE")) { std::cout << x << std::flush; }}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,142 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish() {
|
|
||||||
IT("publishes a null-terminated string");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.expect(publish,16);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",(char*)"payload");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_publish_bytes() {
|
|
||||||
IT("publishes a byte array");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
|
||||||
int length = 5;
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",payload,length);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_publish_retained() {
|
|
||||||
IT("publishes retained");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
|
||||||
int length = 5;
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",payload,length,true);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish_not_connected() {
|
|
||||||
IT("publish fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int rc = client.publish((char*)"topic",(char*)"payload");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_publish_P() {
|
|
||||||
IT("publishes using PROGMEM");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
|
||||||
int length = 5;
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish_P((char*)"topic",payload,length,true);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
test_publish();
|
|
||||||
test_publish_bytes();
|
|
||||||
test_publish_retained();
|
|
||||||
test_publish_not_connected();
|
|
||||||
test_publish_P();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
||||||
|
|
@ -1,248 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
bool callback_called = false;
|
|
||||||
char lastTopic[1024];
|
|
||||||
char lastPayload[1024];
|
|
||||||
unsigned int lastLength;
|
|
||||||
|
|
||||||
void reset_callback() {
|
|
||||||
callback_called = false;
|
|
||||||
lastTopic[0] = '\0';
|
|
||||||
lastPayload[0] = '\0';
|
|
||||||
lastLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
callback_called = true;
|
|
||||||
strcpy(lastTopic,topic);
|
|
||||||
memcpy(lastPayload,payload,length);
|
|
||||||
lastLength = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_callback() {
|
|
||||||
IT("receives a callback message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,16);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(memcmp(lastPayload,"payload",7)==0);
|
|
||||||
IS_TRUE(lastLength == 7);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_stream() {
|
|
||||||
IT("receives a streamed callback message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
Stream stream;
|
|
||||||
stream.expect((uint8_t*)"payload",7);
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient, stream);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,16);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(lastLength == 7);
|
|
||||||
|
|
||||||
IS_FALSE(stream.error());
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_max_sized_message() {
|
|
||||||
IT("receives an max-sized message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte length = MQTT_MAX_PACKET_SIZE;
|
|
||||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
byte bigPublish[length];
|
|
||||||
memset(bigPublish,'A',length);
|
|
||||||
bigPublish[length] = 'B';
|
|
||||||
memcpy(bigPublish,publish,16);
|
|
||||||
shimClient.respond(bigPublish,length);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(lastLength == length-9);
|
|
||||||
IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_oversized_message() {
|
|
||||||
IT("drops an oversized message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte length = MQTT_MAX_PACKET_SIZE+1;
|
|
||||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
byte bigPublish[length];
|
|
||||||
memset(bigPublish,'A',length);
|
|
||||||
bigPublish[length] = 'B';
|
|
||||||
memcpy(bigPublish,publish,16);
|
|
||||||
shimClient.respond(bigPublish,length);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(callback_called);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_oversized_stream_message() {
|
|
||||||
IT("drops an oversized message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
Stream stream;
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient, stream);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte length = MQTT_MAX_PACKET_SIZE+1;
|
|
||||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
|
|
||||||
byte bigPublish[length];
|
|
||||||
memset(bigPublish,'A',length);
|
|
||||||
bigPublish[length] = 'B';
|
|
||||||
memcpy(bigPublish,publish,16);
|
|
||||||
|
|
||||||
shimClient.respond(bigPublish,length);
|
|
||||||
stream.expect(bigPublish+9,length-9);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(lastLength == length-9);
|
|
||||||
|
|
||||||
IS_FALSE(stream.error());
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_qos1() {
|
|
||||||
IT("receives a qos1 message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,18);
|
|
||||||
|
|
||||||
byte puback[] = {0x40,0x2,0x12,0x34};
|
|
||||||
shimClient.expect(puback,4);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(memcmp(lastPayload,"payload",7)==0);
|
|
||||||
IS_TRUE(lastLength == 7);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
test_receive_callback();
|
|
||||||
test_receive_stream();
|
|
||||||
test_receive_max_sized_message();
|
|
||||||
test_receive_oversized_message();
|
|
||||||
test_receive_oversized_stream_message();
|
|
||||||
test_receive_qos1();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int test_subscribe_no_qos() {
|
|
||||||
IT("subscribe without qos defaults to 0");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x0 };
|
|
||||||
shimClient.expect(subscribe,12);
|
|
||||||
byte suback[] = { 0x90,0x3,0x0,0x2,0x0 };
|
|
||||||
shimClient.respond(suback,5);
|
|
||||||
|
|
||||||
rc = client.subscribe((char*)"topic");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_qos_1() {
|
|
||||||
IT("subscribes qos 1");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1 };
|
|
||||||
shimClient.expect(subscribe,12);
|
|
||||||
byte suback[] = { 0x90,0x3,0x0,0x2,0x1 };
|
|
||||||
shimClient.respond(suback,5);
|
|
||||||
|
|
||||||
rc = client.subscribe((char*)"topic",1);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_not_connected() {
|
|
||||||
IT("subscribe fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int rc = client.subscribe((char*)"topic");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_invalid_qos() {
|
|
||||||
IT("subscribe fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
rc = client.subscribe((char*)"topic",2);
|
|
||||||
IS_FALSE(rc);
|
|
||||||
rc = client.subscribe((char*)"topic",254);
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_unsubscribe() {
|
|
||||||
IT("unsubscribes");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte unsubscribe[] = { 0xA2,0x9,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63 };
|
|
||||||
shimClient.expect(unsubscribe,12);
|
|
||||||
byte unsuback[] = { 0xB0,0x2,0x0,0x2 };
|
|
||||||
shimClient.respond(unsuback,4);
|
|
||||||
|
|
||||||
rc = client.unsubscribe((char*)"topic");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_unsubscribe_not_connected() {
|
|
||||||
IT("unsubscribe fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int rc = client.unsubscribe((char*)"topic");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
test_subscribe_no_qos();
|
|
||||||
test_subscribe_qos_1();
|
|
||||||
test_subscribe_not_connected();
|
|
||||||
test_subscribe_invalid_qos();
|
|
||||||
test_unsubscribe();
|
|
||||||
test_unsubscribe_not_connected();
|
|
||||||
FINISH
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
server_ip = "172.16.0.2"
|
|
||||||
arduino_ip = "172.16.0.100"
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
#!/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()
|
|
||||||
6
tutorial.html
Normal file
6
tutorial.html
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: Tutorial
|
||||||
|
---
|
||||||
|
<h1>The little book of MQTT on Arduino</h1>
|
||||||
|
<p>More to come...</p>
|
||||||
Loading…
Reference in a new issue