19 Commits
v1.6 ... v1.9

Author SHA1 Message Date
ecedcb804f Update CHANGES.txt for 1.9 2012-10-28 23:07:06 +00:00
569ada9937 Write each packet with a single call to _client->write() 2012-10-28 00:14:33 +01:00
97e9614780 Unable to set keep alive to over 32 seconds (signed int vs unsigned long) 2012-10-27 22:10:38 +01:00
6f23967ee1 License update 2012-09-13 22:38:08 +01:00
a971ed4a2c Allow any instance of Client to be passed in.
Added new example for publishing in callback.
2012-09-13 22:35:48 +01:00
2d43044338 Merge pull request #11 from jobytaffey/9607eefa0fbdda698efe538c5537340b58eb3812
Allow setting will, allow message to be stored in flash
2012-09-13 12:40:33 -07:00
9607eefa0f Added publish_P, for publishing a message from PROGMEM.
Message can be any size.
2012-07-13 16:41:31 +01:00
62693d406c Allow setting of a will without also setting a username and password. 2012-07-13 16:34:45 +01:00
7e53f612f2 Merge pull request #8 from WilHall/master
Implemented user/pass authentication
2012-04-04 13:52:49 -07:00
Wil
87fb3a9895 Implemented user/pass auth as per MQTT v3.1 specs 2012-04-04 03:44:45 -04:00
18fe49c070 Merge pull request #7 from dpslwk/master
Minor Update for example to 1.8 api
2012-03-13 12:24:50 -07:00
cc7e1c45c7 Update example to be 1.8 compatible 2012-03-13 18:29:43 +00:00
ee378802a9 Merge branch 'master' of github.com:knolleary/pubsubclient
Conflicts:
	PubSubClient/PubSubClient.cpp
	PubSubClient/PubSubClient.h
2012-03-08 22:11:28 +00:00
4d2e5994bc Various changes for v1.8 including larger message support and tidied up types. 2012-03-08 21:54:24 +00:00
6f0db7db0f Merge pull request #5 from mcollina/dns
Added DNS support.
2012-01-28 13:22:16 -08:00
f328d0849f Added DNS support. 2012-01-25 14:36:16 +01:00
e4fdabf4db Changes for beta4 - use EthernetClient not Client 2011-09-12 09:28:06 +01:00
4ad0d5f4ac Updated to the Arduino-1.0 API 2011-08-04 15:38:43 +01:00
fb5f8de2a4 Improved keepalive handling 2011-04-26 20:52:57 +01:00
7 changed files with 369 additions and 105 deletions

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

@ -1,3 +1,21 @@
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

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

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

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

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

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

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

View File

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

View File

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

View File

@ -0,0 +1,51 @@
/*
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) {
client.publish("outTopic", payload, length);
}
void setup()
{
Ethernet.begin(mac, ip);
if (client.connect("arduinoClient")) {
client.publish("outTopic","hello world");
client.subscribe("inTopic");
}
}
void loop()
{
client.loop();
}