mirror of https://github.com/arendst/Tasmota.git
Update PubSubClient library to 2.7+fixes
Update PubSubClient library to 2.7+fixes
This commit is contained in:
parent
f5fc4ae9dd
commit
9d780519f7
|
@ -1,43 +0,0 @@
|
||||||
import unittest
|
|
||||||
import settings
|
|
||||||
|
|
||||||
import time
|
|
||||||
import mosquitto
|
|
||||||
|
|
||||||
import serial
|
|
||||||
|
|
||||||
def on_message(mosq, obj, msg):
|
|
||||||
obj.message_queue.append(msg)
|
|
||||||
|
|
||||||
class mqtt_basic(unittest.TestCase):
|
|
||||||
|
|
||||||
message_queue = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(self):
|
|
||||||
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self)
|
|
||||||
self.client.connect(settings.server_ip)
|
|
||||||
self.client.on_message = on_message
|
|
||||||
self.client.subscribe("outTopic",0)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(self):
|
|
||||||
self.client.disconnect()
|
|
||||||
|
|
||||||
def test_one(self):
|
|
||||||
i=30
|
|
||||||
while len(self.message_queue) == 0 and i > 0:
|
|
||||||
self.client.loop()
|
|
||||||
time.sleep(0.5)
|
|
||||||
i -= 1
|
|
||||||
self.assertTrue(i>0, "message receive timed-out")
|
|
||||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
|
||||||
msg = self.message_queue[0]
|
|
||||||
self.assertEqual(msg.mid,0,"message id not 0")
|
|
||||||
self.assertEqual(msg.topic,"outTopic","message topic incorrect")
|
|
||||||
self.assertEqual(msg.payload,"hello world")
|
|
||||||
self.assertEqual(msg.qos,0,"message qos not 0")
|
|
||||||
self.assertEqual(msg.retain,False,"message retain flag incorrect")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import unittest
|
|
||||||
import settings
|
|
||||||
|
|
||||||
import time
|
|
||||||
import mosquitto
|
|
||||||
|
|
||||||
import serial
|
|
||||||
|
|
||||||
def on_message(mosq, obj, msg):
|
|
||||||
obj.message_queue.append(msg)
|
|
||||||
|
|
||||||
class mqtt_publish_in_callback(unittest.TestCase):
|
|
||||||
|
|
||||||
message_queue = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(self):
|
|
||||||
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self)
|
|
||||||
self.client.connect(settings.server_ip)
|
|
||||||
self.client.on_message = on_message
|
|
||||||
self.client.subscribe("outTopic",0)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(self):
|
|
||||||
self.client.disconnect()
|
|
||||||
|
|
||||||
def test_connect(self):
|
|
||||||
i=30
|
|
||||||
while len(self.message_queue) == 0 and i > 0:
|
|
||||||
self.client.loop()
|
|
||||||
time.sleep(0.5)
|
|
||||||
i -= 1
|
|
||||||
self.assertTrue(i>0, "message receive timed-out")
|
|
||||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
|
||||||
msg = self.message_queue.pop(0)
|
|
||||||
self.assertEqual(msg.mid,0,"message id not 0")
|
|
||||||
self.assertEqual(msg.topic,"outTopic","message topic incorrect")
|
|
||||||
self.assertEqual(msg.payload,"hello world")
|
|
||||||
self.assertEqual(msg.qos,0,"message qos not 0")
|
|
||||||
self.assertEqual(msg.retain,False,"message retain flag incorrect")
|
|
||||||
|
|
||||||
|
|
||||||
def test_publish(self):
|
|
||||||
self.assertEqual(len(self.message_queue), 0, "message queue not empty")
|
|
||||||
payload = "abcdefghij"
|
|
||||||
self.client.publish("inTopic",payload)
|
|
||||||
|
|
||||||
i=30
|
|
||||||
while len(self.message_queue) == 0 and i > 0:
|
|
||||||
self.client.loop()
|
|
||||||
time.sleep(0.5)
|
|
||||||
i -= 1
|
|
||||||
|
|
||||||
self.assertTrue(i>0, "message receive timed-out")
|
|
||||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
|
||||||
msg = self.message_queue.pop(0)
|
|
||||||
self.assertEqual(msg.mid,0,"message id not 0")
|
|
||||||
self.assertEqual(msg.topic,"outTopic","message topic incorrect")
|
|
||||||
self.assertEqual(msg.payload,payload)
|
|
||||||
self.assertEqual(msg.qos,0,"message qos not 0")
|
|
||||||
self.assertEqual(msg.retain,False,"message retain flag incorrect")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
from subprocess import call
|
|
||||||
import importlib
|
|
||||||
import unittest
|
|
||||||
import re
|
|
||||||
|
|
||||||
from testcases import settings
|
|
||||||
|
|
||||||
class Workspace(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.root_dir = os.getcwd()
|
|
||||||
self.build_dir = os.path.join(self.root_dir,"tmpbin");
|
|
||||||
self.log_dir = os.path.join(self.root_dir,"logs");
|
|
||||||
self.tests_dir = os.path.join(self.root_dir,"testcases");
|
|
||||||
self.examples_dir = os.path.join(self.root_dir,"../PubSubClient/examples")
|
|
||||||
self.examples = []
|
|
||||||
self.tests = []
|
|
||||||
if not os.path.isdir("../PubSubClient"):
|
|
||||||
raise Exception("Cannot find PubSubClient library")
|
|
||||||
try:
|
|
||||||
import ino
|
|
||||||
except:
|
|
||||||
raise Exception("ino tool not installed")
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
if os.path.isdir(self.build_dir):
|
|
||||||
shutil.rmtree(self.build_dir)
|
|
||||||
os.mkdir(self.build_dir)
|
|
||||||
if os.path.isdir(self.log_dir):
|
|
||||||
shutil.rmtree(self.log_dir)
|
|
||||||
os.mkdir(self.log_dir)
|
|
||||||
|
|
||||||
os.chdir(self.build_dir)
|
|
||||||
call(["ino","init"])
|
|
||||||
|
|
||||||
shutil.copytree("../../PubSubClient","lib/PubSubClient")
|
|
||||||
|
|
||||||
filenames = []
|
|
||||||
for root, dirs, files in os.walk(self.examples_dir):
|
|
||||||
filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
|
|
||||||
filenames.sort()
|
|
||||||
for e in filenames:
|
|
||||||
self.examples.append(Sketch(self,e))
|
|
||||||
|
|
||||||
filenames = []
|
|
||||||
for root, dirs, files in os.walk(self.tests_dir):
|
|
||||||
filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
|
|
||||||
filenames.sort()
|
|
||||||
for e in filenames:
|
|
||||||
self.tests.append(Sketch(self,e))
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
shutil.rmtree(self.build_dir)
|
|
||||||
|
|
||||||
class Sketch(object):
|
|
||||||
def __init__(self,wksp,fn):
|
|
||||||
self.w = wksp
|
|
||||||
self.filename = fn
|
|
||||||
self.basename = os.path.basename(self.filename)
|
|
||||||
self.build_log = os.path.join(self.w.log_dir,"%s.log"%(os.path.basename(self.filename),))
|
|
||||||
self.build_err_log = os.path.join(self.w.log_dir,"%s.err.log"%(os.path.basename(self.filename),))
|
|
||||||
self.build_upload_log = os.path.join(self.w.log_dir,"%s.upload.log"%(os.path.basename(self.filename),))
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
sys.stdout.write(" Build: ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
# Copy sketch over, replacing IP addresses as necessary
|
|
||||||
fin = open(self.filename,"r")
|
|
||||||
lines = fin.readlines()
|
|
||||||
fin.close()
|
|
||||||
fout = open(os.path.join(self.w.build_dir,"src","sketch.ino"),"w")
|
|
||||||
for l in lines:
|
|
||||||
if re.match(r"^byte server\[\] = {",l):
|
|
||||||
fout.write("byte server[] = { %s };\n"%(settings.server_ip.replace(".",", "),))
|
|
||||||
elif re.match(r"^byte ip\[\] = {",l):
|
|
||||||
fout.write("byte ip[] = { %s };\n"%(settings.arduino_ip.replace(".",", "),))
|
|
||||||
else:
|
|
||||||
fout.write(l)
|
|
||||||
fout.flush()
|
|
||||||
fout.close()
|
|
||||||
|
|
||||||
# Run build
|
|
||||||
fout = open(self.build_log, "w")
|
|
||||||
ferr = open(self.build_err_log, "w")
|
|
||||||
rc = call(["ino","build"],stdout=fout,stderr=ferr)
|
|
||||||
fout.close()
|
|
||||||
ferr.close()
|
|
||||||
if rc == 0:
|
|
||||||
sys.stdout.write("pass")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
sys.stdout.write("fail")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
with open(self.build_err_log) as f:
|
|
||||||
for line in f:
|
|
||||||
print " ",line,
|
|
||||||
return False
|
|
||||||
|
|
||||||
def upload(self):
|
|
||||||
sys.stdout.write(" Upload: ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
fout = open(self.build_upload_log, "w")
|
|
||||||
rc = call(["ino","upload"],stdout=fout,stderr=fout)
|
|
||||||
fout.close()
|
|
||||||
if rc == 0:
|
|
||||||
sys.stdout.write("pass")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
sys.stdout.write("fail")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
with open(self.build_upload_log) as f:
|
|
||||||
for line in f:
|
|
||||||
print " ",line,
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test(self):
|
|
||||||
# import the matching test case, if it exists
|
|
||||||
try:
|
|
||||||
basename = os.path.basename(self.filename)[:-4]
|
|
||||||
i = importlib.import_module("testcases."+basename)
|
|
||||||
except:
|
|
||||||
sys.stdout.write(" Test: no tests found")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
return
|
|
||||||
c = getattr(i,basename)
|
|
||||||
|
|
||||||
testmethods = [m for m in dir(c) if m.startswith("test_")]
|
|
||||||
testmethods.sort()
|
|
||||||
tests = []
|
|
||||||
for m in testmethods:
|
|
||||||
tests.append(c(m))
|
|
||||||
|
|
||||||
result = unittest.TestResult()
|
|
||||||
c.setUpClass()
|
|
||||||
if self.upload():
|
|
||||||
sys.stdout.write(" Test: ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
for t in tests:
|
|
||||||
t.run(result)
|
|
||||||
print "%d/%d"%(result.testsRun-len(result.failures)-len(result.errors),result.testsRun)
|
|
||||||
if not result.wasSuccessful():
|
|
||||||
if len(result.failures) > 0:
|
|
||||||
for f in result.failures:
|
|
||||||
print "-- %s"%(str(f[0]),)
|
|
||||||
print f[1]
|
|
||||||
if len(result.errors) > 0:
|
|
||||||
print " Errors:"
|
|
||||||
for f in result.errors:
|
|
||||||
print "-- %s"%(str(f[0]),)
|
|
||||||
print f[1]
|
|
||||||
c.tearDownClass()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_tests = True
|
|
||||||
|
|
||||||
w = Workspace()
|
|
||||||
w.init()
|
|
||||||
|
|
||||||
for e in w.examples:
|
|
||||||
print "--------------------------------------"
|
|
||||||
print "[%s]"%(e.basename,)
|
|
||||||
if e.build() and run_tests:
|
|
||||||
e.test()
|
|
||||||
for e in w.tests:
|
|
||||||
print "--------------------------------------"
|
|
||||||
print "[%s]"%(e.basename,)
|
|
||||||
if e.build() and run_tests:
|
|
||||||
e.test()
|
|
||||||
|
|
||||||
w.clean()
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
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
|
|
@ -8,7 +8,7 @@ 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
|
||||||
|
|
||||||
|
@ -37,6 +37,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
|
|
@ -38,14 +38,6 @@ long lastMsg = 0;
|
||||||
char msg[50];
|
char msg[50];
|
||||||
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);
|
||||||
|
@ -61,6 +53,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 +74,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 +85,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 +104,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()) {
|
||||||
|
@ -118,7 +124,7 @@ void loop() {
|
||||||
if (now - lastMsg > 2000) {
|
if (now - lastMsg > 2000) {
|
||||||
lastMsg = now;
|
lastMsg = now;
|
||||||
++value;
|
++value;
|
||||||
snprintf (msg, 75, "hello world #%ld", value);
|
snprintf (msg, 50, "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);
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -6,7 +6,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/knolleary/pubsubclient.git"
|
"url": "https://github.com/knolleary/pubsubclient.git"
|
||||||
},
|
},
|
||||||
"version": "2.6",
|
"version": "2.7",
|
||||||
"exclude": "tests",
|
"exclude": "tests",
|
||||||
"examples": "examples/*/*.ino",
|
"examples": "examples/*/*.ino",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
|
@ -1,5 +1,5 @@
|
||||||
name=PubSubClient
|
name=PubSubClient
|
||||||
version=2.6
|
version=2.7
|
||||||
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.
|
|
@ -102,30 +102,41 @@ PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGN
|
||||||
}
|
}
|
||||||
|
|
||||||
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.length() != 0) {
|
if (_client == nullptr) {
|
||||||
result = _client->connect(this->domain.c_str(), this->port);
|
return false;
|
||||||
|
}
|
||||||
|
if (_client->connected()) {
|
||||||
|
result = 1;
|
||||||
} else {
|
} else {
|
||||||
result = _client->connect(this->ip, this->port);
|
if (domain != NULL) {
|
||||||
|
result = _client->connect(this->domain.c_str(), this->port);
|
||||||
|
} else {
|
||||||
|
result = _client->connect(this->ip, this->port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (result == 1) {
|
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
|
||||||
|
@ -141,9 +152,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -158,24 +172,31 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
|
||||||
|
|
||||||
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
|
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
|
||||||
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
|
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
|
||||||
|
|
||||||
|
CHECK_STRING_LENGTH(length,id)
|
||||||
length = writeString(id,buffer,length);
|
length = writeString(id,buffer,length);
|
||||||
if (willTopic) {
|
if (willTopic) {
|
||||||
|
CHECK_STRING_LENGTH(length,willTopic)
|
||||||
length = writeString(willTopic,buffer,length);
|
length = writeString(willTopic,buffer,length);
|
||||||
|
CHECK_STRING_LENGTH(length,willMessage)
|
||||||
length = writeString(willMessage,buffer,length);
|
length = writeString(willMessage,buffer,length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(user != NULL) {
|
if(user != NULL) {
|
||||||
|
CHECK_STRING_LENGTH(length,user)
|
||||||
length = writeString(user,buffer,length);
|
length = writeString(user,buffer,length);
|
||||||
if(pass != NULL) {
|
if(pass != NULL) {
|
||||||
|
CHECK_STRING_LENGTH(length,pass)
|
||||||
length = writeString(pass,buffer,length);
|
length = writeString(pass,buffer,length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
write(MQTTCONNECT,buffer,length-5);
|
write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||||
|
|
||||||
lastInActivity = lastOutActivity = millis();
|
lastInActivity = lastOutActivity = millis();
|
||||||
|
|
||||||
while (!_client->available()) {
|
while (!_client->available()) {
|
||||||
|
delay(0); // Prevent watchdog crashes
|
||||||
unsigned long t = millis();
|
unsigned long t = millis();
|
||||||
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
|
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
|
||||||
_state = MQTT_CONNECTION_TIMEOUT;
|
_state = MQTT_CONNECTION_TIMEOUT;
|
||||||
|
@ -207,9 +228,12 @@ boolean PubSubClient::connect(const char *id, const char *user, const char *pass
|
||||||
|
|
||||||
// reads a byte into result
|
// reads a byte into result
|
||||||
boolean PubSubClient::readByte(uint8_t * result) {
|
boolean PubSubClient::readByte(uint8_t * result) {
|
||||||
|
if (_client == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
uint32_t previousMillis = millis();
|
uint32_t previousMillis = millis();
|
||||||
while(!_client->available()) {
|
while(!_client->available()) {
|
||||||
delay(1); // Add esp8266 de-blocking (Tasmota #790, EspEasy #1943)
|
delay(1); // Prevent watchdog crashes
|
||||||
uint32_t currentMillis = millis();
|
uint32_t currentMillis = millis();
|
||||||
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
|
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
|
||||||
return false;
|
return false;
|
||||||
|
@ -241,7 +265,7 @@ uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
|
||||||
uint8_t start = 0;
|
uint8_t start = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (len == 6) {
|
if (len == 5) {
|
||||||
// Invalid remaining length encoding - kill the connection
|
// Invalid remaining length encoding - kill the connection
|
||||||
_state = MQTT_DISCONNECTED;
|
_state = MQTT_DISCONNECTED;
|
||||||
_client->stop();
|
_client->stop();
|
||||||
|
@ -353,11 +377,13 @@ 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);
|
size_t plength = (payload != nullptr) ? strlen(payload) : 0;
|
||||||
|
return publish(topic,(const uint8_t*)payload,plength,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);
|
size_t plength = (payload != nullptr) ? strlen(payload) : 0;
|
||||||
|
return publish(topic,(const uint8_t*)payload,plength,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) {
|
||||||
|
@ -366,12 +392,12 @@ 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 (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + 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,buffer,length);
|
||||||
uint16_t i;
|
uint16_t i;
|
||||||
for (i=0;i<plength;i++) {
|
for (i=0;i<plength;i++) {
|
||||||
|
@ -381,11 +407,16 @@ boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigne
|
||||||
if (retained) {
|
if (retained) {
|
||||||
header |= 1;
|
header |= 1;
|
||||||
}
|
}
|
||||||
return write(header,buffer,length-5);
|
return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
|
||||||
|
size_t plength = (payload != nullptr) ? strlen(payload) : 0;
|
||||||
|
return publish_P(topic, (const uint8_t*)payload, plength, 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;
|
||||||
|
@ -428,15 +459,53 @@ boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsig
|
||||||
|
|
||||||
lastOutActivity = millis();
|
lastOutActivity = millis();
|
||||||
|
|
||||||
return rc == tlen + 3 + llen + plength;
|
// Header (1 byte) + llen + identifier (2 bytes) + topic len + payload len
|
||||||
|
const int 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,buffer,length);
|
||||||
|
uint8_t header = MQTTPUBLISH;
|
||||||
|
if (retained) {
|
||||||
|
header |= 1;
|
||||||
|
}
|
||||||
|
size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
|
||||||
|
uint16_t rc = _client->write(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();
|
||||||
|
if (_client == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return _client->write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
|
||||||
|
lastOutActivity = millis();
|
||||||
|
if (_client == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
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;
|
digit = len % 128;
|
||||||
|
@ -450,15 +519,22 @@ 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) {
|
||||||
|
delay(0); // Prevent watchdog crashes
|
||||||
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
||||||
rc = _client->write(writeBuf,bytesToWrite);
|
rc = _client->write(writeBuf,bytesToWrite);
|
||||||
result = (rc == bytesToWrite);
|
result = (rc == bytesToWrite);
|
||||||
|
@ -467,9 +543,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +563,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
|
||||||
}
|
}
|
||||||
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;
|
||||||
|
@ -496,7 +572,7 @@ boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
|
||||||
buffer[length++] = (nextMsgId & 0xFF);
|
buffer[length++] = (nextMsgId & 0xFF);
|
||||||
length = writeString((char*)topic, buffer,length);
|
length = writeString((char*)topic, buffer,length);
|
||||||
buffer[length++] = qos;
|
buffer[length++] = qos;
|
||||||
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -507,7 +583,7 @@ boolean PubSubClient::unsubscribe(const char* topic) {
|
||||||
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;
|
||||||
|
@ -515,7 +591,7 @@ boolean PubSubClient::unsubscribe(const char* topic) {
|
||||||
buffer[length++] = (nextMsgId >> 8);
|
buffer[length++] = (nextMsgId >> 8);
|
||||||
buffer[length++] = (nextMsgId & 0xFF);
|
buffer[length++] = (nextMsgId & 0xFF);
|
||||||
length = writeString(topic, buffer,length);
|
length = writeString(topic, buffer,length);
|
||||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5);
|
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -523,7 +599,7 @@ boolean PubSubClient::unsubscribe(const char* topic) {
|
||||||
void PubSubClient::disconnect() {
|
void PubSubClient::disconnect() {
|
||||||
buffer[0] = MQTTDISCONNECT;
|
buffer[0] = MQTTDISCONNECT;
|
||||||
buffer[1] = 0;
|
buffer[1] = 0;
|
||||||
if (_client != NULL) {
|
if (_client != nullptr) {
|
||||||
_client->write(buffer,2);
|
_client->write(buffer,2);
|
||||||
_client->flush();
|
_client->flush();
|
||||||
_client->stop();
|
_client->stop();
|
||||||
|
@ -558,6 +634,8 @@ boolean PubSubClient::connected() {
|
||||||
_client->flush();
|
_client->flush();
|
||||||
_client->stop();
|
_client->stop();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return this->_state == MQTT_CONNECTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
|
@ -75,6 +75,9 @@
|
||||||
#define MQTTQOS1 (1 << 1)
|
#define MQTTQOS1 (1 << 1)
|
||||||
#define MQTTQOS2 (2 << 1)
|
#define MQTTQOS2 (2 << 1)
|
||||||
|
|
||||||
|
// Maximum size of fixed header and variable length size header
|
||||||
|
#define MQTT_MAX_HEADER_SIZE 5
|
||||||
|
|
||||||
#if defined(ESP8266) || defined(ESP32)
|
#if defined(ESP8266) || defined(ESP32)
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
||||||
|
@ -82,7 +85,9 @@
|
||||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class PubSubClient {
|
#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
|
||||||
|
|
||||||
|
class PubSubClient : public Print {
|
||||||
private:
|
private:
|
||||||
Client* _client;
|
Client* _client;
|
||||||
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
||||||
|
@ -96,6 +101,11 @@ private:
|
||||||
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;
|
||||||
String domain;
|
String domain;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
@ -129,12 +139,31 @@ public:
|
||||||
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);
|
|
@ -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");
|
||||||
|
@ -239,10 +283,12 @@ int test_connect_disconnect_connect() {
|
||||||
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();
|
|
@ -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
|
|
@ -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() {
|
|
@ -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
|
|
@ -160,6 +160,35 @@ 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_receive_oversized_stream_message() {
|
int test_receive_oversized_stream_message() {
|
||||||
IT("drops an oversized message");
|
IT("drops an oversized message");
|
||||||
reset_callback();
|
reset_callback();
|
||||||
|
@ -241,6 +270,7 @@ 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_receive_oversized_stream_message();
|
test_receive_oversized_stream_message();
|
||||||
test_receive_qos1();
|
test_receive_qos1();
|
|
@ -0,0 +1,39 @@
|
||||||
|
import unittest
|
||||||
|
import settings
|
||||||
|
import time
|
||||||
|
import mosquitto
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(mosq, obj, msg):
|
||||||
|
obj.message_queue.append(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class mqtt_basic(unittest.TestCase):
|
||||||
|
|
||||||
|
message_queue = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(self):
|
||||||
|
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
|
||||||
|
self.client.connect(settings.server_ip)
|
||||||
|
self.client.on_message = on_message
|
||||||
|
self.client.subscribe("outTopic", 0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(self):
|
||||||
|
self.client.disconnect()
|
||||||
|
|
||||||
|
def test_one(self):
|
||||||
|
i = 30
|
||||||
|
while len(self.message_queue) == 0 and i > 0:
|
||||||
|
self.client.loop()
|
||||||
|
time.sleep(0.5)
|
||||||
|
i -= 1
|
||||||
|
self.assertTrue(i > 0, "message receive timed-out")
|
||||||
|
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
||||||
|
msg = self.message_queue[0]
|
||||||
|
self.assertEqual(msg.mid, 0, "message id not 0")
|
||||||
|
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
||||||
|
self.assertEqual(msg.payload, "hello world")
|
||||||
|
self.assertEqual(msg.qos, 0, "message qos not 0")
|
||||||
|
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
|
@ -0,0 +1,59 @@
|
||||||
|
import unittest
|
||||||
|
import settings
|
||||||
|
import time
|
||||||
|
import mosquitto
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(mosq, obj, msg):
|
||||||
|
obj.message_queue.append(msg)
|
||||||
|
|
||||||
|
|
||||||
|
class mqtt_publish_in_callback(unittest.TestCase):
|
||||||
|
|
||||||
|
message_queue = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(self):
|
||||||
|
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
|
||||||
|
self.client.connect(settings.server_ip)
|
||||||
|
self.client.on_message = on_message
|
||||||
|
self.client.subscribe("outTopic", 0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(self):
|
||||||
|
self.client.disconnect()
|
||||||
|
|
||||||
|
def test_connect(self):
|
||||||
|
i = 30
|
||||||
|
while len(self.message_queue) == 0 and i > 0:
|
||||||
|
self.client.loop()
|
||||||
|
time.sleep(0.5)
|
||||||
|
i -= 1
|
||||||
|
self.assertTrue(i > 0, "message receive timed-out")
|
||||||
|
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
||||||
|
msg = self.message_queue.pop(0)
|
||||||
|
self.assertEqual(msg.mid, 0, "message id not 0")
|
||||||
|
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
||||||
|
self.assertEqual(msg.payload, "hello world")
|
||||||
|
self.assertEqual(msg.qos, 0, "message qos not 0")
|
||||||
|
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
||||||
|
|
||||||
|
def test_publish(self):
|
||||||
|
self.assertEqual(len(self.message_queue), 0, "message queue not empty")
|
||||||
|
payload = "abcdefghij"
|
||||||
|
self.client.publish("inTopic", payload)
|
||||||
|
|
||||||
|
i = 30
|
||||||
|
while len(self.message_queue) == 0 and i > 0:
|
||||||
|
self.client.loop()
|
||||||
|
time.sleep(0.5)
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
self.assertTrue(i > 0, "message receive timed-out")
|
||||||
|
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
||||||
|
msg = self.message_queue.pop(0)
|
||||||
|
self.assertEqual(msg.mid, 0, "message id not 0")
|
||||||
|
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
||||||
|
self.assertEqual(msg.payload, payload)
|
||||||
|
self.assertEqual(msg.qos, 0, "message qos not 0")
|
||||||
|
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
|
@ -0,0 +1,181 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
from subprocess import call
|
||||||
|
import importlib
|
||||||
|
import unittest
|
||||||
|
import re
|
||||||
|
|
||||||
|
from testcases import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Workspace(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.root_dir = os.getcwd()
|
||||||
|
self.build_dir = os.path.join(self.root_dir, "tmpbin")
|
||||||
|
self.log_dir = os.path.join(self.root_dir, "logs")
|
||||||
|
self.tests_dir = os.path.join(self.root_dir, "testcases")
|
||||||
|
self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples")
|
||||||
|
self.examples = []
|
||||||
|
self.tests = []
|
||||||
|
if not os.path.isdir("../PubSubClient"):
|
||||||
|
raise Exception("Cannot find PubSubClient library")
|
||||||
|
try:
|
||||||
|
return __import__('ino')
|
||||||
|
except ImportError:
|
||||||
|
raise Exception("ino tool not installed")
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if os.path.isdir(self.build_dir):
|
||||||
|
shutil.rmtree(self.build_dir)
|
||||||
|
os.mkdir(self.build_dir)
|
||||||
|
if os.path.isdir(self.log_dir):
|
||||||
|
shutil.rmtree(self.log_dir)
|
||||||
|
os.mkdir(self.log_dir)
|
||||||
|
|
||||||
|
os.chdir(self.build_dir)
|
||||||
|
call(["ino", "init"])
|
||||||
|
|
||||||
|
shutil.copytree("../../PubSubClient", "lib/PubSubClient")
|
||||||
|
|
||||||
|
filenames = []
|
||||||
|
for root, dirs, files in os.walk(self.examples_dir):
|
||||||
|
filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
|
||||||
|
filenames.sort()
|
||||||
|
for e in filenames:
|
||||||
|
self.examples.append(Sketch(self, e))
|
||||||
|
|
||||||
|
filenames = []
|
||||||
|
for root, dirs, files in os.walk(self.tests_dir):
|
||||||
|
filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
|
||||||
|
filenames.sort()
|
||||||
|
for e in filenames:
|
||||||
|
self.tests.append(Sketch(self, e))
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
shutil.rmtree(self.build_dir)
|
||||||
|
|
||||||
|
|
||||||
|
class Sketch(object):
|
||||||
|
def __init__(self, wksp, fn):
|
||||||
|
self.w = wksp
|
||||||
|
self.filename = fn
|
||||||
|
self.basename = os.path.basename(self.filename)
|
||||||
|
self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),))
|
||||||
|
self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),))
|
||||||
|
self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),))
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
sys.stdout.write(" Build: ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
# Copy sketch over, replacing IP addresses as necessary
|
||||||
|
fin = open(self.filename, "r")
|
||||||
|
lines = fin.readlines()
|
||||||
|
fin.close()
|
||||||
|
fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w")
|
||||||
|
for l in lines:
|
||||||
|
if re.match(r"^byte server\[\] = {", l):
|
||||||
|
fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),))
|
||||||
|
elif re.match(r"^byte ip\[\] = {", l):
|
||||||
|
fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),))
|
||||||
|
else:
|
||||||
|
fout.write(l)
|
||||||
|
fout.flush()
|
||||||
|
fout.close()
|
||||||
|
|
||||||
|
# Run build
|
||||||
|
fout = open(self.build_log, "w")
|
||||||
|
ferr = open(self.build_err_log, "w")
|
||||||
|
rc = call(["ino", "build"], stdout=fout, stderr=ferr)
|
||||||
|
fout.close()
|
||||||
|
ferr.close()
|
||||||
|
if rc == 0:
|
||||||
|
sys.stdout.write("pass")
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
sys.stdout.write("fail")
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
with open(self.build_err_log) as f:
|
||||||
|
for line in f:
|
||||||
|
print(" " + line)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def upload(self):
|
||||||
|
sys.stdout.write(" Upload: ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
fout = open(self.build_upload_log, "w")
|
||||||
|
rc = call(["ino", "upload"], stdout=fout, stderr=fout)
|
||||||
|
fout.close()
|
||||||
|
if rc == 0:
|
||||||
|
sys.stdout.write("pass")
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
sys.stdout.write("fail")
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
with open(self.build_upload_log) as f:
|
||||||
|
for line in f:
|
||||||
|
print(" " + line)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
# import the matching test case, if it exists
|
||||||
|
try:
|
||||||
|
basename = os.path.basename(self.filename)[:-4]
|
||||||
|
i = importlib.import_module("testcases." + basename)
|
||||||
|
except:
|
||||||
|
sys.stdout.write(" Test: no tests found")
|
||||||
|
sys.stdout.write("\n")
|
||||||
|
return
|
||||||
|
c = getattr(i, basename)
|
||||||
|
|
||||||
|
testmethods = [m for m in dir(c) if m.startswith("test_")]
|
||||||
|
testmethods.sort()
|
||||||
|
tests = []
|
||||||
|
for m in testmethods:
|
||||||
|
tests.append(c(m))
|
||||||
|
|
||||||
|
result = unittest.TestResult()
|
||||||
|
c.setUpClass()
|
||||||
|
if self.upload():
|
||||||
|
sys.stdout.write(" Test: ")
|
||||||
|
sys.stdout.flush()
|
||||||
|
for t in tests:
|
||||||
|
t.run(result)
|
||||||
|
print(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()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
run_tests = True
|
||||||
|
|
||||||
|
w = Workspace()
|
||||||
|
w.init()
|
||||||
|
|
||||||
|
for e in w.examples:
|
||||||
|
print("--------------------------------------")
|
||||||
|
print("[" + e.basename + "]")
|
||||||
|
if e.build() and run_tests:
|
||||||
|
e.test()
|
||||||
|
for e in w.tests:
|
||||||
|
print("--------------------------------------")
|
||||||
|
print("[" + e.basename + "]")
|
||||||
|
if e.build() and run_tests:
|
||||||
|
e.test()
|
||||||
|
|
||||||
|
w.clean()
|
Loading…
Reference in New Issue