Compare commits

...

100 Commits
2.4 ... master

Author SHA1 Message Date
8b3a05fec2
Arduino abstraction layer continued 2020-11-12 12:29:42 +01:00
673f2815d5
fixes 2020-11-11 20:03:44 +01:00
121cef72e6
start implementing Arduino abstraction layer and add Makefile 2020-11-11 19:52:21 +01:00
Nick O'Leary
2d228f2f86 Publish v2.8 2020-05-20 01:40:39 +01:00
Nick O'Leary
a69b18a4cb
Merge pull request #518 from ivankravets/patch-3
Declare support for ESP32
2020-05-20 01:37:47 +01:00
Nick O'Leary
24510271ff Add setSocketTimeout function 2020-05-20 01:33:31 +01:00
Nick O'Leary
77c4e445ea Add setKeepAlive function and tests 2020-05-20 01:18:02 +01:00
Nick O'Leary
64e981190b Merge branch 'master' into pr_282 2020-05-20 00:49:38 +01:00
Nick O'Leary
baad92dd0c Fix merge error with strnlen 2020-05-09 23:20:44 +01:00
Nick O'Leary
aca970d2d4
Merge pull request #655 from abderraouf-adjal/strnlen-patch-1
Using strnlen() instead of strlen()
2020-05-09 22:30:23 +01:00
Nick O'Leary
98098ede85
Merge branch 'master' into strnlen-patch-1 2020-05-09 22:29:59 +01:00
Nick O'Leary
289b64ce89
Merge pull request #693 from leojz/master
declare wifi mode prior to connect
2020-05-09 22:21:53 +01:00
Nick O'Leary
25977bef8a
Merge pull request #727 from sn0kerbuzz/fix-subscribe-empty-topic
Added check to prevent subscribe/unsubscribe to empty topics
2020-05-09 22:19:35 +01:00
Andrei Balasescu
6099ee028f Added check to prevent subscribe/unsubscribe to empty topics 2020-04-12 01:20:08 +03:00
Leo Zimmermann
d88909db3e declare wifi mode prior to connect
must have statement for current Arduino Framework, see also https://github.com/knolleary/pubsubclient/issues/138#issuecomment-326113915
2019-12-24 11:08:59 +01:00
Nick O'Leary
7dd2ff90a9
Merge pull request #279 from PhirePhly/esp8266_example_fixes
ESP8266 example fixes
2019-11-25 11:37:11 +00:00
Nick O'Leary
d64528b31b
Merge branch 'master' into esp8266_example_fixes 2019-11-25 11:36:58 +00:00
Nick O'Leary
cff1fc7bdd
Merge pull request #568 from turgu1/patch-1
Allow streaming of large messages
2019-11-25 11:34:25 +00:00
Nick O'Leary
8a5b51803d
Merge pull request #600 from sensidev/master
Fix publish_P return value
2019-11-25 11:32:18 +00:00
Nick O'Leary
1f4011142b
Merge pull request #528 from lexszero/patch-1
Fix publish() when payload == NULL
2019-11-25 11:29:38 +00:00
Nick O'Leary
7cad688df3
Merge pull request #363 from eykamp/documentation_update
Updated documentation
2019-11-25 11:28:19 +00:00
Nick O'Leary
b1cb57208c
Merge pull request #361 from eykamp/comments
Add some comments
2019-11-25 11:28:00 +00:00
Nick O'Leary
299c8293cb
Merge pull request #365 from czaraugust/master
Adding some speed improvments
2019-11-25 11:27:24 +00:00
Nick O'Leary
2b83aa0804
Merge pull request #571 from kevinresol/patch-1
Support pre-connected Client
2019-11-25 11:21:09 +00:00
Abderraouf Adjal
f13ad2af3b
PubSubClient.cpp: Using strnlen() 2019-08-22 17:19:38 +01:00
Abderraouf Adjal
719b90eafd
PubSubClient.h: Using strnlen() 2019-08-22 17:14:02 +01:00
Nick O'Leary
d2ff771f4a
Merge pull request #618 from Brandon2255p/patch-1
Removed an unused variable
2019-05-22 14:58:57 +01:00
Brandon Piner
bc65fe5244
Removed an unused variable 2019-05-09 22:10:56 +02:00
lucian
1b9f52b1fc Fix publish_P return value 2019-04-07 22:19:47 +03:00
Kevin Leung
a4f7821a70
Support pre-connected Client 2019-02-04 00:07:05 +08:00
Guy Turcotte
b25040a0d7
Update receive_spec.cpp 2019-01-29 09:05:26 -05:00
Guy Turcotte
7d6e409b59
Update receive_spec.cpp 2019-01-29 08:59:35 -05:00
Guy Turcotte
e7d9688ca5
Update receive_spec.cpp 2019-01-29 08:43:30 -05:00
Guy Turcotte
373c7d3569
Update PubSubClient.h 2019-01-28 18:47:18 -05:00
Guy Turcotte
b6239823fb
Allow streaming of large messages
These changes are required to allow for the transmission of large messages through a connected stream. The changes do not have an impact on the class interface and habitual behavior. In particular, it will enable the use of OTA through a stream hooked through the setStream() class method. I've designed such a stream to demonstrate the functionality: https://github.com/turgu1/mqtt_ota_example.git

Guy
2019-01-28 18:02:37 -05:00
Alex Ignatov
2dca84a776
Fix publish() when payload == NULL
Messages with empty payload are completely valid and are actually useful (e.g., to delete retained value).
This adds a check before calling `strlen()` to prevent crash when using shortcut methods taking `const char*`
2018-11-21 03:09:08 +02:00
Ivan Kravets
3f34227911
Declare support for ESP32 2018-11-06 13:34:26 +02:00
Nick O'Leary
26ce89fa47 Update for 2.7 2018-11-02 11:42:44 +00:00
Nick O'Leary
a29d0c3d72
Merge pull request #336 from jaecktec/master
Enable ESP32 to use PubSubClient (make MQTT_CALLBACK_SIGNATURE functional)
2018-11-02 01:07:40 +00:00
Nick O'Leary
fa3c4362ea
Merge branch 'master' into master 2018-11-02 01:07:33 +00:00
Nick O'Leary
b381728998 Add yield to mock test framework 2018-11-02 01:05:02 +00:00
Nick O'Leary
2bcd9b074a
Merge pull request #472 from apicquot/master
Add yield to resolve connection failures
2018-11-02 01:01:44 +00:00
Nick O'Leary
2ed03ad522
Merge pull request #362 from eykamp/new_sig
Create new signature to permit cleaner user code
2018-11-02 00:53:02 +00:00
Nick O'Leary
2d053d2df0 Add buffer overflow protection to connect
Closes #492
2018-11-02 00:50:52 +00:00
Nick O'Leary
ee30733e24 Fixup bad revert of 500 2018-11-02 00:12:44 +00:00
Nick O'Leary
4daba0ae5c Fix remaining length protection 2018-11-02 00:06:32 +00:00
Nick O'Leary
af860133e8 Merge branch 'pr_500' 2018-11-01 23:47:15 +00:00
Nick O'Leary
a0f09681f5 Add separate connect function for clean session + test 2018-11-01 23:46:09 +00:00
Nick O'Leary
05a601cc55
Merge pull request #512 from knolleary/revert-500-master
Revert "Added support of CleanSession flag during connect"
2018-11-01 23:35:56 +00:00
Nick O'Leary
3637cbec69
Revert "Added support of CleanSession flag during connect" 2018-11-01 23:35:31 +00:00
Nick O'Leary
b1bdbb7aaf
Merge pull request #500 from maxim-kukushkin/master
Added support of CleanSession flag during connect
2018-11-01 23:33:51 +00:00
Nick O'Leary
539838822c
Merge pull request #481 from bwibwi13/master
Correct maximum size of snprintf (use same size as the buffer ;)
2018-11-01 23:32:13 +00:00
Nick O'Leary
b5f90b679b
Merge pull request #495 from flok99/patch-1
Update PubSubClient.cpp
2018-11-01 23:28:22 +00:00
Nick O'Leary
4ecd32ec08 Fix up CI tests 2018-11-01 23:25:04 +00:00
Nick O'Leary
7517de7974
Merge pull request #494 from mcqn/master
Add large message API
2018-11-01 23:24:33 +00:00
Maxim Kukushkin
8154cbc09c Fixed an issue with clean session flag set when willTopic is provided 2018-10-22 23:35:47 +01:00
Maxim Kukushkin
0e8e3123cf Added support of CleanSession flag during connect 2018-10-02 00:02:31 +01:00
Nick O'Leary
9eff4b3308
Merge pull request #499 from nickdex/patch-1
Fix spelling in mqtt_esp8266 example
2018-09-24 10:02:00 +01:00
Nikhil Warke
a1cfd5af56
Fix spelling in mqtt_esp8266 example 2018-09-24 00:29:11 +05:30
Folkert van Heusden
0e2d6c322b
Update PubSubClient.cpp
Make sure all data is flushed to the other end when doing a disconnect(): that way we know for sure that it is there when we disconnect the wifi or maybe even reboot.
This change was made after I noticed that I did not get any mqtt messages. I verified that it indeed solves the problem. Example code on request.
2018-09-07 21:39:45 +02:00
Adrian McEwen
b2f3a6d2ec Add example code for using the large message API. 2018-09-04 13:36:45 +01:00
Adrian McEwen
0c2d12d8b0 Allow the large message publishing to use all the print methods. 2018-09-04 13:31:43 +01:00
bwibwi13
c87c9a47b3 Correct maximum size of snprintf (use same size as the buffer ;) 2018-08-19 08:39:39 +02:00
Adrian McEwen
3b3a8da8d2 Add large-payload API, make max header size a define, not magic number. 2018-08-15 17:15:04 +01:00
apicquot
9e1a6e6479
Update PubSubClient.cpp
added yield to resolve random connection failure
2018-08-07 17:29:29 -04:00
Nick O'Leary
54be6e87db Check remaining-length encoding is valid 2018-07-18 11:02:08 +01:00
Trygve Laugstøl
f029640ee6 Fixing compiler warnings exposed with -Wall and -Wextra: qos can't be less than zero. (#274) 2018-02-22 18:29:44 -08:00
Rotzbua
bb101c58e8 Update link http->https (#384) 2018-01-20 00:13:10 +00:00
César Augusto
3b0775a91d Excluding nada.txt which was empty and improving indentation 2017-12-07 15:31:24 -03:00
César Augusto
6bc3b76a9b Adding improvments 2017-12-07 12:20:23 -03:00
Unknown
1174d642ab Updated documentation. 2017-11-16 16:37:41 -08:00
Unknown
8795fdf0f5 Create new signature to permit cleaner user code 2017-11-16 12:49:01 -08:00
Unknown
4fa0226cce Add some comments 2017-11-16 11:45:33 -08:00
César Augusto
54043f5469 Start 2017-11-14 22:34:33 -03:00
constantin
8498284792 Add ESP32 to documentation 2017-09-19 18:10:09 +02:00
constantin
49f307506b Add ESP32 callback signature to be functional like ESP8266 2017-09-18 19:27:44 +02:00
Nick O'Leary
dddfffbe0c Initialise buffer variables in test framework 2017-06-07 21:31:48 +01:00
Nick O'Leary
bef5814858 Add test for blank (not-null) password 2017-06-07 20:37:45 +01:00
duncan law
5b23a97fb0 Update documentation for dynamic buffer resizing. 2017-04-30 10:43:18 +01:00
duncan law
e21ac7baae Add test for dynamic buffer resize. 2017-04-30 10:32:48 +01:00
duncan law
a257083a66 Dynamic buffer size. 2017-04-30 10:29:25 +01:00
Kenneth Finnegan
98ad16eff8 Correct buffer overflow and remove magic constants from esp8266 example
The msg[] array is defined as 50 bytes long, but then the snprintf is
protected at 75 bytes long, which is incorrectly 25 bytes longer than
the underlying array.

To correct this, define a new macro MSG_BUFFER_SIZE and use it in both
places:
 * defining msg[]
 * writing to msg[] with snprintf()
2017-04-25 20:43:54 -07:00
Kenneth Finnegan
b85f4dc35c Corrected time data types in esp8266 example
The millis() function returns unsigned longs, not longs.
2017-04-25 20:39:09 -07:00
Nick O'Leary
f46d0011ee Merge pull request #270 from pacm93/master
Make Python files compliant with PEP8, except for E501
2017-04-12 08:58:43 +01:00
Paulo Martinez
10925659ef Made Python files compliant with PEP8, except for E501 2017-04-12 09:39:36 +02:00
Nick O'Leary
4c8ce14dad Merge pull request #169 from igrr/master
Use random MQTT client ID in example sketch
2016-08-21 23:22:25 +01:00
Nick O'Leary
df4122466c Merge pull request #173 from edwin-oetelaar/master
Reduce stack usage in loop(). no data duplication on stack
2016-08-21 23:21:03 +01:00
Edwin vd Oetelaar PA2LVD
d724864095 Reduce stack usage in loop(). No need to duplicate
topic string onto stack before giving it to callback()
Just move it one byte in buffer to add space for 'C' string end \0x00
2016-06-26 20:53:07 +02:00
Ivan Grokhotkov
33170273a9 Use random MQTT client ID in example sketch
Many users load the sketch having only changed WiFi SSID and password. When multiple users attempt to connect to the same broker using same client ID, they get rejected. Currently the chances of connecting to broker.mqtt-dashboard.com using "ESP8266Client" ID are fairly slim. This change adds a random number to the client ID, increasing chances of connection for new users of this library.
2016-06-14 19:10:15 +08:00
Nick O'Leary
35ead348e3 Fix esp8266 example 2016-02-13 21:23:48 +00:00
Nick O'Leary
341661671b Revert breaking change to callback signature 2016-02-02 07:40:48 +00:00
Nick O'Leary
4739ca0802 Update library files
closes #44
2016-01-31 20:53:44 +00:00
Nick O'Leary
36bb1ffa6a Merge pull request #96 from ElvisTheKing/patch-1
correct handling of dns fauilure
2016-01-31 20:48:43 +00:00
Nick O'Leary
83b69a766e Merge pull request #120 from tomkcook/master
Use std::function on ESP8266 platform.
2016-01-31 20:47:32 +00:00
Nick O'Leary
baeb59e263 Merge pull request #119 from skorokithakis/master
Add definable parameters.
2016-01-31 20:41:19 +00:00
Tom Cook
67eba6dad4 Moved #include of <functional> 2016-01-27 12:41:50 +00:00
Tom Cook
98a9c296f6 Made use of std::function ESP8266-specific. 2016-01-27 12:39:43 +00:00
Stavros Korokithakis
68400b7b6c Add definable parameters. 2016-01-26 12:52:38 +02:00
Tom Cook
21b75a2c4a Changed callback type. 2016-01-26 09:50:27 +00:00
Sergey Konyukhovskiy
830f34c7d0 correct handling of dns fauilure
In case of domain name resolution error result can be negative (see Dns.cpp:46)
2015-12-04 04:10:18 +03:00
35 changed files with 1365 additions and 392 deletions

6
.gitignore vendored
View File

@ -1 +1,7 @@
build
pubsub.a
tests/bin tests/bin
.pioenvs
.piolibdeps
.clang_complete
.gcc-flags.json

12
AAL/Arduino.cpp Normal file
View File

@ -0,0 +1,12 @@
#include <Arduino.h>
#include <stdint.h>
millis_t millis() {
return HAL_GetTick();
}
void yield() {
// does nothing
}

25
AAL/Arduino.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _ARDUINO_H_
#define _ARDUINO_H_
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <Print.h>
#include <IPAddress.h>
#include <Stream.h>
#include <Client.h>
#include <stubs.h>
typedef uint32_t millis_t;
typedef bool boolean;
millis_t millis();
void yield();
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define pgm_read_byte_near(addr) pgm_read_byte(addr)
#endif // _ARDUINO_H_

122
AAL/Client.cpp Normal file
View File

@ -0,0 +1,122 @@
#include <socket.h>
#include <Arduino.h>
#include <Client.h>
#include <stdlib.h>
#include <stdint.h>
/*
class Client {
public:
Client();
int connect(const char *host, uint16_t port);
int connect(IPAddress ip, uint16_t port);
int available();
void stop();
int read();
size_t write(const uint8_t *buf, size_t size);
size_t write(uint8_t b);
void flush();
uint8_t connected();
};
*/
Client::Client(uint8_t sockNum) : sockNum(sockNum) {
}
int Client::connect(const char *host, uint16_t) {
// DNS request required
return 0;
}
int Client::connect(IPAddress ip, uint16_t port) {
int8_t res = socket(this->sockNum, Sn_MR_TCP, port, SF_IO_NONBLOCK);
if (res != this->sockNum) {
close(this->sockNum);
return INVALID_RESPONSE;
}
logMsg("Client::connect: socket initialized");
res = ::connect(this->sockNum, ip.raw_address(), port);
if (res != SOCK_BUSY) {
close(this->sockNum);
return INVALID_RESPONSE;
}
uint32_t startTime = HAL_GetTick();
while (startTime + TIMEOUT_MS > HAL_GetTick()) {
uint8_t sockState = getSn_SR(this->sockNum);
if (sockState == SOCK_ESTABLISHED) {
logMsg("Client::connect: connection established");
return SUCCESS;
}
}
return TIMED_OUT;
}
int Client::available() {
return getSn_RX_RSR(this->sockNum);
}
void Client::stop() {
int8_t res = disconnect(this->sockNum);
if (res != SOCK_BUSY) {
close(this->sockNum);
logMsg("Client::stop: disconnect returns 0x%02x, invalid response, ignore it", res);
} else {
bool successfullyClosed = false;
uint32_t startTime = HAL_GetTick();
while (startTime + TIMEOUT_MS > HAL_GetTick()) {
uint8_t sockState = getSn_SR(this->sockNum);
if (sockState == SOCK_CLOSED) {
logMsg("Client::stop: connection closed");
successfullyClosed = true;
break;
}
}
if (successfullyClosed) {
logMsg("Client::stop: done");
close(this->sockNum);
} else {
logMsg("Client::stop: timeout when closing, ignore");
close(this->sockNum);
}
}
}
int Client::read() {
int res = -1;
if (this->available() >= 1) {
uint8_t buf;
int32_t res = recv(this->sockNum, &buf, 1);
if (res == 1) {
res = (int) buf;
}
}
return res;
}
size_t Client::write(const uint8_t *buf, size_t size) {
int32_t res = send(this->sockNum, (uint8_t*) buf, size);
return (res == size) ? size : 0;
}
size_t Client::write(uint8_t b) {
return this->write(&b, 1);
}
void Client::flush() {
// does nothing
}
uint8_t Client::connected() {
return (getSn_SR(this->sockNum) == SOCK_ESTABLISHED) ? 1 : 0;
}

35
AAL/Client.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include <stdlib.h>
#include <stdint.h>
#include <Arduino.h>
#include <IPAddress.h>
const int SUCCESS = 1;
const int TIMED_OUT = -1;
const int INVALID_SERVER = -2;
const int TRUNCATED = -3;
const int INVALID_RESPONSE = -4;
const uint32_t TIMEOUT_MS = 1000;
class Client {
private:
const uint8_t sockNum;
public:
Client(const uint8_t sockNum);
int connect(const char *host, uint16_t port);
int connect(IPAddress ip, uint16_t port);
int available();
void stop();
int read();
size_t write(const uint8_t *buf, size_t size);
size_t write(uint8_t b);
void flush();
uint8_t connected();
};
#endif // _CLIENT_H_

16
AAL/IPAddress.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <IPAddress.h>
#include <stdint.h>
#include <string.h>
IPAddress::IPAddress() {
memset(_address, 0, sizeof(_address));
}
IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4) {
_address[0] = o1;
_address[1] = o2;
_address[2] = o3;
_address[3] = o4;
}

18
AAL/IPAddress.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _IPADDRESS_H_
#define _IPADDRESS_H_
#include <stdint.h>
class IPAddress {
private:
uint8_t _address[4];
uint8_t *raw_address() { return _address; };
public:
IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4);
IPAddress();
friend class Client;
};
#endif // _IPADDRESS_H_

7
AAL/Print.cpp Normal file
View File

@ -0,0 +1,7 @@
#include <Arduino.h>
#include <Print.h>
Print::Print() {
}

9
AAL/Print.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _PRINT_H_
#define _PRINT_H_
class Print {
public:
Print();
};
#endif // _PRINT_H_

12
AAL/Stream.cpp Normal file
View File

@ -0,0 +1,12 @@
#include <Stream.h>
#include <stdint.h>
#include <stdlib.h>
Stream::Stream() {
}
size_t Stream::write(uint8_t c) {
return 0;
}

13
AAL/Stream.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef _STREAM_H_
#define _STREAM_H_
#include <stdint.h>
#include <stdlib.h>
class Stream {
public:
Stream();
size_t write(uint8_t c);
};
#endif // _STREAM_H_

9
AAL/stubs.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _STUBS_H_
#define _STUBS_H_
uint32_t HAL_GetTick(void);
int logMsg(const char *format, ...);
#endif // _STUBS_H_

View File

@ -1,8 +1,25 @@
2.8
* Add setBufferSize() to override MQTT_MAX_PACKET_SIZE
* Add setKeepAlive() to override MQTT_KEEPALIVE
* Add setSocketTimeout() to overide MQTT_SOCKET_TIMEOUT
* Added check to prevent subscribe/unsubscribe to empty topics
* Declare wifi mode prior to connect in ESP example
* Use `strnlen` to avoid overruns
* Support pre-connected Client objects
2.7
* Fix remaining-length handling to prevent buffer overrun
* Add large-payload API - beginPublish/write/publish/endPublish
* Add yield call to improve reliability on ESP
* Add Clean Session flag to connect options
* Add ESP32 support for functional callback signature
* Various other fixes
2.4 2.4
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely * Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
whilst waiting for inbound data whilst waiting for inbound data
* Fixed return code when publishing >256 bytes * Fixed return code when publishing >256 bytes
2.3 2.3
* Add publish(topic,payload,retained) function * Add publish(topic,payload,retained) function

View File

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

30
Makefile Normal file
View File

@ -0,0 +1,30 @@
CFLAGS?=-mcpu=cortex-m3 -mthumb -Og -fdata-sections -ffunction-sections -g -gdwarf-2
CC=arm-none-eabi-gcc
CXX=arm-none-eabi-g++
AR=arm-none-eabi-ar
CFLAGS+=-I../ioLibrary_Driver/Ethernet -Isrc -IAAL
OBJDIR=build
VPATH=src AAL
OBJS=$(addprefix $(OBJDIR)/,PubSubClient.o IPAddress.o Stream.o Arduino.o Print.o Client.o)
all: $(OBJS)
$(AR) rcs pubsub.a $^
$(OBJDIR)/%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(OBJDIR)/%.o: %.cpp
$(CXX) $(CFLAGS) -c $< -o $@
$(OBJS): | $(OBJDIR)
$(OBJDIR):
mkdir $(OBJDIR)
.PHONY: clean
clean:
-rm -rf $(OBJDIR)

View File

@ -8,15 +8,17 @@ a server that supports MQTT.
The library comes with a number of example sketches. See File > Examples > PubSubClient The library comes with a number of example sketches. See File > Examples > PubSubClient
within the Arduino application. within the Arduino application.
Full API documentation is available here: http://pubsubclient.knolleary.net Full API documentation is available here: https://pubsubclient.knolleary.net
## Limitations ## Limitations
- It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1. - It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
- The maximum message size, including header, is **128 bytes** by default. This - The maximum message size, including header, is **256 bytes** by default. This
is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`. is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h` or can be changed
by calling `PubSubClient::setBufferSize(size)`.
- The keepalive interval is set to 15 seconds by default. This is configurable - The keepalive interval is set to 15 seconds by default. This is configurable
via `MQTT_KEEPALIVE` in `PubSubClient.h`. via `MQTT_KEEPALIVE` in `PubSubClient.h` or can be changed by calling
`PubSubClient::setKeepAlive(keepAlive)`.
- The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by - The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
changing value of `MQTT_VERSION` in `PubSubClient.h`. changing value of `MQTT_VERSION` in `PubSubClient.h`.
@ -37,6 +39,7 @@ boards and shields, including:
- TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library) - TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
- Intel Galileo/Edison - Intel Galileo/Edison
- ESP8266 - ESP8266
- ESP32
The library cannot currently be used with hardware based on the ENC28J60 chip The library cannot currently be used with hardware based on the ENC28J60 chip
such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an

View File

@ -27,9 +27,9 @@ void setup()
{ {
Ethernet.begin(mac, ip); Ethernet.begin(mac, ip);
// Note - the default maximum packet size is 128 bytes. If the // Note - the default maximum packet size is 128 bytes. If the
// combined length of clientId, username and password exceed this, // combined length of clientId, username and password exceed this use the
// you will need to increase the value of MQTT_MAX_PACKET_SIZE in // following to increase the buffer size:
// PubSubClient.h // client.setBufferSize(255);
if (client.connect("arduinoClient", "testuser", "testpass")) { if (client.connect("arduinoClient", "testuser", "testpass")) {
client.publish("outTopic","hello world"); client.publish("outTopic","hello world");

View File

@ -1,26 +1,21 @@
/* /*
Basic ESP8266 MQTT example Basic ESP8266 MQTT example
This sketch demonstrates the capabilities of the pubsub library in combination This sketch demonstrates the capabilities of the pubsub library in combination
with the ESP8266 board/library. with the ESP8266 board/library.
It connects to an MQTT server then: It connects to an MQTT server then:
- publishes "hello world" to the topic "outTopic" every two seconds - publishes "hello world" to the topic "outTopic" every two seconds
- subscribes to the topic "inTopic", printing out any messages - subscribes to the topic "inTopic", printing out any messages
it receives. NB - it assumes the received payloads are strings not binary it receives. NB - it assumes the received payloads are strings not binary
- If the first character of the topic "inTopic" is an 1, switch ON the ESP Led, - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
else switch it off else switch it off
It will reconnect to the server if the connection is lost using a blocking It will reconnect to the server if the connection is lost using a blocking
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
achieve the same result without blocking the main loop. achieve the same result without blocking the main loop.
To install the ESP8266 board, (using Arduino 1.6.4+): To install the ESP8266 board, (using Arduino 1.6.4+):
- Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs": - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
http://arduino.esp8266.com/stable/package_esp8266com_index.json http://arduino.esp8266.com/stable/package_esp8266com_index.json
- Open the "Tools -> Board -> Board Manager" and click install for the ESP8266" - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
- Select your ESP8266 in "Tools -> Board" - Select your ESP8266 in "Tools -> Board"
*/ */
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
@ -34,18 +29,11 @@ const char* mqtt_server = "broker.mqtt-dashboard.com";
WiFiClient espClient; WiFiClient espClient;
PubSubClient client(espClient); PubSubClient client(espClient);
long lastMsg = 0; unsigned long lastMsg = 0;
char msg[50]; #define MSG_BUFFER_SIZE (50)
char msg[MSG_BUFFER_SIZE];
int value = 0; int value = 0;
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void setup_wifi() { void setup_wifi() {
delay(10); delay(10);
@ -54,6 +42,7 @@ void setup_wifi() {
Serial.print("Connecting to "); Serial.print("Connecting to ");
Serial.println(ssid); Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password); WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
@ -61,6 +50,8 @@ void setup_wifi() {
Serial.print("."); Serial.print(".");
} }
randomSeed(micros());
Serial.println(""); Serial.println("");
Serial.println("WiFi connected"); Serial.println("WiFi connected");
Serial.println("IP address: "); Serial.println("IP address: ");
@ -80,7 +71,7 @@ void callback(char* topic, byte* payload, unsigned int length) {
if ((char)payload[0] == '1') { if ((char)payload[0] == '1') {
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
// but actually the LED is on; this is because // but actually the LED is on; this is because
// it is acive low on the ESP-01) // it is active low on the ESP-01)
} else { } else {
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
} }
@ -91,8 +82,11 @@ void reconnect() {
// Loop until we're reconnected // Loop until we're reconnected
while (!client.connected()) { while (!client.connected()) {
Serial.print("Attempting MQTT connection..."); Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect // Attempt to connect
if (client.connect("ESP8266Client")) { if (client.connect(clientId.c_str())) {
Serial.println("connected"); Serial.println("connected");
// Once connected, publish an announcement... // Once connected, publish an announcement...
client.publish("outTopic", "hello world"); client.publish("outTopic", "hello world");
@ -107,6 +101,15 @@ void reconnect() {
} }
} }
} }
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() { void loop() {
if (!client.connected()) { if (!client.connected()) {
@ -114,11 +117,11 @@ void loop() {
} }
client.loop(); client.loop();
long now = millis(); unsigned long now = millis();
if (now - lastMsg > 2000) { if (now - lastMsg > 2000) {
lastMsg = now; lastMsg = now;
++value; ++value;
snprintf (msg, 75, "hello world #%ld", value); snprintf (msg, MSG_BUFFER_SIZE, "hello world #%ld", value);
Serial.print("Publish message: "); Serial.print("Publish message: ");
Serial.println(msg); Serial.println(msg);
client.publish("outTopic", msg); client.publish("outTopic", msg);

View File

@ -0,0 +1,179 @@
/*
Long message ESP8266 MQTT example
This sketch demonstrates sending arbitrarily large messages in combination
with the ESP8266 board/library.
It connects to an MQTT server then:
- publishes "hello world" to the topic "outTopic"
- subscribes to the topic "greenBottles/#", printing out any messages
it receives. NB - it assumes the received payloads are strings not binary
- If the sub-topic is a number, it publishes a "greenBottles/lyrics" message
with a payload consisting of the lyrics to "10 green bottles", replacing
10 with the number given in the sub-topic.
It will reconnect to the server if the connection is lost using a blocking
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
achieve the same result without blocking the main loop.
To install the ESP8266 board, (using Arduino 1.6.4+):
- Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
http://arduino.esp8266.com/stable/package_esp8266com_index.json
- Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
- Select your ESP8266 in "Tools -> Board"
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// Update these with values suitable for your network.
const char* ssid = "........";
const char* password = "........";
const char* mqtt_server = "broker.mqtt-dashboard.com";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
}
Serial.println();
// Find out how many bottles we should generate lyrics for
String topicStr(topic);
int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic
if (topicStr.indexOf('/') >= 0) {
// The topic includes a '/', we'll try to read the number of bottles from just after that
topicStr.remove(0, topicStr.indexOf('/')+1);
// Now see if there's a number of bottles after the '/'
bottleCount = topicStr.toInt();
}
if (bottleCount > 0) {
// Work out how big our resulting message will be
int msgLen = 0;
for (int i = bottleCount; i > 0; i--) {
String numBottles(i);
msgLen += 2*numBottles.length();
if (i == 1) {
msgLen += 2*String(" green bottle, standing on the wall\n").length();
} else {
msgLen += 2*String(" green bottles, standing on the wall\n").length();
}
msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length();
switch (i) {
case 1:
msgLen += String("no green bottles, standing on the wall\n\n").length();
break;
case 2:
msgLen += String("1 green bottle, standing on the wall\n\n").length();
break;
default:
numBottles = i-1;
msgLen += numBottles.length();
msgLen += String(" green bottles, standing on the wall\n\n").length();
break;
};
}
// Now we can start to publish the message
client.beginPublish("greenBottles/lyrics", msgLen, false);
for (int i = bottleCount; i > 0; i--) {
for (int j = 0; j < 2; j++) {
client.print(i);
if (i == 1) {
client.print(" green bottle, standing on the wall\n");
} else {
client.print(" green bottles, standing on the wall\n");
}
}
client.print("And if one green bottle should accidentally fall\nThere'll be ");
switch (i) {
case 1:
client.print("no green bottles, standing on the wall\n\n");
break;
case 2:
client.print("1 green bottle, standing on the wall\n\n");
break;
default:
client.print(i-1);
client.print(" green bottles, standing on the wall\n\n");
break;
};
}
// Now we're done!
client.endPublish();
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("connected");
// Once connected, publish an announcement...
client.publish("outTopic", "hello world");
// ... and resubscribe
client.subscribe("greenBottles/#");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
void setup() {
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
}

View File

@ -16,6 +16,9 @@ connect KEYWORD2
disconnect KEYWORD2 disconnect KEYWORD2
publish KEYWORD2 publish KEYWORD2
publish_P KEYWORD2 publish_P KEYWORD2
beginPublish KEYWORD2
endPublish KEYWORD2
write KEYWORD2
subscribe KEYWORD2 subscribe KEYWORD2
unsubscribe KEYWORD2 unsubscribe KEYWORD2
loop KEYWORD2 loop KEYWORD2
@ -24,6 +27,9 @@ setServer KEYWORD2
setCallback KEYWORD2 setCallback KEYWORD2
setClient KEYWORD2 setClient KEYWORD2
setStream KEYWORD2 setStream KEYWORD2
setKeepAlive KEYWORD2
setBufferSize KEYWORD2
setSocketTimeout KEYWORD2
####################################### #######################################
# Constants (LITERAL1) # Constants (LITERAL1)

18
library.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "PubSubClient",
"keywords": "ethernet, mqtt, m2m, iot",
"description": "A client library for MQTT messaging. MQTT is a lightweight messaging protocol ideal for small devices. This library allows you to send and receive MQTT messages. It supports the latest MQTT 3.1.1 protocol and can be configured to use the older MQTT 3.1 if needed. It supports all Arduino Ethernet Client compatible hardware, including the Intel Galileo/Edison, ESP8266 and TI CC3000.",
"repository": {
"type": "git",
"url": "https://github.com/knolleary/pubsubclient.git"
},
"version": "2.8",
"exclude": "tests",
"examples": "examples/*/*.ino",
"frameworks": "arduino",
"platforms": [
"atmelavr",
"espressif8266",
"espressif32"
]
}

View File

@ -1,5 +1,5 @@
name=PubSubClient name=PubSubClient
version=2.4 version=2.8
author=Nick O'Leary <nick.oleary@gmail.com> author=Nick O'Leary <nick.oleary@gmail.com>
maintainer=Nick O'Leary <nick.oleary@gmail.com> maintainer=Nick O'Leary <nick.oleary@gmail.com>
sentence=A client library for MQTT messaging. sentence=A client library for MQTT messaging.

View File

@ -1,4 +1,5 @@
/* /*
PubSubClient.cpp - A simple client for MQTT. PubSubClient.cpp - A simple client for MQTT.
Nick O'Leary Nick O'Leary
http://knolleary.net http://knolleary.net
@ -12,12 +13,20 @@ PubSubClient::PubSubClient() {
this->_client = NULL; this->_client = NULL;
this->stream = NULL; this->stream = NULL;
setCallback(NULL); setCallback(NULL);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(Client& client) { PubSubClient::PubSubClient(Client& client) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) { PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
@ -25,12 +34,20 @@ PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
setServer(addr, port); setServer(addr, port);
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) { PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
setServer(addr,port); setServer(addr,port);
setClient(client); setClient(client);
setStream(stream); setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
@ -38,6 +55,10 @@ PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATUR
setCallback(callback); setCallback(callback);
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
@ -45,6 +66,10 @@ PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATUR
setCallback(callback); setCallback(callback);
setClient(client); setClient(client);
setStream(stream); setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) { PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
@ -52,12 +77,20 @@ PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
setServer(ip, port); setServer(ip, port);
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) { PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
setServer(ip,port); setServer(ip,port);
setClient(client); setClient(client);
setStream(stream); setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
@ -65,6 +98,10 @@ PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE,
setCallback(callback); setCallback(callback);
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
@ -72,6 +109,10 @@ PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE,
setCallback(callback); setCallback(callback);
setClient(client); setClient(client);
setStream(stream); setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) { PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
@ -79,12 +120,20 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
setServer(domain,port); setServer(domain,port);
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) { PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
setServer(domain,port); setServer(domain,port);
setClient(client); setClient(client);
setStream(stream); setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) { PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
@ -92,6 +141,10 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN
setCallback(callback); setCallback(callback);
setClient(client); setClient(client);
this->stream = NULL; this->stream = NULL;
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
} }
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) { PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
this->_state = MQTT_DISCONNECTED; this->_state = MQTT_DISCONNECTED;
@ -99,33 +152,51 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN
setCallback(callback); setCallback(callback);
setClient(client); setClient(client);
setStream(stream); setStream(stream);
this->bufferSize = 0;
setBufferSize(MQTT_MAX_PACKET_SIZE);
setKeepAlive(MQTT_KEEPALIVE);
setSocketTimeout(MQTT_SOCKET_TIMEOUT);
}
PubSubClient::~PubSubClient() {
free(this->buffer);
} }
boolean PubSubClient::connect(const char *id) { boolean PubSubClient::connect(const char *id) {
return connect(id,NULL,NULL,0,0,0,0); return connect(id,NULL,NULL,0,0,0,0,1);
} }
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) { boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
return connect(id,user,pass,0,0,0,0); return connect(id,user,pass,0,0,0,0,1);
} }
boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage); return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
} }
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) { boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
}
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
if (!connected()) { if (!connected()) {
int result = 0; int result = 0;
if (domain != NULL) {
result = _client->connect(this->domain, this->port); if(_client->connected()) {
result = 1;
} else { } else {
result = _client->connect(this->ip, this->port); if (domain != NULL) {
result = _client->connect(this->domain, this->port);
} else {
result = _client->connect(this->ip, this->port);
}
} }
if (result) {
if (result == 1) {
nextMsgId = 1; nextMsgId = 1;
// Leave room in the buffer for header and variable length field // Leave room in the buffer for header and variable length field
uint16_t length = 5; uint16_t length = MQTT_MAX_HEADER_SIZE;
unsigned int j; unsigned int j;
#if MQTT_VERSION == MQTT_VERSION_3_1 #if MQTT_VERSION == MQTT_VERSION_3_1
@ -136,14 +207,17 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
#define MQTT_HEADER_VERSION_LENGTH 7 #define MQTT_HEADER_VERSION_LENGTH 7
#endif #endif
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) { for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
buffer[length++] = d[j]; this->buffer[length++] = d[j];
} }
uint8_t v; uint8_t v;
if (willTopic) { if (willTopic) {
v = 0x06|(willQos<<3)|(willRetain<<5); v = 0x04|(willQos<<3)|(willRetain<<5);
} else { } else {
v = 0x02; v = 0x00;
}
if (cleanSession) {
v = v|0x02;
} }
if(user != NULL) { if(user != NULL) {
@ -153,38 +227,43 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
v = v|(0x80>>1); v = v|(0x80>>1);
} }
} }
this->buffer[length++] = v;
buffer[length++] = v; this->buffer[length++] = ((this->keepAlive) >> 8);
this->buffer[length++] = ((this->keepAlive) & 0xFF);
buffer[length++] = ((MQTT_KEEPALIVE) >> 8); CHECK_STRING_LENGTH(length,id)
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF); length = writeString(id,this->buffer,length);
length = writeString(id,buffer,length);
if (willTopic) { if (willTopic) {
length = writeString(willTopic,buffer,length); CHECK_STRING_LENGTH(length,willTopic)
length = writeString(willMessage,buffer,length); length = writeString(willTopic,this->buffer,length);
CHECK_STRING_LENGTH(length,willMessage)
length = writeString(willMessage,this->buffer,length);
} }
if(user != NULL) { if(user != NULL) {
length = writeString(user,buffer,length); CHECK_STRING_LENGTH(length,user)
length = writeString(user,this->buffer,length);
if(pass != NULL) { if(pass != NULL) {
length = writeString(pass,buffer,length); CHECK_STRING_LENGTH(length,pass)
length = writeString(pass,this->buffer,length);
} }
} }
write(MQTTCONNECT,buffer,length-5); write(MQTTCONNECT,this->buffer,length-MQTT_MAX_HEADER_SIZE);
lastInActivity = lastOutActivity = millis(); lastInActivity = lastOutActivity = millis();
while (!_client->available()) { while (!_client->available()) {
unsigned long t = millis(); unsigned long t = millis();
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) { if (t-lastInActivity >= ((int32_t) this->socketTimeout*1000UL)) {
_state = MQTT_CONNECTION_TIMEOUT; _state = MQTT_CONNECTION_TIMEOUT;
_client->stop(); _client->stop();
return false; return false;
} }
} }
uint8_t llen; uint8_t llen;
uint16_t len = readPacket(&llen); uint32_t len = readPacket(&llen);
if (len == 4) { if (len == 4) {
if (buffer[3] == 0) { if (buffer[3] == 0) {
@ -209,8 +288,9 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
boolean PubSubClient::readByte(uint8_t * result) { boolean PubSubClient::readByte(uint8_t * result) {
uint32_t previousMillis = millis(); uint32_t previousMillis = millis();
while(!_client->available()) { while(!_client->available()) {
yield();
uint32_t currentMillis = millis(); uint32_t currentMillis = millis();
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){ if(currentMillis - previousMillis >= ((int32_t) this->socketTimeout * 1000)){
return false; return false;
} }
} }
@ -229,68 +309,76 @@ boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
return false; return false;
} }
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) { uint32_t PubSubClient::readPacket(uint8_t* lengthLength) {
uint16_t len = 0; uint16_t len = 0;
if(!readByte(buffer, &len)) return 0; if(!readByte(this->buffer, &len)) return 0;
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH; bool isPublish = (this->buffer[0]&0xF0) == MQTTPUBLISH;
uint32_t multiplier = 1; uint32_t multiplier = 1;
uint16_t length = 0; uint32_t length = 0;
uint8_t digit = 0; uint8_t digit = 0;
uint16_t skip = 0; uint16_t skip = 0;
uint8_t start = 0; uint32_t start = 0;
do { do {
if (len == 5) {
// Invalid remaining length encoding - kill the connection
_state = MQTT_DISCONNECTED;
_client->stop();
return 0;
}
if(!readByte(&digit)) return 0; if(!readByte(&digit)) return 0;
buffer[len++] = digit; this->buffer[len++] = digit;
length += (digit & 127) * multiplier; length += (digit & 127) * multiplier;
multiplier *= 128; multiplier <<=7; //multiplier *= 128
} while ((digit & 128) != 0); } while ((digit & 128) != 0);
*lengthLength = len-1; *lengthLength = len-1;
if (isPublish) { if (isPublish) {
// Read in topic length to calculate bytes to skip over for Stream writing // Read in topic length to calculate bytes to skip over for Stream writing
if(!readByte(buffer, &len)) return 0; if(!readByte(this->buffer, &len)) return 0;
if(!readByte(buffer, &len)) return 0; if(!readByte(this->buffer, &len)) return 0;
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2]; skip = (this->buffer[*lengthLength+1]<<8)+this->buffer[*lengthLength+2];
start = 2; start = 2;
if (buffer[0]&MQTTQOS1) { if (this->buffer[0]&MQTTQOS1) {
// skip message id // skip message id
skip += 2; skip += 2;
} }
} }
uint32_t idx = len;
for (uint16_t i = start;i<length;i++) { for (uint32_t i = start;i<length;i++) {
if(!readByte(&digit)) return 0; if(!readByte(&digit)) return 0;
if (this->stream) { if (this->stream) {
if (isPublish && len-*lengthLength-2>skip) { if (isPublish && idx-*lengthLength-2>skip) {
this->stream->write(digit); this->stream->write(digit);
} }
} }
if (len < MQTT_MAX_PACKET_SIZE) {
buffer[len] = digit; if (len < this->bufferSize) {
this->buffer[len] = digit;
len++;
} }
len++; idx++;
} }
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) { if (!this->stream && idx > this->bufferSize) {
len = 0; // This will cause the packet to be ignored. len = 0; // This will cause the packet to be ignored.
} }
return len; return len;
} }
boolean PubSubClient::loop() { boolean PubSubClient::loop() {
if (connected()) { if (connected()) {
unsigned long t = millis(); unsigned long t = millis();
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) { if ((t - lastInActivity > this->keepAlive*1000UL) || (t - lastOutActivity > this->keepAlive*1000UL)) {
if (pingOutstanding) { if (pingOutstanding) {
this->_state = MQTT_CONNECTION_TIMEOUT; this->_state = MQTT_CONNECTION_TIMEOUT;
_client->stop(); _client->stop();
return false; return false;
} else { } else {
buffer[0] = MQTTPINGREQ; this->buffer[0] = MQTTPINGREQ;
buffer[1] = 0; this->buffer[1] = 0;
_client->write(buffer,2); _client->write(this->buffer,2);
lastOutActivity = t; lastOutActivity = t;
lastInActivity = t; lastInActivity = t;
pingOutstanding = true; pingOutstanding = true;
@ -303,40 +391,41 @@ boolean PubSubClient::loop() {
uint8_t *payload; uint8_t *payload;
if (len > 0) { if (len > 0) {
lastInActivity = t; lastInActivity = t;
uint8_t type = buffer[0]&0xF0; uint8_t type = this->buffer[0]&0xF0;
if (type == MQTTPUBLISH) { if (type == MQTTPUBLISH) {
if (callback) { if (callback) {
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; uint16_t tl = (this->buffer[llen+1]<<8)+this->buffer[llen+2]; /* topic length in bytes */
char topic[tl+1]; memmove(this->buffer+llen+2,this->buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
for (uint16_t i=0;i<tl;i++) { this->buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
topic[i] = buffer[llen+3+i]; char *topic = (char*) this->buffer+llen+2;
}
topic[tl] = 0;
// msgId only present for QOS>0 // msgId only present for QOS>0
if ((buffer[0]&0x06) == MQTTQOS1) { if ((this->buffer[0]&0x06) == MQTTQOS1) {
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1]; msgId = (this->buffer[llen+3+tl]<<8)+this->buffer[llen+3+tl+1];
payload = buffer+llen+3+tl+2; payload = this->buffer+llen+3+tl+2;
callback(topic,payload,len-llen-3-tl-2); callback(topic,payload,len-llen-3-tl-2);
buffer[0] = MQTTPUBACK; this->buffer[0] = MQTTPUBACK;
buffer[1] = 2; this->buffer[1] = 2;
buffer[2] = (msgId >> 8); this->buffer[2] = (msgId >> 8);
buffer[3] = (msgId & 0xFF); this->buffer[3] = (msgId & 0xFF);
_client->write(buffer,4); _client->write(this->buffer,4);
lastOutActivity = t; lastOutActivity = t;
} else { } else {
payload = buffer+llen+3+tl; payload = this->buffer+llen+3+tl;
callback(topic,payload,len-llen-3-tl); callback(topic,payload,len-llen-3-tl);
} }
} }
} else if (type == MQTTPINGREQ) { } else if (type == MQTTPINGREQ) {
buffer[0] = MQTTPINGRESP; this->buffer[0] = MQTTPINGRESP;
buffer[1] = 0; this->buffer[1] = 0;
_client->write(buffer,2); _client->write(this->buffer,2);
} else if (type == MQTTPINGRESP) { } else if (type == MQTTPINGRESP) {
pingOutstanding = false; pingOutstanding = false;
} }
} else if (!connected()) {
// readPacket has closed the connection
return false;
} }
} }
return true; return true;
@ -345,11 +434,11 @@ boolean PubSubClient::loop() {
} }
boolean PubSubClient::publish(const char* topic, const char* payload) { boolean PubSubClient::publish(const char* topic, const char* payload) {
return publish(topic,(const uint8_t*)payload,strlen(payload),false); return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,false);
} }
boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
return publish(topic,(const uint8_t*)payload,strlen(payload),retained); return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,retained);
} }
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) { boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
@ -358,26 +447,34 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
if (connected()) { if (connected()) {
if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) { if (this->bufferSize < MQTT_MAX_HEADER_SIZE + 2+strnlen(topic, this->bufferSize) + plength) {
// Too long // Too long
return false; return false;
} }
// Leave room in the buffer for header and variable length field // Leave room in the buffer for header and variable length field
uint16_t length = 5; uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic,buffer,length); length = writeString(topic,this->buffer,length);
// Add payload
uint16_t i; uint16_t i;
for (i=0;i<plength;i++) { for (i=0;i<plength;i++) {
buffer[length++] = payload[i]; this->buffer[length++] = payload[i];
} }
// Write the header
uint8_t header = MQTTPUBLISH; uint8_t header = MQTTPUBLISH;
if (retained) { if (retained) {
header |= 1; header |= 1;
} }
return write(header,buffer,length-5); return write(header,this->buffer,length-MQTT_MAX_HEADER_SIZE);
} }
return false; return false;
} }
boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
return publish_P(topic, (const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0, retained);
}
boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
uint8_t llen = 0; uint8_t llen = 0;
uint8_t digit; uint8_t digit;
@ -387,32 +484,33 @@ boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsig
unsigned int i; unsigned int i;
uint8_t header; uint8_t header;
unsigned int len; unsigned int len;
int expectedLength;
if (!connected()) { if (!connected()) {
return false; return false;
} }
tlen = strlen(topic); tlen = strnlen(topic, this->bufferSize);
header = MQTTPUBLISH; header = MQTTPUBLISH;
if (retained) { if (retained) {
header |= 1; header |= 1;
} }
buffer[pos++] = header; this->buffer[pos++] = header;
len = plength + 2 + tlen; len = plength + 2 + tlen;
do { do {
digit = len % 128; digit = len & 127; //digit = len %128
len = len / 128; len >>= 7; //len = len / 128
if (len > 0) { if (len > 0) {
digit |= 0x80; digit |= 0x80;
} }
buffer[pos++] = digit; this->buffer[pos++] = digit;
llen++; llen++;
} while(len>0); } while(len>0);
pos = writeString(topic,buffer,pos); pos = writeString(topic,this->buffer,pos);
rc += _client->write(buffer,pos); rc += _client->write(this->buffer,pos);
for (i=0;i<plength;i++) { for (i=0;i<plength;i++) {
rc += _client->write((char)pgm_read_byte_near(payload + i)); rc += _client->write((char)pgm_read_byte_near(payload + i));
@ -420,19 +518,52 @@ boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsig
lastOutActivity = millis(); lastOutActivity = millis();
return rc == tlen + 4 + plength; expectedLength = 1 + llen + 2 + tlen + plength;
return (rc == expectedLength);
} }
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
if (connected()) {
// Send the header and variable length field
uint16_t length = MQTT_MAX_HEADER_SIZE;
length = writeString(topic,this->buffer,length);
uint8_t header = MQTTPUBLISH;
if (retained) {
header |= 1;
}
size_t hlen = buildHeader(header, this->buffer, plength+length-MQTT_MAX_HEADER_SIZE);
uint16_t rc = _client->write(this->buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
lastOutActivity = millis();
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
}
return false;
}
int PubSubClient::endPublish() {
return 1;
}
size_t PubSubClient::write(uint8_t data) {
lastOutActivity = millis();
return _client->write(data);
}
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
lastOutActivity = millis();
return _client->write(buffer,size);
}
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
uint8_t lenBuf[4]; uint8_t lenBuf[4];
uint8_t llen = 0; uint8_t llen = 0;
uint8_t digit; uint8_t digit;
uint8_t pos = 0; uint8_t pos = 0;
uint16_t rc;
uint16_t len = length; uint16_t len = length;
do { do {
digit = len % 128;
len = len / 128; digit = len & 127; //digit = len %128
len >>= 7; //len = len / 128
if (len > 0) { if (len > 0) {
digit |= 0x80; digit |= 0x80;
} }
@ -442,12 +573,18 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
buf[4-llen] = header; buf[4-llen] = header;
for (int i=0;i<llen;i++) { for (int i=0;i<llen;i++) {
buf[5-llen+i] = lenBuf[i]; buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
} }
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
}
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
uint16_t rc;
uint8_t hlen = buildHeader(header, buf, length);
#ifdef MQTT_MAX_TRANSFER_SIZE #ifdef MQTT_MAX_TRANSFER_SIZE
uint8_t* writeBuf = buf+(4-llen); uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
uint16_t bytesRemaining = length+1+llen; //Match the length type uint16_t bytesRemaining = length+hlen; //Match the length type
uint8_t bytesToWrite; uint8_t bytesToWrite;
boolean result = true; boolean result = true;
while((bytesRemaining > 0) && result) { while((bytesRemaining > 0) && result) {
@ -459,9 +596,9 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
} }
return result; return result;
#else #else
rc = _client->write(buf+(4-llen),length+1+llen); rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
lastOutActivity = millis(); lastOutActivity = millis();
return (rc == 1+llen+length); return (rc == hlen+length);
#endif #endif
} }
@ -470,53 +607,62 @@ boolean PubSubClient::subscribe(const char* topic) {
} }
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
if (qos < 0 || qos > 1) { size_t topicLength = strnlen(topic, this->bufferSize);
if (topic == 0) {
return false; return false;
} }
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { if (qos > 1) {
return false;
}
if (this->bufferSize < 9 + topicLength) {
// Too long // Too long
return false; return false;
} }
if (connected()) { if (connected()) {
// Leave room in the buffer for header and variable length field // Leave room in the buffer for header and variable length field
uint16_t length = 5; uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++; nextMsgId++;
if (nextMsgId == 0) { if (nextMsgId == 0) {
nextMsgId = 1; nextMsgId = 1;
} }
buffer[length++] = (nextMsgId >> 8); this->buffer[length++] = (nextMsgId >> 8);
buffer[length++] = (nextMsgId & 0xFF); this->buffer[length++] = (nextMsgId & 0xFF);
length = writeString((char*)topic, buffer,length); length = writeString((char*)topic, this->buffer,length);
buffer[length++] = qos; this->buffer[length++] = qos;
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5); return write(MQTTSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE);
} }
return false; return false;
} }
boolean PubSubClient::unsubscribe(const char* topic) { boolean PubSubClient::unsubscribe(const char* topic) {
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) { size_t topicLength = strnlen(topic, this->bufferSize);
if (topic == 0) {
return false;
}
if (this->bufferSize < 9 + topicLength) {
// Too long // Too long
return false; return false;
} }
if (connected()) { if (connected()) {
uint16_t length = 5; uint16_t length = MQTT_MAX_HEADER_SIZE;
nextMsgId++; nextMsgId++;
if (nextMsgId == 0) { if (nextMsgId == 0) {
nextMsgId = 1; nextMsgId = 1;
} }
buffer[length++] = (nextMsgId >> 8); this->buffer[length++] = (nextMsgId >> 8);
buffer[length++] = (nextMsgId & 0xFF); this->buffer[length++] = (nextMsgId & 0xFF);
length = writeString(topic, buffer,length); length = writeString(topic, this->buffer,length);
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5); return write(MQTTUNSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE);
} }
return false; return false;
} }
void PubSubClient::disconnect() { void PubSubClient::disconnect() {
buffer[0] = MQTTDISCONNECT; this->buffer[0] = MQTTDISCONNECT;
buffer[1] = 0; this->buffer[1] = 0;
_client->write(buffer,2); _client->write(this->buffer,2);
_state = MQTT_DISCONNECTED; _state = MQTT_DISCONNECTED;
_client->flush();
_client->stop(); _client->stop();
lastInActivity = lastOutActivity = millis(); lastInActivity = lastOutActivity = millis();
} }
@ -547,6 +693,8 @@ boolean PubSubClient::connected() {
_client->flush(); _client->flush();
_client->stop(); _client->stop();
} }
} else {
return this->_state == MQTT_CONNECTED;
} }
} }
return rc; return rc;
@ -570,7 +718,7 @@ PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
return *this; return *this;
} }
PubSubClient& PubSubClient::setCallback(void(*callback)(char*,uint8_t*,unsigned int)){ PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
this->callback = callback; this->callback = callback;
return *this; return *this;
} }
@ -588,3 +736,34 @@ PubSubClient& PubSubClient::setStream(Stream& stream){
int PubSubClient::state() { int PubSubClient::state() {
return this->_state; return this->_state;
} }
boolean PubSubClient::setBufferSize(uint16_t size) {
if (size == 0) {
// Cannot set it back to 0
return false;
}
if (this->bufferSize == 0) {
this->buffer = (uint8_t*)malloc(size);
} else {
uint8_t* newBuffer = (uint8_t*)realloc(this->buffer, size);
if (newBuffer != NULL) {
this->buffer = newBuffer;
} else {
return false;
}
}
this->bufferSize = size;
return (this->buffer != NULL);
}
uint16_t PubSubClient::getBufferSize() {
return this->bufferSize;
}
PubSubClient& PubSubClient::setKeepAlive(uint16_t keepAlive) {
this->keepAlive = keepAlive;
return *this;
}
PubSubClient& PubSubClient::setSocketTimeout(uint16_t timeout) {
this->socketTimeout = timeout;
return *this;
}

View File

@ -17,16 +17,24 @@
// MQTT_VERSION : Pick the version // MQTT_VERSION : Pick the version
//#define MQTT_VERSION MQTT_VERSION_3_1 //#define MQTT_VERSION MQTT_VERSION_3_1
#ifndef MQTT_VERSION
#define MQTT_VERSION MQTT_VERSION_3_1_1 #define MQTT_VERSION MQTT_VERSION_3_1_1
#endif
// MQTT_MAX_PACKET_SIZE : Maximum packet size // MQTT_MAX_PACKET_SIZE : Maximum packet size. Override with setBufferSize().
#define MQTT_MAX_PACKET_SIZE 128 #ifndef MQTT_MAX_PACKET_SIZE
#define MQTT_MAX_PACKET_SIZE 256
#endif
// MQTT_KEEPALIVE : keepAlive interval in Seconds // MQTT_KEEPALIVE : keepAlive interval in Seconds. Override with setKeepAlive()
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 15 #define MQTT_KEEPALIVE 15
#endif
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds // MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds. Override with setSocketTimeout()
#ifndef MQTT_SOCKET_TIMEOUT
#define MQTT_SOCKET_TIMEOUT 15 #define MQTT_SOCKET_TIMEOUT 15
#endif
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client // MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to // in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
@ -65,22 +73,40 @@
#define MQTTQOS1 (1 << 1) #define MQTTQOS1 (1 << 1)
#define MQTTQOS2 (2 << 1) #define MQTTQOS2 (2 << 1)
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*,uint8_t*,unsigned int) // Maximum size of fixed header and variable length size header
#define MQTT_MAX_HEADER_SIZE 5
class PubSubClient { #if defined(ESP8266) || defined(ESP32)
#include <functional>
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
#else
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
#endif
#define CHECK_STRING_LENGTH(l,s) if (l+2+strnlen(s, this->bufferSize) > this->bufferSize) {_client->stop();return false;}
class PubSubClient : public Print {
private: private:
Client* _client; Client* _client;
uint8_t buffer[MQTT_MAX_PACKET_SIZE]; uint8_t* buffer;
uint16_t bufferSize;
uint16_t keepAlive;
uint16_t socketTimeout;
uint16_t nextMsgId; uint16_t nextMsgId;
unsigned long lastOutActivity; unsigned long lastOutActivity;
unsigned long lastInActivity; unsigned long lastInActivity;
bool pingOutstanding; bool pingOutstanding;
MQTT_CALLBACK_SIGNATURE; MQTT_CALLBACK_SIGNATURE;
uint16_t readPacket(uint8_t*); uint32_t readPacket(uint8_t*);
boolean readByte(uint8_t * result); boolean readByte(uint8_t * result);
boolean readByte(uint8_t * result, uint16_t * index); boolean readByte(uint8_t * result, uint16_t * index);
boolean write(uint8_t header, uint8_t* buf, uint16_t length); boolean write(uint8_t header, uint8_t* buf, uint16_t length);
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos); uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
// Build up the header ready to send
// Returns the size of the header
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
IPAddress ip; IPAddress ip;
const char* domain; const char* domain;
uint16_t port; uint16_t port;
@ -102,29 +128,56 @@ public:
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client); PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&); PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
~PubSubClient();
PubSubClient& setServer(IPAddress ip, uint16_t port); PubSubClient& setServer(IPAddress ip, uint16_t port);
PubSubClient& setServer(uint8_t * ip, uint16_t port); PubSubClient& setServer(uint8_t * ip, uint16_t port);
PubSubClient& setServer(const char * domain, uint16_t port); PubSubClient& setServer(const char * domain, uint16_t port);
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE); PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
PubSubClient& setClient(Client& client); PubSubClient& setClient(Client& client);
PubSubClient& setStream(Stream& stream); PubSubClient& setStream(Stream& stream);
PubSubClient& setKeepAlive(uint16_t keepAlive);
PubSubClient& setSocketTimeout(uint16_t timeout);
boolean setBufferSize(uint16_t size);
uint16_t getBufferSize();
boolean connect(const char* id); boolean connect(const char* id);
boolean connect(const char* id, const char* user, const char* pass); boolean connect(const char* id, const char* user, const char* pass);
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage); boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
void disconnect(); void disconnect();
boolean publish(const char* topic, const char* payload); boolean publish(const char* topic, const char* payload);
boolean publish(const char* topic, const char* payload, boolean retained); boolean publish(const char* topic, const char* payload, boolean retained);
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
boolean publish_P(const char* topic, const char* payload, boolean retained);
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained); boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
// Start to publish a message.
// This API:
// beginPublish(...)
// one or more calls to write(...)
// endPublish()
// Allows for arbitrarily large payloads to be sent without them having to be copied into
// a new buffer and held in memory at one time
// Returns 1 if the message was started successfully, 0 if there was an error
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
// Finish off this publish message (started with beginPublish)
// Returns 1 if the packet was sent successfully, 0 if there was an error
int endPublish();
// Write a single byte of payload (only to be used with beginPublish/endPublish)
virtual size_t write(uint8_t);
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
// Returns the number of bytes written
virtual size_t write(const uint8_t *buffer, size_t size);
boolean subscribe(const char* topic); boolean subscribe(const char* topic);
boolean subscribe(const char* topic, uint8_t qos); boolean subscribe(const char* topic, uint8_t qos);
boolean unsubscribe(const char* topic); boolean unsubscribe(const char* topic);
boolean loop(); boolean loop();
boolean connected(); boolean connected();
int state(); int state();
}; };

View File

@ -98,6 +98,33 @@ int test_connect_fails_on_bad_rc() {
END_IT END_IT
} }
int test_connect_non_clean_session() {
IT("sends a properly formatted non-clean session connect packet and succeeds");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte expectServer[] = { 172, 16, 0, 2 };
shimClient.expectConnect(expectServer,1883);
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,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,26);
shimClient.respond(connack,4);
PubSubClient client(server, 1883, callback, shimClient);
int state = client.state();
IS_TRUE(state == MQTT_DISCONNECTED);
int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0);
IS_TRUE(rc);
IS_FALSE(shimClient.error());
state = client.state();
IS_TRUE(state == MQTT_CONNECTED);
END_IT
}
int test_connect_accepts_username_password() { int test_connect_accepts_username_password() {
IT("accepts a username and password"); IT("accepts a username and password");
ShimClient shimClient; ShimClient shimClient;
@ -133,6 +160,23 @@ int test_connect_accepts_username_no_password() {
END_IT END_IT
} }
int test_connect_accepts_username_blank_password() {
IT("accepts a username and blank password");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connect[] = { 0x10,0x20,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x0};
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
shimClient.expect(connect,0x26);
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_ignores_password_no_username() { int test_connect_ignores_password_no_username() {
IT("ignores a password but no username"); IT("ignores a password but no username");
@ -236,13 +280,47 @@ int test_connect_disconnect_connect() {
END_IT END_IT
} }
int test_connect_custom_keepalive() {
IT("sends a properly formatted connect packet with custom keepalive value");
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte expectServer[] = { 172, 16, 0, 2 };
shimClient.expectConnect(expectServer,1883);
// Set keepalive to 300secs == 0x01 0x2c
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x01,0x2c,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
shimClient.expect(connect,26);
shimClient.respond(connack,4);
PubSubClient client(server, 1883, callback, shimClient);
int state = client.state();
IS_TRUE(state == MQTT_DISCONNECTED);
client.setKeepAlive(300);
int rc = client.connect((char*)"client_test1");
IS_TRUE(rc);
IS_FALSE(shimClient.error());
state = client.state();
IS_TRUE(state == MQTT_CONNECTED);
END_IT
}
int main() int main()
{ {
SUITE("Connect"); SUITE("Connect");
test_connect_fails_no_network(); test_connect_fails_no_network();
test_connect_fails_on_no_response(); test_connect_fails_on_no_response();
test_connect_properly_formatted(); test_connect_properly_formatted();
test_connect_non_clean_session();
test_connect_accepts_username_password(); test_connect_accepts_username_password();
test_connect_fails_on_bad_rc(); test_connect_fails_on_bad_rc();
test_connect_properly_formatted_hostname(); test_connect_properly_formatted_hostname();
@ -252,5 +330,7 @@ int main()
test_connect_with_will(); test_connect_with_will();
test_connect_with_will_username_password(); test_connect_with_will_username_password();
test_connect_disconnect_connect(); test_connect_disconnect_connect();
test_connect_custom_keepalive();
FINISH FINISH
} }

View File

@ -5,6 +5,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include "Print.h"
extern "C"{ extern "C"{
@ -20,4 +21,6 @@ extern "C"{
#define PROGMEM #define PROGMEM
#define pgm_read_byte_near(x) *(x) #define pgm_read_byte_near(x) *(x)
#define yield(x) {}
#endif // Arduino_h #endif // Arduino_h

View File

@ -2,9 +2,13 @@
#include "Arduino.h" #include "Arduino.h"
Buffer::Buffer() { Buffer::Buffer() {
this->pos = 0;
this->length = 0;
} }
Buffer::Buffer(uint8_t* buf, size_t size) { Buffer::Buffer(uint8_t* buf, size_t size) {
this->pos = 0;
this->length = 0;
this->add(buf,size); this->add(buf,size);
} }
bool Buffer::available() { bool Buffer::available() {

View File

@ -5,18 +5,18 @@
class Buffer { class Buffer {
private: private:
uint8_t buffer[1024]; uint8_t buffer[2048];
uint16_t pos; uint16_t pos;
uint16_t length; uint16_t length;
public: public:
Buffer(); Buffer();
Buffer(uint8_t* buf, size_t size); Buffer(uint8_t* buf, size_t size);
virtual bool available(); virtual bool available();
virtual uint8_t next(); virtual uint8_t next();
virtual void reset(); virtual void reset();
virtual void add(uint8_t* buf, size_t size); virtual void add(uint8_t* buf, size_t size);
}; };

28
tests/src/lib/Print.h Normal file
View File

@ -0,0 +1,28 @@
/*
Print.h - Base class that provides print() and println()
Copyright (c) 2008 David A. Mellis. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef Print_h
#define Print_h
class Print {
public:
virtual size_t write(uint8_t) = 0;
};
#endif

View File

@ -134,6 +134,7 @@ int test_publish_too_long() {
shimClient.respond(connack,4); shimClient.respond(connack,4);
PubSubClient client(server, 1883, callback, shimClient); PubSubClient client(server, 1883, callback, shimClient);
client.setBufferSize(128);
int rc = client.connect((char*)"client_test1"); int rc = client.connect((char*)"client_test1");
IS_TRUE(rc); IS_TRUE(rc);

View File

@ -20,6 +20,7 @@ void reset_callback() {
} }
void callback(char* topic, byte* payload, unsigned int length) { void callback(char* topic, byte* payload, unsigned int length) {
TRACE("Callback received topic=[" << topic << "] length=" << length << "\n")
callback_called = true; callback_called = true;
strcpy(lastTopic,topic); strcpy(lastTopic,topic);
memcpy(lastPayload,payload,length); memcpy(lastPayload,payload,length);
@ -102,10 +103,15 @@ int test_receive_max_sized_message() {
shimClient.respond(connack,4); shimClient.respond(connack,4);
PubSubClient client(server, 1883, callback, shimClient); PubSubClient client(server, 1883, callback, shimClient);
int length = 80; // If this is changed to > 128 then the publish packet below
// is no longer valid as it assumes the remaining length
// is a single-byte. Don't make that mistake like I just
// did and lose a whole evening tracking down the issue.
client.setBufferSize(length);
int rc = client.connect((char*)"client_test1"); int rc = client.connect((char*)"client_test1");
IS_TRUE(rc); IS_TRUE(rc);
int 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 publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
byte bigPublish[length]; byte bigPublish[length];
memset(bigPublish,'A',length); memset(bigPublish,'A',length);
@ -137,11 +143,13 @@ int test_receive_oversized_message() {
byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
shimClient.respond(connack,4); shimClient.respond(connack,4);
int length = 80; // See comment in test_receive_max_sized_message before changing this value
PubSubClient client(server, 1883, callback, shimClient); PubSubClient client(server, 1883, callback, shimClient);
client.setBufferSize(length-1);
int rc = client.connect((char*)"client_test1"); int rc = client.connect((char*)"client_test1");
IS_TRUE(rc); IS_TRUE(rc);
int 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 publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
byte bigPublish[length]; byte bigPublish[length];
memset(bigPublish,'A',length); memset(bigPublish,'A',length);
@ -160,8 +168,86 @@ int test_receive_oversized_message() {
END_IT END_IT
} }
int test_drop_invalid_remaining_length_message() {
IT("drops invalid remaining length 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,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
shimClient.respond(publish,20);
rc = client.loop();
IS_FALSE(rc);
IS_FALSE(callback_called);
IS_FALSE(shimClient.error());
END_IT
}
int test_resize_buffer() {
IT("receives a message larger than the default maximum");
reset_callback();
ShimClient shimClient;
shimClient.setAllowConnect(true);
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
shimClient.respond(connack,4);
int length = 80; // See comment in test_receive_max_sized_message before changing this value
PubSubClient client(server, 1883, callback, shimClient);
client.setBufferSize(length-1);
int rc = client.connect((char*)"client_test1");
IS_TRUE(rc);
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);
// Send it twice
shimClient.respond(bigPublish,length);
shimClient.respond(bigPublish,length);
rc = client.loop();
IS_TRUE(rc);
// First message fails as it is too big
IS_FALSE(callback_called);
// Resize the buffer
client.setBufferSize(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_stream_message() { int test_receive_oversized_stream_message() {
IT("drops an oversized message"); IT("receive an oversized streamed message");
reset_callback(); reset_callback();
Stream stream; Stream stream;
@ -172,11 +258,13 @@ int test_receive_oversized_stream_message() {
byte connack[] = { 0x20, 0x02, 0x00, 0x00 }; byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
shimClient.respond(connack,4); shimClient.respond(connack,4);
int length = 80; // See comment in test_receive_max_sized_message before changing this value
PubSubClient client(server, 1883, callback, shimClient, stream); PubSubClient client(server, 1883, callback, shimClient, stream);
client.setBufferSize(length-1);
int rc = client.connect((char*)"client_test1"); int rc = client.connect((char*)"client_test1");
IS_TRUE(rc); IS_TRUE(rc);
int 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 publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
byte bigPublish[length]; byte bigPublish[length];
@ -193,7 +281,8 @@ int test_receive_oversized_stream_message() {
IS_TRUE(callback_called); IS_TRUE(callback_called);
IS_TRUE(strcmp(lastTopic,"topic")==0); IS_TRUE(strcmp(lastTopic,"topic")==0);
IS_TRUE(lastLength == length-9);
IS_TRUE(lastLength == length-10);
IS_FALSE(stream.error()); IS_FALSE(stream.error());
IS_FALSE(shimClient.error()); IS_FALSE(shimClient.error());
@ -241,7 +330,9 @@ int main()
test_receive_callback(); test_receive_callback();
test_receive_stream(); test_receive_stream();
test_receive_max_sized_message(); test_receive_max_sized_message();
test_drop_invalid_remaining_length_message();
test_receive_oversized_message(); test_receive_oversized_message();
test_resize_buffer();
test_receive_oversized_stream_message(); test_receive_oversized_stream_message();
test_receive_qos1(); test_receive_qos1();

View File

@ -106,6 +106,7 @@ int test_subscribe_too_long() {
shimClient.respond(connack,4); shimClient.respond(connack,4);
PubSubClient client(server, 1883, callback, shimClient); PubSubClient client(server, 1883, callback, shimClient);
client.setBufferSize(128);
int rc = client.connect((char*)"client_test1"); int rc = client.connect((char*)"client_test1");
IS_TRUE(rc); IS_TRUE(rc);

View File

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

View File

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

View File

@ -10,170 +10,172 @@ import re
from testcases import settings from testcases import settings
class Workspace(object): 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): def __init__(self):
if os.path.isdir(self.build_dir): self.root_dir = os.getcwd()
shutil.rmtree(self.build_dir) self.build_dir = os.path.join(self.root_dir, "tmpbin")
os.mkdir(self.build_dir) self.log_dir = os.path.join(self.root_dir, "logs")
if os.path.isdir(self.log_dir): self.tests_dir = os.path.join(self.root_dir, "testcases")
shutil.rmtree(self.log_dir) self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples")
os.mkdir(self.log_dir) self.examples = []
self.tests = []
os.chdir(self.build_dir) if not os.path.isdir("../PubSubClient"):
call(["ino","init"]) raise Exception("Cannot find PubSubClient library")
try:
shutil.copytree("../../PubSubClient","lib/PubSubClient") return __import__('ino')
except ImportError:
filenames = [] raise Exception("ino tool not installed")
for root, dirs, files in os.walk(self.examples_dir):
filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")] def init(self):
filenames.sort() if os.path.isdir(self.build_dir):
for e in filenames: shutil.rmtree(self.build_dir)
self.examples.append(Sketch(self,e)) os.mkdir(self.build_dir)
if os.path.isdir(self.log_dir):
filenames = [] shutil.rmtree(self.log_dir)
for root, dirs, files in os.walk(self.tests_dir): os.mkdir(self.log_dir)
filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
filenames.sort() os.chdir(self.build_dir)
for e in filenames: call(["ino", "init"])
self.tests.append(Sketch(self,e))
shutil.copytree("../../PubSubClient", "lib/PubSubClient")
def clean(self):
shutil.rmtree(self.build_dir) 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): class Sketch(object):
def __init__(self,wksp,fn): def __init__(self, wksp, fn):
self.w = wksp self.w = wksp
self.filename = fn self.filename = fn
self.basename = os.path.basename(self.filename) 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_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_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),)) self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),))
def build(self): def build(self):
sys.stdout.write(" Build: ") sys.stdout.write(" Build: ")
sys.stdout.flush() 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
# 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(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun))
if not result.wasSuccessful():
if len(result.failures) > 0:
for f in result.failures:
print("-- " + str(f[0]))
print(f[1])
if len(result.errors) > 0:
print(" Errors:")
for f in result.errors:
print("-- " + str(f[0]))
print(f[1])
c.tearDownClass()
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__': if __name__ == '__main__':
run_tests = True run_tests = True
w = Workspace() w = Workspace()
w.init() w.init()
for e in w.examples: for e in w.examples:
print "--------------------------------------" print("--------------------------------------")
print "[%s]"%(e.basename,) print("[" + e.basename + "]")
if e.build() and run_tests: if e.build() and run_tests:
e.test() e.test()
for e in w.tests: for e in w.tests:
print "--------------------------------------" print("--------------------------------------")
print "[%s]"%(e.basename,) print("[" + e.basename + "]")
if e.build() and run_tests: if e.build() and run_tests:
e.test() e.test()
w.clean() w.clean()