2 |
3 | This project is an Android Application developed using App Inventor.
4 |
5 | App Inventor is accessible online at: http://ai2.appinventor.mit.edu/
6 |
7 | This example is provided as method to interface with an MQTT broker.
8 |
9 | How it works:
10 |
11 | The App Inventor communicates with an html/JavaScript webpage using
12 | the WebViewString. WebViewString is visible to both App Inventor and JavaScript.
13 |
14 | The MQTT parameters are hard-coded into the html file mqtt_appinventor.html.
15 |
16 | var mqtturl = "ws://test.mosquitto.org:8080/mqtt";
17 | var txtopic = "MyMqttSvrRqst";
18 | var rxtopic = "MyMqttSvrRply";
19 |
20 | These values will work only with the project presented here. They will need to be
21 | modified as necessary to meet your own system requirements
22 |
23 |
Setup:
24 |
25 | 1. Connect your Android device to a PC via USB.
26 | 2. From the PC, copy the file mqtt_appinventor.html to your Android SD card at the following location:
27 | (ANDROID NAME ON PC)\Card\mqtt\mqtt_appinventor.html
28 | You can also use an Android File Manager App to make sure the file is in the proper location.
29 | I used the App ES File Explorer. With that App, the file should be placed at:
31 | The folder "mqtt" will need to be created.
32 | 3. Install the application "AppInventor_MQTT.apk" on the Android device.
33 |
34 | NOTE: If you want to build your own Android Application using the App Inventor web interface,
35 | import the project file AppInventor_MQTT.aia.
36 |
37 | This App assumes an ESP8266 device is running the sketch found at:
38 |
39 | https://github.com/internetofhomethings/Configurable-Web-Server
40 |
41 | For more information, check out the post at http://wp.me/p5NRQ8-lf.
42 |
--------------------------------------------------------------------------------
/AppInventor/mqtt_appinventor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MQTT JavaScript Client Example
5 |
6 |
7 |
8 |
9 |
2 |
3 | This project is an Android Application developoed using App Inventor.
4 | It is provided as an example method to interface with an MQTT broker.
5 |
6 | How it works:
7 |
8 | The App Inventor communicates with an html/JavaScript webpage using
9 | the WebViewString. WebViewString is visible to both App Inventor and JavaScript.
10 |
11 | The MQTT parameters are now configurable using this App Inventor App. The initial
12 | default values are:
13 |
14 | broker: "ws://test.mosquitto.org:8080/mqtt";
15 | request topic: "MyMqttSvrRqst";
16 | reply topic: "MyMqttSvrRply";
17 |
18 | These values will work only with the project presented here. They will need to be
19 | modified as necessary to meet your own system requirements
20 |
21 |
Setup:
22 |
23 | 1. Connect your Android device to a PC via USB.
24 | 2. From the PC, copy the file mqtt_appinventor.html to your Android SD card at the following location:
25 | \Phone\mqtt\mqtt_appinventor.html
26 | 3. Install the application "AppInventor_MQTT_CFG.apk" on the Android device.
27 |
28 | NOTE: If you want to build your own Android Application using the App Inventor web interface,
29 | import the project file AppInventor_MQTT_CFG.aia.
30 |
31 | This App assumes an ESP8266 device is running with the sketch found at
32 |
33 | https://github.com/internetofhomethings/Configurable-Web-Server
34 |
35 | installed.
36 |
--------------------------------------------------------------------------------
/AppInventor_CfgMQTT/cfg.txt:
--------------------------------------------------------------------------------
1 | {
2 | "mqtt_broker":"ws://test.mosquitto.org:8080/mqtt",
3 | "mqtt_txtopic":"MyMqttSvrRqst",
4 | "mqtt_rxtopic":"MyMqttSvrRply"
5 | }
6 |
--------------------------------------------------------------------------------
/AppInventor_CfgMQTT/mqtt_appinventor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MQTT JavaScript Client Example
5 |
6 |
7 |
8 |
9 |
2 |
3 | This project is an Android Application developoed using App Inventor.
4 | It is provided as an example method to interface with an MQTT broker.
5 |
6 | How it works:
7 |
8 | The App Inventor communicates with an html/JavaScript webpage using
9 | the WebViewString. WebViewString is visible to both App Inventor and JavaScript.
10 |
11 | The MQTT parameters are now configurable using this App Inventor App. The initial
12 | default values are:
13 |
14 | broker: "broker.mqttdashboard.com";
15 | request topic: "mqtt_request";
16 | reply topic: "mqtt_reply";
17 | user name: "mosquitto";
18 | password: "mosquitto";
19 | reply topic: "mqtt_reply";
20 | connect port: "8000";
21 | connect clientID: "clientID";
22 | keepalive timeout: "60";
23 | last will topic: "closing down....";
24 | last will message: "lwt";
25 | last will QoS: "0";
26 | TLS connect enable: "true";
27 |
28 | These values will only work with the project presented here. This example broker will only
29 | support non-TLS connections. You will need to use a broker that supports secure TLS websockets
30 | to utilize that feature. These values will need to be modified as necessary to meet your
31 | own system requirements.
32 |
33 |
Setup:
34 |
35 | 1. Connect your Android device to a PC via USB.
36 | 2. From the PC, copy the following files to your Android SD card at the following location:
2 |
3 | This project is an Android Application developoed using App Inventor.
4 | It is provided as an example method to interface with an MQTT broker.
5 |
6 | How it works:
7 |
8 | The App Inventor communicates with an html/JavaScript webpage using
9 | the WebViewString. WebViewString is visible to both App Inventor and JavaScript.
10 |
11 | The MQTT parameters are now configurable using this App Inventor App. The initial
12 | default values are:
13 |
14 | broker: "broker.mqttdashboard.com";
15 | request topic: "mqtt_request";
16 | reply topic: "mqtt_reply";
17 | user name: "mosquitto";
18 | password: "mosquitto";
19 | reply topic: "mqtt_reply";
20 | connect port: "8000";
21 | connect clientID: "clientID";
22 | keepalive timeout:"60";
23 | last will topic: "closing down....";
24 | last will message:"lwt";
25 | last will QoS: "0";
26 |
27 | These values will only work with the project presented here. They will need to be
28 | modified as necessary to meet your own system requirements
29 |
30 |
Setup:
31 |
32 | 1. Connect your Android device to a PC via USB.
33 | 2. From the PC, copy the following files to your Android SD card at the following location:
37 | 3. Install the application "AppInventor_MQTT_CFG_rev1.apk" on the Android device.
38 |
39 | NOTE: If you want to build your own Android Application using the App Inventor web interface,
40 | import the project file AppInventor_MQTT_CFG_rev1.aia.
41 |
42 | This App assumes an ESP8266 device is running with the sketch found at
43 |
44 | https://github.com/internetofhomethings/Configurable-Web-Server
45 |
46 | installed.
47 |
--------------------------------------------------------------------------------
/AppInventor_CfgMQTT_rev1/cfg.txt:
--------------------------------------------------------------------------------
1 | {
2 | "mqtt_broker":"broker.mqttdashboard.com",
3 | "mqtt_txtopic":"mqtt_request",
4 | "mqtt_rxtopic":"mqtt_reply",
5 | "mqtt_un":"mosquitto",
6 | "mqtt_pw":"mosquitto",
7 | "mqtt_port":"8000",
8 | "mqtt_clientId":"clientID",
9 | "mqtt_keepalive":"60",
10 | "mqtt_lastWillTopic":"lwt",
11 | "mqtt_lastWillMessage":"closing down....",
12 | "mqtt_lastWillQoS":"0"
13 | }
14 |
--------------------------------------------------------------------------------
/AppInventor_CfgMQTT_rev1/mqtt_appinventor_paho.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MQTT JavaScript Client Example
5 |
6 |
7 |
8 |
ESP8266 MQTT Server
9 |
10 |
11 | Request:
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
209 |
210 |
211 |
--------------------------------------------------------------------------------
/ArduinoHomeAutomation/ArduinoHomeAutomation.ino:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #define SERBAUD 115200
5 | #define ESPSERBAUD 1200
6 |
7 | /*
8 | The circuit:
9 | * RX is digital pin 10 (connect to TX of other device)
10 | * TX is digital pin 11 (connect to RX of other device)
11 | */
12 |
13 | SoftwareSerial toEspSerial(10, 11); // RX, TX
14 | String fromEsp8266 = "";
15 | String toEsp8266 = "";
16 | String periodicMsg="";
17 | long lastMsg = 0;
18 |
19 | // Process Requests from ESP8266
20 | void ReadAndProcessRequests() {
21 | int chan,state,button;
22 | float Ain;
23 | char szAin[20],szT[200];
24 | button = digitalRead(6);
25 | if(button==0) {
26 | toEspSerial.println("Button has been Pressed");
27 | }
28 |
29 | while(toEspSerial.available()) {
30 | char inChar = (char)toEspSerial.read(); // get the new byte
31 | if (inChar != '\n') {
32 | fromEsp8266 += inChar; // add it to receive string
33 | }
34 | else { // process string if end of line
35 | if(fromEsp8266.substring(0,8)=="Arduino_") { // Valid command from ESP8266?
36 | if(fromEsp8266.substring(8,10) == "SM") { // Set Digital if true
37 | // --- Build Reply String -----------------------------------------------
38 | toEsp8266 = "Echoing your message: ";
39 | toEsp8266 += fromEsp8266.substring(10,50);
40 | // ---- Set Periodic Message String -------------------------------------
41 | url_decode((char *)toEsp8266.c_str(), (char *)szT );
42 | periodicMsg = szT;
43 | }
44 | else if(fromEsp8266.substring(8,10) == "SD") { // Set Digital if true
45 | // --- Build Reply String -----------------------------------------------
46 | toEsp8266 = "Digital Channel ";
47 | toEsp8266 += fromEsp8266.substring(10,12); //Digital Channel
48 | toEsp8266 += " is ";
49 | toEsp8266 += (fromEsp8266.substring(12,13)=="0") ? "LO" : "HI";
50 | // ---- Send Reply String -----------------------------------------------
51 | toEspSerial.println(toEsp8266); // Send Reply String to ESP8266
52 | Serial.println(toEsp8266); // Send Reply String to Console
53 | // --- Set Digital Channel State ----------------------------------------
54 | chan = atoi(fromEsp8266.substring(10,12).c_str());
55 | state = atoi(fromEsp8266.substring(12,13).c_str());
56 | digitalWrite(chan, state); // Set Digital Output per request
57 | }
58 | else if(fromEsp8266.substring(8,10) == "GD") { // Get Digital if true
59 | // --- Get Digital Channel State ----------------------------------------
60 | chan = atoi(fromEsp8266.substring(10,12).c_str());
61 | state = digitalRead(chan); // Set Digital Output per request
62 | // --- Build Reply String -----------------------------------------------
63 | toEsp8266 = "Digital Channel ";
64 | toEsp8266 += fromEsp8266.substring(10,12); //Digital Channel
65 | toEsp8266 += " is ";
66 | toEsp8266 += (state==0) ? "LO" : "HI";
67 | // ---- Send Reply String -----------------------------------------------
68 | toEspSerial.println(toEsp8266); // Send Reply String to ESP8266
69 | Serial.println(toEsp8266); // Send Reply String to Console
70 | }
71 | else if(fromEsp8266.substring(8,10) == "GA") { // Get Analog if true
72 | // --- Get Analog Channel Reading ---------------------------------------
73 | chan = atoi(fromEsp8266.substring(10,12).c_str());
74 | Ain = 0.0048828 * (float) analogRead(chan); // Read analog input
75 | ftoa(Ain,szAin, 2);
76 | // --- Build Reply String -----------------------------------------------
77 | toEsp8266 = "Analog Channel ";
78 | toEsp8266 += fromEsp8266.substring(10,12); //Analog Channel
79 | toEsp8266 += " is ";
80 | toEsp8266 += String(szAin);
81 | // ---- Send Reply String -----------------------------------------------
82 | toEspSerial.println(toEsp8266); // Send Reply String to ESP8266
83 | Serial.println(toEsp8266); // Send Reply String to Console
84 | }
85 | else {
86 | // ---- Send Reply String -----------------------------------------------
87 | toEsp8266 = "Arduino does not recognize request.";
88 | toEspSerial.println(toEsp8266); // Send Reply String to ESP8266
89 | Serial.println(toEsp8266); // Send Reply String to Console
90 | }
91 | }
92 | fromEsp8266 = "";
93 | toEspSerial.flush();
94 | }
95 | }
96 | }
97 |
98 | /********************************************************
99 | * URL Message Decoder
100 | ********************************************************/
101 |
102 | int url_decode(char *encoded_str, char *decoded_str) {
103 |
104 | // While we're not at the end of the string (current character not NULL)
105 | while (*encoded_str) {
106 | // Check to see if the current character is a %
107 | if (*encoded_str == '%') {
108 |
109 | // Grab the next two characters and move encoded_str forwards
110 | encoded_str++;
111 | char high = *encoded_str;
112 | encoded_str++;
113 | char low = *encoded_str;
114 |
115 | // Convert ASCII 0-9A-F to a value 0-15
116 | if (high > 0x39) high -= 7;
117 | high &= 0x0f;
118 |
119 | // Same again for the low byte:
120 | if (low > 0x39) low -= 7;
121 | low &= 0x0f;
122 |
123 | // Combine the two into a single byte and store in decoded_str:
124 | *decoded_str = (high << 4) | low;
125 | } else {
126 | // All other characters copy verbatim
127 | *decoded_str = *encoded_str;
128 | }
129 |
130 | // Move both pointers to the next character:
131 | encoded_str++;
132 | decoded_str++;
133 | }
134 | // Terminate the new string with a NULL character to trim it off
135 | *decoded_str = 0;
136 | }
137 | /*****************************************************
138 | * Send Message Every 2 seconds until Message = stop *
139 | *****************************************************/
140 | void PeriodicMessageEcho() {
141 | String dc;
142 | if(periodicMsg!="") {
143 | //Stop sending when Msg="stop"
144 | if(periodicMsg.substring(22,26) == "stop" ) {
145 | toEspSerial.println("Msg echo terminated.");
146 | periodicMsg = "";
147 | }
148 | // Send Message every 2 seconds
149 | else {
150 | long now = millis();
151 | if (now - lastMsg > 2000) {
152 | toEspSerial.println(periodicMsg);
153 | lastMsg = now;
154 | }
155 | }
156 | }
157 | }
158 |
159 |
160 | void setup() {
161 | Serial.begin(SERBAUD); // Initialize serial port
162 | toEspSerial.begin(ESPSERBAUD);
163 | pinMode(4 , OUTPUT); // Set Indicator LED as output
164 |
165 | }
166 |
167 | void loop() {
168 | ReadAndProcessRequests();
169 | PeriodicMessageEcho();
170 | }
171 |
172 |
173 |
--------------------------------------------------------------------------------
/PubSubClient/CHANGES.txt:
--------------------------------------------------------------------------------
1 | 2.3
2 | * Add publish(topic,payload,retained) function
3 |
4 | 2.2
5 | * Change code layout to match Arduino Library reqs
6 |
7 | 2.1
8 | * Add MAX_TRANSFER_SIZE def to chunk messages if needed
9 | * Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
10 |
11 | 2.0
12 | * Add (and default to) MQTT 3.1.1 support
13 | * Fix PROGMEM handling for Intel Galileo/ESP8266
14 | * Add overloaded constructors for convenience
15 | * Add chainable setters for server/callback/client/stream
16 | * Add state function to return connack return code
17 |
18 | 1.9
19 | * Do not split MQTT packets over multiple calls to _client->write()
20 | * API change: All constructors now require an instance of Client
21 | to be passed in.
22 | * Fixed example to match 1.8 api changes - dpslwk
23 | * Added username/password support - WilHall
24 | * Added publish_P - publishes messages from PROGMEM - jobytaffey
25 |
26 | 1.8
27 | * KeepAlive interval is configurable in PubSubClient.h
28 | * Maximum packet size is configurable in PubSubClient.h
29 | * API change: Return boolean rather than int from various functions
30 | * API change: Length parameter in message callback changed
31 | from int to unsigned int
32 | * Various internal tidy-ups around types
33 | 1.7
34 | * Improved keepalive handling
35 | * Updated to the Arduino-1.0 API
36 | 1.6
37 | * Added the ability to publish a retained message
38 |
39 | 1.5
40 | * Added default constructor
41 | * Fixed compile error when used with arduino-0021 or later
42 |
43 | 1.4
44 | * Fixed connection lost handling
45 |
46 | 1.3
47 | * Fixed packet reading bug in PubSubClient.readPacket
48 |
49 | 1.2
50 | * Fixed compile error when used with arduino-0016 or later
51 |
52 |
53 | 1.1
54 | * Reduced size of library
55 | * Added support for Will messages
56 | * Clarified licensing - see LICENSE.txt
57 |
58 |
59 | 1.0
60 | * Only Quality of Service (QOS) 0 messaging is supported
61 | * The maximum message size, including header, is 128 bytes
62 | * The keepalive interval is set to 30 seconds
63 | * No support for Will messages
64 |
--------------------------------------------------------------------------------
/PubSubClient/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008-2015 Nicholas O'Leary
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/PubSubClient/README.md:
--------------------------------------------------------------------------------
1 | # Arduino Client for MQTT
2 |
3 | This library provides a client for doing simple publish/subscribe messaging with
4 | a server that supports MQTT.
5 |
6 | ## Examples
7 |
8 | The library comes with a number of example sketches. See File > Examples > PubSubClient
9 | within the Arduino application.
10 |
11 | Full API documentation is available here: http://pubsubclient.knolleary.net
12 |
13 | ## Limitations
14 |
15 | - It can only publish QoS 0 messages. It can subscribe at QoS 0 or QoS 1.
16 | - The maximum message size, including header, is **128 bytes** by default. This
17 | is configurable via `MQTT_MAX_PACKET_SIZE` in `PubSubClient.h`.
18 | - The keepalive interval is set to 15 seconds by default. This is configurable
19 | via `MQTT_KEEPALIVE` in `PubSubClient.h`.
20 | - The client uses MQTT 3.1.1 by default. It can be changed to use MQTT 3.1 by
21 | changing value of `MQTT_VERSION` in `PubSubClient.h`.
22 |
23 |
24 | ## Compatible Hardware
25 |
26 | The library uses the Arduino Ethernet Client api for interacting with the
27 | underlying network hardware. This means it Just Works with a growing number of
28 | boards and shields, including:
29 |
30 | - Arduino Ethernet
31 | - Arduino Ethernet Shield
32 | - Arduino YUN – use the included `YunClient` in place of `EthernetClient`, and
33 | be sure to do a `Bridge.begin()` first
34 | - Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield,
35 | enable the `MQTT_MAX_TRANSFER_SIZE` define in `PubSubClient.h`.
36 | - Sparkfun WiFly Shield – [library](https://github.com/dpslwk/WiFly)
37 | - TI CC3000 WiFi - [library](https://github.com/sparkfun/SFE_CC3000_Library)
38 | - Intel Galileo/Edison
39 | - ESP8266
40 |
41 | The library cannot currently be used with hardware based on the ENC28J60 chip –
42 | such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an
43 | [alternative library](https://github.com/njh/NanodeMQTT) available.
44 |
45 | ## License
46 |
47 | This code is released under the MIT License.
48 |
--------------------------------------------------------------------------------
/PubSubClient/examples/mqtt_auth/mqtt_auth.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Basic MQTT example with Authentication
3 |
4 | - connects to an MQTT server, providing username
5 | and password
6 | - publishes "hello world" to the topic "outTopic"
7 | - subscribes to the topic "inTopic"
8 | */
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | // Update these with values suitable for your network.
15 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
16 | IPAddress ip(172, 16, 0, 100);
17 | IPAddress server(172, 16, 0, 2);
18 |
19 | void callback(char* topic, byte* payload, unsigned int length) {
20 | // handle message arrived
21 | }
22 |
23 | EthernetClient ethClient;
24 | PubSubClient client(server, 1883, callback, ethClient);
25 |
26 | void setup()
27 | {
28 | Ethernet.begin(mac, ip);
29 | // Note - the default maximum packet size is 128 bytes. If the
30 | // combined length of clientId, username and password exceed this,
31 | // you will need to increase the value of MQTT_MAX_PACKET_SIZE in
32 | // PubSubClient.h
33 |
34 | if (client.connect("arduinoClient", "testuser", "testpass")) {
35 | client.publish("outTopic","hello world");
36 | client.subscribe("inTopic");
37 | }
38 | }
39 |
40 | void loop()
41 | {
42 | client.loop();
43 | }
44 |
--------------------------------------------------------------------------------
/PubSubClient/examples/mqtt_basic/mqtt_basic.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Basic MQTT example
3 |
4 | This sketch demonstrates the basic capabilities of the library.
5 | It connects to an MQTT server then:
6 | - publishes "hello world" to the topic "outTopic"
7 | - subscribes to the topic "inTopic", printing out any messages
8 | it receives. NB - it assumes the received payloads are strings not binary
9 |
10 | It will reconnect to the server if the connection is lost using a blocking
11 | reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
12 | achieve the same result without blocking the main loop.
13 |
14 | */
15 |
16 | #include
17 | #include
18 | #include
19 |
20 | // Update these with values suitable for your network.
21 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
22 | IPAddress ip(172, 16, 0, 100);
23 | IPAddress server(172, 16, 0, 2);
24 |
25 | void callback(char* topic, byte* payload, unsigned int length) {
26 | Serial.print("Message arrived [");
27 | Serial.print(topic);
28 | Serial.print("] ");
29 | for (int i=0;i Preferences -> Additional Boards Manager URLs":
20 | http://arduino.esp8266.com/stable/package_esp8266com_index.json
21 | - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
22 | - Select your ESP8266 in "Tools -> Board"
23 |
24 | */
25 |
26 | #include
27 | #include
28 |
29 | // Update these with values suitable for your network.
30 |
31 | const char* ssid = "........";
32 | const char* password = "........";
33 | const char* mqtt_server = "broker.mqtt-dashboard.com";
34 |
35 | WiFiClient espClient;
36 | PubSubClient client(espClient);
37 | long lastMsg = 0;
38 | char msg[50];
39 | int value = 0;
40 |
41 | void setup() {
42 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
43 | Serial.begin(115200);
44 | setup_wifi();
45 | client.setServer(mqtt_server, 1883);
46 | client.setCallback(callback);
47 | }
48 |
49 | void setup_wifi() {
50 |
51 | delay(10);
52 | // We start by connecting to a WiFi network
53 | Serial.println();
54 | Serial.print("Connecting to ");
55 | Serial.println(ssid);
56 |
57 | WiFi.begin(ssid, password);
58 |
59 | while (WiFi.status() != WL_CONNECTED) {
60 | delay(500);
61 | Serial.print(".");
62 | }
63 |
64 | Serial.println("");
65 | Serial.println("WiFi connected");
66 | Serial.println("IP address: ");
67 | Serial.println(WiFi.localIP());
68 | }
69 |
70 | void callback(char* topic, byte* payload, unsigned int length) {
71 | Serial.print("Message arrived [");
72 | Serial.print(topic);
73 | Serial.print("] ");
74 | for (int i = 0; i < length; i++) {
75 | Serial.print((char)payload[i]);
76 | }
77 | Serial.println();
78 |
79 | // Switch on the LED if an 1 was received as first character
80 | if ((char)payload[0] == '1') {
81 | digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
82 | // but actually the LED is on; this is because
83 | // it is acive low on the ESP-01)
84 | } else {
85 | digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
86 | }
87 |
88 | }
89 |
90 | void reconnect() {
91 | // Loop until we're reconnected
92 | while (!client.connected()) {
93 | Serial.print("Attempting MQTT connection...");
94 | // Attempt to connect
95 | if (client.connect("ESP8266Client")) {
96 | Serial.println("connected");
97 | // Once connected, publish an announcement...
98 | client.publish("outTopic", "hello world");
99 | // ... and resubscribe
100 | client.subscribe("inTopic");
101 | } else {
102 | Serial.print("failed, rc=");
103 | Serial.print(client.state());
104 | Serial.println(" try again in 5 seconds");
105 | // Wait 5 seconds before retrying
106 | delay(5000);
107 | }
108 | }
109 | }
110 | void loop() {
111 |
112 | if (!client.connected()) {
113 | reconnect();
114 | }
115 | client.loop();
116 |
117 | long now = millis();
118 | if (now - lastMsg > 2000) {
119 | lastMsg = now;
120 | ++value;
121 | snprintf (msg, 75, "hello world #%ld", value);
122 | Serial.print("Publish message: ");
123 | Serial.println(msg);
124 | client.publish("outTopic", msg);
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/PubSubClient/examples/mqtt_publish_in_callback/mqtt_publish_in_callback.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Publishing in the callback
3 |
4 | - connects to an MQTT server
5 | - subscribes to the topic "inTopic"
6 | - when a message is received, republishes it to "outTopic"
7 |
8 | This example shows how to publish messages within the
9 | callback function. The callback function header needs to
10 | be declared before the PubSubClient constructor and the
11 | actual callback defined afterwards.
12 | This ensures the client reference in the callback function
13 | is valid.
14 |
15 | */
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | // Update these with values suitable for your network.
22 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
23 | IPAddress ip(172, 16, 0, 100);
24 | IPAddress server(172, 16, 0, 2);
25 |
26 | // Callback function header
27 | void callback(char* topic, byte* payload, unsigned int length);
28 |
29 | EthernetClient ethClient;
30 | PubSubClient client(server, 1883, callback, ethClient);
31 |
32 | // Callback function
33 | void callback(char* topic, byte* payload, unsigned int length) {
34 | // In order to republish this payload, a copy must be made
35 | // as the orignal payload buffer will be overwritten whilst
36 | // constructing the PUBLISH packet.
37 |
38 | // Allocate the correct amount of memory for the payload copy
39 | byte* p = (byte*)malloc(length);
40 | // Copy the payload to the new buffer
41 | memcpy(p,payload,length);
42 | client.publish("outTopic", p, length);
43 | // Free the memory
44 | free(p);
45 | }
46 |
47 | void setup()
48 | {
49 |
50 | Ethernet.begin(mac, ip);
51 | if (client.connect("arduinoClient")) {
52 | client.publish("outTopic","hello world");
53 | client.subscribe("inTopic");
54 | }
55 | }
56 |
57 | void loop()
58 | {
59 | client.loop();
60 | }
61 |
--------------------------------------------------------------------------------
/PubSubClient/examples/mqtt_reconnect_nonblocking/mqtt_reconnect_nonblocking.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Reconnecting MQTT example - non-blocking
3 |
4 | This sketch demonstrates how to keep the client connected
5 | using a non-blocking reconnect function. If the client loses
6 | its connection, it attempts to reconnect every 5 seconds
7 | without blocking the main loop.
8 |
9 | */
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | // Update these with values suitable for your hardware/network.
16 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
17 | IPAddress ip(172, 16, 0, 100);
18 | IPAddress server(172, 16, 0, 2);
19 |
20 | void callback(char* topic, byte* payload, unsigned int length) {
21 | // handle message arrived
22 | }
23 |
24 | EthernetClient ethClient;
25 | PubSubClient client(ethClient);
26 |
27 | long lastReconnectAttempt = 0;
28 |
29 | boolean reconnect() {
30 | if (client.connect("arduinoClient")) {
31 | // Once connected, publish an announcement...
32 | client.publish("outTopic","hello world");
33 | // ... and resubscribe
34 | client.subscribe("inTopic");
35 | }
36 | return client.connected();
37 | }
38 |
39 | void setup()
40 | {
41 | client.setServer(server, 1883);
42 | client.setCallback(callback);
43 |
44 | Ethernet.begin(mac, ip);
45 | delay(1500);
46 | lastReconnectAttempt = 0;
47 | }
48 |
49 |
50 | void loop()
51 | {
52 | if (!client.connected()) {
53 | long now = millis();
54 | if (now - lastReconnectAttempt > 5000) {
55 | lastReconnectAttempt = now;
56 | // Attempt to reconnect
57 | if (reconnect()) {
58 | lastReconnectAttempt = 0;
59 | }
60 | }
61 | } else {
62 | // Client connected
63 |
64 | client.loop();
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/PubSubClient/examples/mqtt_stream/mqtt_stream.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Example of using a Stream object to store the message payload
3 |
4 | Uses SRAM library: https://github.com/ennui2342/arduino-sram
5 | but could use any Stream based class such as SD
6 |
7 | - connects to an MQTT server
8 | - publishes "hello world" to the topic "outTopic"
9 | - subscribes to the topic "inTopic"
10 | */
11 |
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | // Update these with values suitable for your network.
18 | byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
19 | IPAddress ip(172, 16, 0, 100);
20 | IPAddress server(172, 16, 0, 2);
21 |
22 | SRAM sram(4, SRAM_1024);
23 |
24 | void callback(char* topic, byte* payload, unsigned int length) {
25 | sram.seek(1);
26 |
27 | // do something with the message
28 | for(uint8_t i=0; i
4 | maintainer=Nick O'Leary
5 | sentence=A client library for MQTT messaging.
6 | paragraph=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.
7 | category=Communication
8 | url=http://pubsubclient.knolleary.net
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/PubSubClient/src/PubSubClient.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | PubSubClient.cpp - A simple client for MQTT.
3 | Nick O'Leary
4 | http://knolleary.net
5 | */
6 |
7 | #include "PubSubClient.h"
8 | #include "Arduino.h"
9 |
10 | PubSubClient::PubSubClient() {
11 | this->_state = MQTT_DISCONNECTED;
12 | this->_client = NULL;
13 | this->stream = NULL;
14 | setCallback(NULL);
15 | }
16 |
17 | PubSubClient::PubSubClient(Client& client) {
18 | this->_state = MQTT_DISCONNECTED;
19 | setClient(client);
20 | this->stream = NULL;
21 | }
22 |
23 | PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
24 | this->_state = MQTT_DISCONNECTED;
25 | setServer(addr, port);
26 | setClient(client);
27 | this->stream = NULL;
28 | }
29 | PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
30 | this->_state = MQTT_DISCONNECTED;
31 | setServer(addr,port);
32 | setClient(client);
33 | setStream(stream);
34 | }
35 | PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
36 | this->_state = MQTT_DISCONNECTED;
37 | setServer(addr, port);
38 | setCallback(callback);
39 | setClient(client);
40 | this->stream = NULL;
41 | }
42 | PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
43 | this->_state = MQTT_DISCONNECTED;
44 | setServer(addr,port);
45 | setCallback(callback);
46 | setClient(client);
47 | setStream(stream);
48 | }
49 |
50 | PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
51 | this->_state = MQTT_DISCONNECTED;
52 | setServer(ip, port);
53 | setClient(client);
54 | this->stream = NULL;
55 | }
56 | PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
57 | this->_state = MQTT_DISCONNECTED;
58 | setServer(ip,port);
59 | setClient(client);
60 | setStream(stream);
61 | }
62 | PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
63 | this->_state = MQTT_DISCONNECTED;
64 | setServer(ip, port);
65 | setCallback(callback);
66 | setClient(client);
67 | this->stream = NULL;
68 | }
69 | PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
70 | this->_state = MQTT_DISCONNECTED;
71 | setServer(ip,port);
72 | setCallback(callback);
73 | setClient(client);
74 | setStream(stream);
75 | }
76 |
77 | PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
78 | this->_state = MQTT_DISCONNECTED;
79 | setServer(domain,port);
80 | setClient(client);
81 | this->stream = NULL;
82 | }
83 | PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
84 | this->_state = MQTT_DISCONNECTED;
85 | setServer(domain,port);
86 | setClient(client);
87 | setStream(stream);
88 | }
89 | PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
90 | this->_state = MQTT_DISCONNECTED;
91 | setServer(domain,port);
92 | setCallback(callback);
93 | setClient(client);
94 | this->stream = NULL;
95 | }
96 | PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
97 | this->_state = MQTT_DISCONNECTED;
98 | setServer(domain,port);
99 | setCallback(callback);
100 | setClient(client);
101 | setStream(stream);
102 | }
103 |
104 | boolean PubSubClient::connect(const char *id) {
105 | return connect(id,NULL,NULL,0,0,0,0);
106 | }
107 |
108 | boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
109 | return connect(id,user,pass,0,0,0,0);
110 | }
111 |
112 | boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
113 | return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage);
114 | }
115 |
116 | boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
117 | if (!connected()) {
118 | int result = 0;
119 |
120 | if (domain != NULL) {
121 | result = _client->connect(this->domain, this->port);
122 | } else {
123 | result = _client->connect(this->ip, this->port);
124 | }
125 | if (result) {
126 | nextMsgId = 1;
127 | // Leave room in the buffer for header and variable length field
128 | uint16_t length = 5;
129 | unsigned int j;
130 |
131 | #if MQTT_VERSION == MQTT_VERSION_3_1
132 | uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
133 | #define MQTT_HEADER_VERSION_LENGTH 9
134 | #elif MQTT_VERSION == MQTT_VERSION_3_1_1
135 | uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
136 | #define MQTT_HEADER_VERSION_LENGTH 7
137 | #endif
138 | for (j = 0;j>1);
154 | }
155 | }
156 |
157 | buffer[length++] = v;
158 |
159 | buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
160 | buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
161 | length = writeString(id,buffer,length);
162 | if (willTopic) {
163 | length = writeString(willTopic,buffer,length);
164 | length = writeString(willMessage,buffer,length);
165 | }
166 |
167 | if(user != NULL) {
168 | length = writeString(user,buffer,length);
169 | if(pass != NULL) {
170 | length = writeString(pass,buffer,length);
171 | }
172 | }
173 |
174 | write(MQTTCONNECT,buffer,length-5);
175 |
176 | lastInActivity = lastOutActivity = millis();
177 |
178 | while (!_client->available()) {
179 | unsigned long t = millis();
180 | if (t-lastInActivity > MQTT_KEEPALIVE*1000UL) {
181 | _state = MQTT_CONNECTION_TIMEOUT;
182 | _client->stop();
183 | return false;
184 | }
185 | }
186 | uint8_t llen;
187 | uint16_t len = readPacket(&llen);
188 |
189 | if (len == 4) {
190 | if (buffer[3] == 0) {
191 | lastInActivity = millis();
192 | pingOutstanding = false;
193 | _state = MQTT_CONNECTED;
194 | return true;
195 | } else {
196 | _state = buffer[3];
197 | }
198 | }
199 | _client->stop();
200 | } else {
201 | _state = MQTT_CONNECT_FAILED;
202 | }
203 | return false;
204 | }
205 | return true;
206 | }
207 |
208 | uint8_t PubSubClient::readByte() {
209 | while(!_client->available()) {}
210 | return _client->read();
211 | }
212 |
213 | uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
214 | uint16_t len = 0;
215 | buffer[len++] = readByte();
216 | bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
217 | uint32_t multiplier = 1;
218 | uint16_t length = 0;
219 | uint8_t digit = 0;
220 | uint16_t skip = 0;
221 | uint8_t start = 0;
222 |
223 | do {
224 | digit = readByte();
225 | buffer[len++] = digit;
226 | length += (digit & 127) * multiplier;
227 | multiplier *= 128;
228 | } while ((digit & 128) != 0);
229 | *lengthLength = len-1;
230 |
231 | if (isPublish) {
232 | // Read in topic length to calculate bytes to skip over for Stream writing
233 | buffer[len++] = readByte();
234 | buffer[len++] = readByte();
235 | skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
236 | start = 2;
237 | if (buffer[0]&MQTTQOS1) {
238 | // skip message id
239 | skip += 2;
240 | }
241 | }
242 |
243 | for (uint16_t i = start;istream) {
246 | if (isPublish && len-*lengthLength-2>skip) {
247 | this->stream->write(digit);
248 | }
249 | }
250 | if (len < MQTT_MAX_PACKET_SIZE) {
251 | buffer[len] = digit;
252 | }
253 | len++;
254 | }
255 |
256 | if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
257 | len = 0; // This will cause the packet to be ignored.
258 | }
259 |
260 | return len;
261 | }
262 |
263 | boolean PubSubClient::loop() {
264 | if (connected()) {
265 | unsigned long t = millis();
266 | if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
267 | if (pingOutstanding) {
268 | this->_state = MQTT_CONNECTION_TIMEOUT;
269 | _client->stop();
270 | return false;
271 | } else {
272 | buffer[0] = MQTTPINGREQ;
273 | buffer[1] = 0;
274 | _client->write(buffer,2);
275 | lastOutActivity = t;
276 | lastInActivity = t;
277 | pingOutstanding = true;
278 | }
279 | }
280 | if (_client->available()) {
281 | uint8_t llen;
282 | uint16_t len = readPacket(&llen);
283 | uint16_t msgId = 0;
284 | uint8_t *payload;
285 | if (len > 0) {
286 | lastInActivity = t;
287 | uint8_t type = buffer[0]&0xF0;
288 | if (type == MQTTPUBLISH) {
289 | if (callback) {
290 | uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2];
291 | char topic[tl+1];
292 | for (uint16_t i=0;i0
297 | if ((buffer[0]&0x06) == MQTTQOS1) {
298 | msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
299 | payload = buffer+llen+3+tl+2;
300 | callback(topic,payload,len-llen-3-tl-2);
301 |
302 | buffer[0] = MQTTPUBACK;
303 | buffer[1] = 2;
304 | buffer[2] = (msgId >> 8);
305 | buffer[3] = (msgId & 0xFF);
306 | _client->write(buffer,4);
307 | lastOutActivity = t;
308 |
309 | } else {
310 | payload = buffer+llen+3+tl;
311 | callback(topic,payload,len-llen-3-tl);
312 | }
313 | }
314 | } else if (type == MQTTPINGREQ) {
315 | buffer[0] = MQTTPINGRESP;
316 | buffer[1] = 0;
317 | _client->write(buffer,2);
318 | } else if (type == MQTTPINGRESP) {
319 | pingOutstanding = false;
320 | }
321 | }
322 | }
323 | return true;
324 | }
325 | return false;
326 | }
327 |
328 | boolean PubSubClient::publish(const char* topic, const char* payload) {
329 | return publish(topic,(const uint8_t*)payload,strlen(payload),false);
330 | }
331 |
332 | boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
333 | return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
334 | }
335 |
336 | boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
337 | return publish(topic, payload, plength, false);
338 | }
339 |
340 | boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
341 | if (connected()) {
342 | if (MQTT_MAX_PACKET_SIZE < 5 + 2+strlen(topic) + plength) {
343 | // Too long
344 | return false;
345 | }
346 | // Leave room in the buffer for header and variable length field
347 | uint16_t length = 5;
348 | length = writeString(topic,buffer,length);
349 | uint16_t i;
350 | for (i=0;i 0) {
388 | digit |= 0x80;
389 | }
390 | buffer[pos++] = digit;
391 | llen++;
392 | } while(len>0);
393 |
394 | pos = writeString(topic,buffer,pos);
395 |
396 | rc += _client->write(buffer,pos);
397 |
398 | for (i=0;iwrite((char)pgm_read_byte_near(payload + i));
400 | }
401 |
402 | lastOutActivity = millis();
403 |
404 | return rc == tlen + 4 + plength;
405 | }
406 |
407 | boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
408 | uint8_t lenBuf[4];
409 | uint8_t llen = 0;
410 | uint8_t digit;
411 | uint8_t pos = 0;
412 | uint8_t rc;
413 | uint16_t len = length;
414 | do {
415 | digit = len % 128;
416 | len = len / 128;
417 | if (len > 0) {
418 | digit |= 0x80;
419 | }
420 | lenBuf[pos++] = digit;
421 | llen++;
422 | } while(len>0);
423 |
424 | buf[4-llen] = header;
425 | for (int i=0;i 0) && result) {
435 | bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
436 | rc = _client->write(writeBuf,bytesToWrite);
437 | result = (rc == bytesToWrite);
438 | bytesRemaining -= rc;
439 | writeBuf += rc;
440 | }
441 | return result;
442 | #else
443 | rc = _client->write(buf+(4-llen),length+1+llen);
444 | lastOutActivity = millis();
445 | return (rc == 1+llen+length);
446 | #endif
447 | }
448 |
449 | boolean PubSubClient::subscribe(const char* topic) {
450 | return subscribe(topic, 0);
451 | }
452 |
453 | boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
454 | if (qos < 0 || qos > 1) {
455 | return false;
456 | }
457 | if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
458 | // Too long
459 | return false;
460 | }
461 | if (connected()) {
462 | // Leave room in the buffer for header and variable length field
463 | uint16_t length = 5;
464 | nextMsgId++;
465 | if (nextMsgId == 0) {
466 | nextMsgId = 1;
467 | }
468 | buffer[length++] = (nextMsgId >> 8);
469 | buffer[length++] = (nextMsgId & 0xFF);
470 | length = writeString((char*)topic, buffer,length);
471 | buffer[length++] = qos;
472 | return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-5);
473 | }
474 | return false;
475 | }
476 |
477 | boolean PubSubClient::unsubscribe(const char* topic) {
478 | if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
479 | // Too long
480 | return false;
481 | }
482 | if (connected()) {
483 | uint16_t length = 5;
484 | nextMsgId++;
485 | if (nextMsgId == 0) {
486 | nextMsgId = 1;
487 | }
488 | buffer[length++] = (nextMsgId >> 8);
489 | buffer[length++] = (nextMsgId & 0xFF);
490 | length = writeString(topic, buffer,length);
491 | return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-5);
492 | }
493 | return false;
494 | }
495 |
496 | void PubSubClient::disconnect() {
497 | buffer[0] = MQTTDISCONNECT;
498 | buffer[1] = 0;
499 | _client->write(buffer,2);
500 | _state = MQTT_DISCONNECTED;
501 | _client->stop();
502 | lastInActivity = lastOutActivity = millis();
503 | }
504 |
505 | uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
506 | const char* idp = string;
507 | uint16_t i = 0;
508 | pos += 2;
509 | while (*idp) {
510 | buf[pos++] = *idp++;
511 | i++;
512 | }
513 | buf[pos-i-2] = (i >> 8);
514 | buf[pos-i-1] = (i & 0xFF);
515 | return pos;
516 | }
517 |
518 |
519 | boolean PubSubClient::connected() {
520 | boolean rc;
521 | if (_client == NULL ) {
522 | rc = false;
523 | } else {
524 | rc = (int)_client->connected();
525 | if (!rc) {
526 | if (this->_state == MQTT_CONNECTED) {
527 | this->_state = MQTT_CONNECTION_LOST;
528 | _client->flush();
529 | _client->stop();
530 | }
531 | }
532 | }
533 | return rc;
534 | }
535 |
536 | PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
537 | IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
538 | return setServer(addr,port);
539 | }
540 |
541 | PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
542 | this->ip = ip;
543 | this->port = port;
544 | this->domain = NULL;
545 | return *this;
546 | }
547 |
548 | PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
549 | this->domain = domain;
550 | this->port = port;
551 | return *this;
552 | }
553 |
554 | PubSubClient& PubSubClient::setCallback(void(*callback)(char*,uint8_t*,unsigned int)){
555 | this->callback = callback;
556 | return *this;
557 | }
558 |
559 | PubSubClient& PubSubClient::setClient(Client& client){
560 | this->_client = &client;
561 | return *this;
562 | }
563 |
564 | PubSubClient& PubSubClient::setStream(Stream& stream){
565 | this->stream = &stream;
566 | return *this;
567 | }
568 |
569 | int PubSubClient::state() {
570 | return this->_state;
571 | }
572 |
--------------------------------------------------------------------------------
/PubSubClient/src/PubSubClient.h:
--------------------------------------------------------------------------------
1 | /*
2 | PubSubClient.h - A simple client for MQTT.
3 | Nick O'Leary
4 | http://knolleary.net
5 | */
6 |
7 | #ifndef PubSubClient_h
8 | #define PubSubClient_h
9 |
10 | #include
11 | #include "IPAddress.h"
12 | #include "Client.h"
13 | #include "Stream.h"
14 |
15 | #define MQTT_VERSION_3_1 3
16 | #define MQTT_VERSION_3_1_1 4
17 |
18 | // MQTT_VERSION : Pick the version
19 | //#define MQTT_VERSION MQTT_VERSION_3_1
20 | #define MQTT_VERSION MQTT_VERSION_3_1_1
21 |
22 | // MQTT_MAX_PACKET_SIZE : Maximum packet size
23 | #define MQTT_MAX_PACKET_SIZE 256
24 |
25 | // MQTT_KEEPALIVE : keepAlive interval in Seconds
26 | #define MQTT_KEEPALIVE 15
27 |
28 | // MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
29 | // in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
30 | // pass the entire MQTT packet in each write call.
31 | //#define MQTT_MAX_TRANSFER_SIZE 80
32 |
33 | // Possible values for client.state()
34 | #define MQTT_CONNECTION_TIMEOUT -4
35 | #define MQTT_CONNECTION_LOST -3
36 | #define MQTT_CONNECT_FAILED -2
37 | #define MQTT_DISCONNECTED -1
38 | #define MQTT_CONNECTED 0
39 | #define MQTT_CONNECT_BAD_PROTOCOL 1
40 | #define MQTT_CONNECT_BAD_CLIENT_ID 2
41 | #define MQTT_CONNECT_UNAVAILABLE 3
42 | #define MQTT_CONNECT_BAD_CREDENTIALS 4
43 | #define MQTT_CONNECT_UNAUTHORIZED 5
44 |
45 | #define MQTTCONNECT 1 << 4 // Client request to connect to Server
46 | #define MQTTCONNACK 2 << 4 // Connect Acknowledgment
47 | #define MQTTPUBLISH 3 << 4 // Publish message
48 | #define MQTTPUBACK 4 << 4 // Publish Acknowledgment
49 | #define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
50 | #define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
51 | #define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
52 | #define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
53 | #define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
54 | #define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
55 | #define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
56 | #define MQTTPINGREQ 12 << 4 // PING Request
57 | #define MQTTPINGRESP 13 << 4 // PING Response
58 | #define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
59 | #define MQTTReserved 15 << 4 // Reserved
60 |
61 | #define MQTTQOS0 (0 << 1)
62 | #define MQTTQOS1 (1 << 1)
63 | #define MQTTQOS2 (2 << 1)
64 |
65 | #define MQTT_CALLBACK_SIGNATURE void (*callback)(char*,uint8_t*,unsigned int)
66 |
67 | class PubSubClient {
68 | private:
69 | Client* _client;
70 | uint8_t buffer[MQTT_MAX_PACKET_SIZE];
71 | uint16_t nextMsgId;
72 | unsigned long lastOutActivity;
73 | unsigned long lastInActivity;
74 | bool pingOutstanding;
75 | MQTT_CALLBACK_SIGNATURE;
76 | uint16_t readPacket(uint8_t*);
77 | uint8_t readByte();
78 | boolean write(uint8_t header, uint8_t* buf, uint16_t length);
79 | uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
80 | IPAddress ip;
81 | const char* domain;
82 | uint16_t port;
83 | Stream* stream;
84 | int _state;
85 | public:
86 | PubSubClient();
87 | PubSubClient(Client& client);
88 | PubSubClient(IPAddress, uint16_t, Client& client);
89 | PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
90 | PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
91 | PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
92 | PubSubClient(uint8_t *, uint16_t, Client& client);
93 | PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
94 | PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
95 | PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
96 | PubSubClient(const char*, uint16_t, Client& client);
97 | PubSubClient(const char*, uint16_t, Client& client, Stream&);
98 | PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
99 | PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
100 |
101 | PubSubClient& setServer(IPAddress ip, uint16_t port);
102 | PubSubClient& setServer(uint8_t * ip, uint16_t port);
103 | PubSubClient& setServer(const char * domain, uint16_t port);
104 | PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
105 | PubSubClient& setClient(Client& client);
106 | PubSubClient& setStream(Stream& stream);
107 |
108 | boolean connect(const char* id);
109 | boolean connect(const char* id, const char* user, const char* pass);
110 | boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
111 | boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
112 | void disconnect();
113 | boolean publish(const char* topic, const char* payload);
114 | boolean publish(const char* topic, const char* payload, boolean retained);
115 | boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
116 | boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
117 | boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
118 | boolean subscribe(const char* topic);
119 | boolean subscribe(const char* topic, uint8_t qos);
120 | boolean unsubscribe(const char* topic);
121 | boolean loop();
122 | boolean connected();
123 | int state();
124 | };
125 |
126 |
127 | #endif
128 |
--------------------------------------------------------------------------------
/PubSubClient/tests/Makefile:
--------------------------------------------------------------------------------
1 | SRC_PATH=./src
2 | OUT_PATH=./bin
3 | TEST_SRC=$(wildcard ${SRC_PATH}/*_spec.cpp)
4 | TEST_BIN= $(TEST_SRC:${SRC_PATH}/%.cpp=${OUT_PATH}/%)
5 | VPATH=${SRC_PATH}
6 | SHIM_FILES=${SRC_PATH}/lib/*.cpp
7 | PSC_FILE=../src/PubSubClient.cpp
8 | CC=g++
9 | CFLAGS=-I${SRC_PATH}/lib -I../src
10 |
11 | all: $(TEST_BIN)
12 |
13 | ${OUT_PATH}/%: ${SRC_PATH}/%.cpp ${PSC_FILE} ${SHIM_FILES}
14 | mkdir -p ${OUT_PATH}
15 | ${CC} ${CFLAGS} $^ -o $@
16 |
17 | clean:
18 | @rm -rf ${OUT_PATH}
19 |
20 | test:
21 | @bin/connect_spec
22 | @bin/publish_spec
23 | @bin/receive_spec
24 | @bin/subscribe_spec
25 | @bin/keepalive_spec
26 |
--------------------------------------------------------------------------------
/PubSubClient/tests/README.md:
--------------------------------------------------------------------------------
1 | # Arduino Client for MQTT Test Suite
2 |
3 | This is a regression test suite for the `PubSubClient` library.
4 |
5 | There are two parts:
6 |
7 | - Tests that can be compiled and run on any machine
8 | - Tests that build the example sketches using the Arduino IDE
9 |
10 |
11 | It is a work-in-progress and is subject to complete refactoring as the whim takes
12 | me.
13 |
14 |
15 | ## Local tests
16 |
17 | These are a set of executables that can be run to test specific areas of functionality.
18 | They do not require a real Arduino to be attached, nor the use of the Arduino IDE.
19 |
20 | The tests include a set of mock files to stub out the parts of the Arduino environment the library
21 | depends on.
22 |
23 | ### Dependencies
24 |
25 | - g++
26 |
27 | ### Running
28 |
29 | Build the tests using the provided `Makefile`:
30 |
31 | $ make
32 |
33 | This will create a set of executables in `./bin/`. Run each of these executables to test the corresponding functionality.
34 |
35 | *Note:* the `connect_spec` and `keepalive_spec` tests involve testing keepalive timers so naturally take a few minutes to run through.
36 |
37 | ## Arduino tests
38 |
39 | *Note:* INO Tool doesn't currently play nicely with Arduino 1.5. This has broken this test suite.
40 |
41 | Without a suitable arduino plugged in, the test suite will only check the
42 | example sketches compile cleanly against the library.
43 |
44 | With an arduino plugged in, each sketch that has a corresponding python
45 | test case is built, uploaded and then the tests run.
46 |
47 | ### Dependencies
48 |
49 | - Python 2.7+
50 | - [INO Tool](http://inotool.org/) - this provides command-line build/upload of Arduino sketches
51 |
52 | ### Running
53 |
54 | The test suite _does not_ run an MQTT server - it is assumed to be running already.
55 |
56 | $ python testsuite.py
57 |
58 | A summary of activity is printed to the console. More comprehensive logs are written
59 | to the `logs` directory.
60 |
61 | ### What it does
62 |
63 | For each sketch in the library's `examples` directory, e.g. `mqtt_basic.ino`, the suite looks for a matching test case
64 | `testcases/mqtt_basic.py`.
65 |
66 | The test case must follow these conventions:
67 | - sub-class `unittest.TestCase`
68 | - provide the class methods `setUpClass` and `tearDownClass` (TODO: make this optional)
69 | - all test method names begin with `test_`
70 |
71 | The suite will call the `setUpClass` method _before_ uploading the sketch. This
72 | allows any test setup to be performed before the sketch runs - such as connecting
73 | a client and subscribing to topics.
74 |
75 |
76 | ### Settings
77 |
78 | The file `testcases/settings.py` is used to config the test environment.
79 |
80 | - `server_ip` - the IP address of the broker the client should connect to (the broker port is assumed to be 1883).
81 | - `arduino_ip` - the IP address the arduino should use (when not testing DHCP).
82 |
83 | Before each sketch is compiled, these values are automatically substituted in. To
84 | do this, the suite looks for lines that _start_ with the following:
85 |
86 | byte server[] = {
87 | byte ip[] = {
88 |
89 | and replaces them with the appropriate values.
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/connect_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 |
15 | int test_connect_fails_no_network() {
16 | IT("fails to connect if underlying client doesn't connect");
17 | ShimClient shimClient;
18 | shimClient.setAllowConnect(false);
19 | PubSubClient client(server, 1883, callback, shimClient);
20 | int rc = client.connect((char*)"client_test1");
21 | IS_FALSE(rc);
22 | int state = client.state();
23 | IS_TRUE(state == MQTT_CONNECT_FAILED);
24 | END_IT
25 | }
26 |
27 | int test_connect_fails_on_no_response() {
28 | IT("fails to connect if no response received after 15 seconds");
29 | ShimClient shimClient;
30 | shimClient.setAllowConnect(true);
31 | PubSubClient client(server, 1883, callback, shimClient);
32 | int rc = client.connect((char*)"client_test1");
33 | IS_FALSE(rc);
34 | int state = client.state();
35 | IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
36 | END_IT
37 | }
38 |
39 | int test_connect_properly_formatted() {
40 | IT("sends a properly formatted connect packet and succeeds");
41 | ShimClient shimClient;
42 |
43 | shimClient.setAllowConnect(true);
44 | byte expectServer[] = { 172, 16, 0, 2 };
45 | shimClient.expectConnect(expectServer,1883);
46 | byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
47 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
48 |
49 | shimClient.expect(connect,26);
50 | shimClient.respond(connack,4);
51 |
52 | PubSubClient client(server, 1883, callback, shimClient);
53 | int state = client.state();
54 | IS_TRUE(state == MQTT_DISCONNECTED);
55 |
56 | int rc = client.connect((char*)"client_test1");
57 | IS_TRUE(rc);
58 | IS_FALSE(shimClient.error());
59 |
60 | state = client.state();
61 | IS_TRUE(state == MQTT_CONNECTED);
62 |
63 | END_IT
64 | }
65 |
66 | int test_connect_properly_formatted_hostname() {
67 | IT("accepts a hostname");
68 | ShimClient shimClient;
69 |
70 | shimClient.setAllowConnect(true);
71 | shimClient.expectConnect((char* const)"localhost",1883);
72 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
73 | shimClient.respond(connack,4);
74 |
75 | PubSubClient client((char* const)"localhost", 1883, callback, shimClient);
76 | int rc = client.connect((char*)"client_test1");
77 | IS_TRUE(rc);
78 | IS_FALSE(shimClient.error());
79 |
80 | END_IT
81 | }
82 |
83 |
84 | int test_connect_fails_on_bad_rc() {
85 | IT("fails to connect if a bad return code is received");
86 | ShimClient shimClient;
87 | shimClient.setAllowConnect(true);
88 | byte connack[] = { 0x20, 0x02, 0x00, 0x01 };
89 | shimClient.respond(connack,4);
90 |
91 | PubSubClient client(server, 1883, callback, shimClient);
92 | int rc = client.connect((char*)"client_test1");
93 | IS_FALSE(rc);
94 |
95 | int state = client.state();
96 | IS_TRUE(state == 0x01);
97 |
98 | END_IT
99 | }
100 |
101 | int test_connect_accepts_username_password() {
102 | IT("accepts a username and password");
103 | ShimClient shimClient;
104 | shimClient.setAllowConnect(true);
105 |
106 | byte connect[] = { 0x10,0x24,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,0x4,0x70,0x61,0x73,0x73};
107 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
108 | shimClient.expect(connect,0x26);
109 | shimClient.respond(connack,4);
110 |
111 | PubSubClient client(server, 1883, callback, shimClient);
112 | int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass");
113 | IS_TRUE(rc);
114 | IS_FALSE(shimClient.error());
115 |
116 | END_IT
117 | }
118 |
119 | int test_connect_accepts_username_no_password() {
120 | IT("accepts a username but no password");
121 | ShimClient shimClient;
122 | shimClient.setAllowConnect(true);
123 |
124 | byte connect[] = { 0x10,0x1e,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x82,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72};
125 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
126 | shimClient.expect(connect,0x20);
127 | shimClient.respond(connack,4);
128 |
129 | PubSubClient client(server, 1883, callback, shimClient);
130 | int rc = client.connect((char*)"client_test1",(char*)"user",0);
131 | IS_TRUE(rc);
132 | IS_FALSE(shimClient.error());
133 |
134 | END_IT
135 | }
136 |
137 | int test_connect_ignores_password_no_username() {
138 | IT("ignores a password but no username");
139 | ShimClient shimClient;
140 | shimClient.setAllowConnect(true);
141 |
142 | byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
143 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
144 | shimClient.expect(connect,26);
145 | shimClient.respond(connack,4);
146 |
147 | PubSubClient client(server, 1883, callback, shimClient);
148 | int rc = client.connect((char*)"client_test1",0,(char*)"pass");
149 | IS_TRUE(rc);
150 | IS_FALSE(shimClient.error());
151 |
152 | END_IT
153 | }
154 |
155 | int test_connect_with_will() {
156 | IT("accepts a will");
157 | ShimClient shimClient;
158 | shimClient.setAllowConnect(true);
159 |
160 | byte connect[] = {0x10,0x30,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xe,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65};
161 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
162 | shimClient.expect(connect,0x32);
163 | shimClient.respond(connack,4);
164 |
165 | PubSubClient client(server, 1883, callback, shimClient);
166 | int rc = client.connect((char*)"client_test1",(char*)"willTopic",1,0,(char*)"willMessage");
167 | IS_TRUE(rc);
168 | IS_FALSE(shimClient.error());
169 |
170 | END_IT
171 | }
172 |
173 | int test_connect_with_will_username_password() {
174 | IT("accepts a will, username and password");
175 | ShimClient shimClient;
176 | shimClient.setAllowConnect(true);
177 |
178 | byte connect[] = {0x10,0x40,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xce,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x8,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64};
179 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
180 | shimClient.expect(connect,0x42);
181 | shimClient.respond(connack,4);
182 |
183 | PubSubClient client(server, 1883, callback, shimClient);
184 | int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"password",(char*)"willTopic",1,0,(char*)"willMessage");
185 | IS_TRUE(rc);
186 | IS_FALSE(shimClient.error());
187 |
188 | END_IT
189 | }
190 |
191 | int test_connect_disconnect_connect() {
192 | IT("connects, disconnects and connects again");
193 | ShimClient shimClient;
194 |
195 | shimClient.setAllowConnect(true);
196 | byte expectServer[] = { 172, 16, 0, 2 };
197 | shimClient.expectConnect(expectServer,1883);
198 | byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
199 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
200 |
201 | shimClient.expect(connect,26);
202 | shimClient.respond(connack,4);
203 |
204 | PubSubClient client(server, 1883, callback, shimClient);
205 |
206 | int state = client.state();
207 | IS_TRUE(state == MQTT_DISCONNECTED);
208 |
209 | int rc = client.connect((char*)"client_test1");
210 | IS_TRUE(rc);
211 | IS_FALSE(shimClient.error());
212 |
213 | state = client.state();
214 | IS_TRUE(state == MQTT_CONNECTED);
215 |
216 | byte disconnect[] = {0xE0,0x00};
217 | shimClient.expect(disconnect,2);
218 |
219 | client.disconnect();
220 |
221 | IS_FALSE(client.connected());
222 | IS_FALSE(shimClient.connected());
223 | IS_FALSE(shimClient.error());
224 |
225 | state = client.state();
226 | IS_TRUE(state == MQTT_DISCONNECTED);
227 |
228 | shimClient.expect(connect,28);
229 | shimClient.respond(connack,4);
230 | rc = client.connect((char*)"client_test1");
231 | IS_TRUE(rc);
232 | IS_FALSE(shimClient.error());
233 | state = client.state();
234 | IS_TRUE(state == MQTT_CONNECTED);
235 |
236 | END_IT
237 | }
238 |
239 | int main()
240 | {
241 | SUITE("Connect");
242 | test_connect_fails_no_network();
243 | test_connect_fails_on_no_response();
244 |
245 | test_connect_properly_formatted();
246 | test_connect_accepts_username_password();
247 | test_connect_fails_on_bad_rc();
248 | test_connect_properly_formatted_hostname();
249 |
250 | test_connect_accepts_username_no_password();
251 | test_connect_ignores_password_no_username();
252 | test_connect_with_will();
253 | test_connect_with_will_username_password();
254 | test_connect_disconnect_connect();
255 | FINISH
256 | }
257 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/keepalive_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 | #include
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 |
15 | int test_keepalive_pings_idle() {
16 | IT("keeps an idle connection alive (takes 1 minute)");
17 |
18 | ShimClient shimClient;
19 | shimClient.setAllowConnect(true);
20 |
21 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
22 | shimClient.respond(connack,4);
23 |
24 | PubSubClient client(server, 1883, callback, shimClient);
25 | int rc = client.connect((char*)"client_test1");
26 | IS_TRUE(rc);
27 |
28 | byte pingreq[] = { 0xC0,0x0 };
29 | shimClient.expect(pingreq,2);
30 | byte pingresp[] = { 0xD0,0x0 };
31 | shimClient.respond(pingresp,2);
32 |
33 | for (int i = 0; i < 50; i++) {
34 | sleep(1);
35 | if ( i == 15 || i == 31 || i == 47) {
36 | shimClient.expect(pingreq,2);
37 | shimClient.respond(pingresp,2);
38 | }
39 | rc = client.loop();
40 | IS_TRUE(rc);
41 | }
42 |
43 | IS_FALSE(shimClient.error());
44 |
45 | END_IT
46 | }
47 |
48 | int test_keepalive_pings_with_outbound_qos0() {
49 | IT("keeps a connection alive that only sends qos0 (takes 1 minute)");
50 |
51 | ShimClient shimClient;
52 | shimClient.setAllowConnect(true);
53 |
54 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
55 | shimClient.respond(connack,4);
56 |
57 | PubSubClient client(server, 1883, callback, shimClient);
58 | int rc = client.connect((char*)"client_test1");
59 | IS_TRUE(rc);
60 |
61 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
62 |
63 | for (int i = 0; i < 50; i++) {
64 | TRACE(i<<":");
65 | shimClient.expect(publish,16);
66 | rc = client.publish((char*)"topic",(char*)"payload");
67 | IS_TRUE(rc);
68 | IS_FALSE(shimClient.error());
69 | sleep(1);
70 | if ( i == 15 || i == 31 || i == 47) {
71 | byte pingreq[] = { 0xC0,0x0 };
72 | shimClient.expect(pingreq,2);
73 | byte pingresp[] = { 0xD0,0x0 };
74 | shimClient.respond(pingresp,2);
75 | }
76 | rc = client.loop();
77 | IS_TRUE(rc);
78 | IS_FALSE(shimClient.error());
79 | }
80 |
81 | END_IT
82 | }
83 |
84 | int test_keepalive_pings_with_inbound_qos0() {
85 | IT("keeps a connection alive that only receives qos0 (takes 1 minute)");
86 |
87 | ShimClient shimClient;
88 | shimClient.setAllowConnect(true);
89 |
90 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
91 | shimClient.respond(connack,4);
92 |
93 | PubSubClient client(server, 1883, callback, shimClient);
94 | int rc = client.connect((char*)"client_test1");
95 | IS_TRUE(rc);
96 |
97 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
98 |
99 | for (int i = 0; i < 50; i++) {
100 | TRACE(i<<":");
101 | sleep(1);
102 | if ( i == 15 || i == 31 || i == 47) {
103 | byte pingreq[] = { 0xC0,0x0 };
104 | shimClient.expect(pingreq,2);
105 | byte pingresp[] = { 0xD0,0x0 };
106 | shimClient.respond(pingresp,2);
107 | }
108 | shimClient.respond(publish,16);
109 | rc = client.loop();
110 | IS_TRUE(rc);
111 | IS_FALSE(shimClient.error());
112 | }
113 |
114 | END_IT
115 | }
116 |
117 | int test_keepalive_no_pings_inbound_qos1() {
118 | IT("does not send pings for connections with inbound qos1 (takes 1 minute)");
119 |
120 | ShimClient shimClient;
121 | shimClient.setAllowConnect(true);
122 |
123 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
124 | shimClient.respond(connack,4);
125 |
126 | PubSubClient client(server, 1883, callback, shimClient);
127 | int rc = client.connect((char*)"client_test1");
128 | IS_TRUE(rc);
129 |
130 | byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
131 | byte puback[] = {0x40,0x2,0x12,0x34};
132 |
133 | for (int i = 0; i < 50; i++) {
134 | shimClient.respond(publish,18);
135 | shimClient.expect(puback,4);
136 | sleep(1);
137 | rc = client.loop();
138 | IS_TRUE(rc);
139 | IS_FALSE(shimClient.error());
140 | }
141 |
142 | END_IT
143 | }
144 |
145 | int test_keepalive_disconnects_hung() {
146 | IT("disconnects a hung connection (takes 30 seconds)");
147 |
148 | ShimClient shimClient;
149 | shimClient.setAllowConnect(true);
150 |
151 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
152 | shimClient.respond(connack,4);
153 |
154 | PubSubClient client(server, 1883, callback, shimClient);
155 | int rc = client.connect((char*)"client_test1");
156 | IS_TRUE(rc);
157 |
158 | byte pingreq[] = { 0xC0,0x0 };
159 | shimClient.expect(pingreq,2);
160 |
161 | for (int i = 0; i < 32; i++) {
162 | sleep(1);
163 | rc = client.loop();
164 | }
165 | IS_FALSE(rc);
166 |
167 | int state = client.state();
168 | IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
169 |
170 | IS_FALSE(shimClient.error());
171 |
172 | END_IT
173 | }
174 |
175 | int main()
176 | {
177 | SUITE("Keep-alive");
178 | test_keepalive_pings_idle();
179 | test_keepalive_pings_with_outbound_qos0();
180 | test_keepalive_pings_with_inbound_qos0();
181 | test_keepalive_no_pings_inbound_qos1();
182 | test_keepalive_disconnects_hung();
183 |
184 | FINISH
185 | }
186 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/Arduino.h:
--------------------------------------------------------------------------------
1 | #ifndef Arduino_h
2 | #define Arduino_h
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 |
10 | extern "C"{
11 | typedef uint8_t byte ;
12 | typedef uint8_t boolean ;
13 |
14 | /* sketch */
15 | extern void setup( void ) ;
16 | extern void loop( void ) ;
17 | uint32_t millis( void );
18 | }
19 |
20 | #define PROGMEM
21 | #define pgm_read_byte_near(x) *(x)
22 |
23 | #endif // Arduino_h
24 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/BDDTest.cpp:
--------------------------------------------------------------------------------
1 | #include "BDDTest.h"
2 | #include "trace.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | int testCount = 0;
9 | int testPasses = 0;
10 | const char* testDescription;
11 |
12 | std::list failureList;
13 |
14 | void bddtest_suite(const char* name) {
15 | LOG(name << "\n");
16 | }
17 |
18 | int bddtest_test(const char* file, int line, const char* assertion, int result) {
19 | if (!result) {
20 | LOG("✗\n");
21 | std::ostringstream os;
22 | os << " ! "<::iterator it = failureList.begin(); it != failureList.end(); it++) {
40 | LOG("\n");
41 | LOG(*it);
42 | LOG("\n");
43 | }
44 |
45 | LOG(std::dec << testPasses << "/" << testCount << " tests passed\n\n");
46 | if (testPasses == testCount) {
47 | return 0;
48 | }
49 | return 1;
50 | }
51 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/BDDTest.h:
--------------------------------------------------------------------------------
1 | #ifndef bddtest_h
2 | #define bddtest_h
3 |
4 | void bddtest_suite(const char* name);
5 | int bddtest_test(const char*, int, const char*, int);
6 | void bddtest_start(const char*);
7 | void bddtest_end();
8 | int bddtest_summary();
9 |
10 | #define SUITE(x) { bddtest_suite(x); }
11 | #define TEST(x) { if (!bddtest_test(__FILE__, __LINE__, #x, (x))) return false; }
12 |
13 | #define IT(x) { bddtest_start(x); }
14 | #define END_IT { bddtest_end();return true;}
15 |
16 | #define FINISH { return bddtest_summary(); }
17 |
18 | #define IS_TRUE(x) TEST(x)
19 | #define IS_FALSE(x) TEST(!(x))
20 | #define IS_EQUAL(x,y) TEST(x==y)
21 | #define IS_NOT_EQUAL(x,y) TEST(x!=y)
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/Buffer.cpp:
--------------------------------------------------------------------------------
1 | #include "Buffer.h"
2 | #include "Arduino.h"
3 |
4 | Buffer::Buffer() {
5 | }
6 |
7 | Buffer::Buffer(uint8_t* buf, size_t size) {
8 | this->add(buf,size);
9 | }
10 | bool Buffer::available() {
11 | return this->pos < this->length;
12 | }
13 |
14 | uint8_t Buffer::next() {
15 | if (this->available()) {
16 | return this->buffer[this->pos++];
17 | }
18 | return 0;
19 | }
20 |
21 | void Buffer::reset() {
22 | this->pos = 0;
23 | }
24 |
25 | void Buffer::add(uint8_t* buf, size_t size) {
26 | uint16_t i = 0;
27 | for (;ibuffer[this->length++] = buf[i];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/Buffer.h:
--------------------------------------------------------------------------------
1 | #ifndef buffer_h
2 | #define buffer_h
3 |
4 | #include "Arduino.h"
5 |
6 | class Buffer {
7 | private:
8 | uint8_t buffer[1024];
9 | uint16_t pos;
10 | uint16_t length;
11 |
12 | public:
13 | Buffer();
14 | Buffer(uint8_t* buf, size_t size);
15 |
16 | virtual bool available();
17 | virtual uint8_t next();
18 | virtual void reset();
19 |
20 | virtual void add(uint8_t* buf, size_t size);
21 | };
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/Client.h:
--------------------------------------------------------------------------------
1 | #ifndef client_h
2 | #define client_h
3 | #include "IPAddress.h"
4 |
5 | class Client {
6 | public:
7 | virtual int connect(IPAddress ip, uint16_t port) =0;
8 | virtual int connect(const char *host, uint16_t port) =0;
9 | virtual size_t write(uint8_t) =0;
10 | virtual size_t write(const uint8_t *buf, size_t size) =0;
11 | virtual int available() = 0;
12 | virtual int read() = 0;
13 | virtual int read(uint8_t *buf, size_t size) = 0;
14 | virtual int peek() = 0;
15 | virtual void flush() = 0;
16 | virtual void stop() = 0;
17 | virtual uint8_t connected() = 0;
18 | virtual operator bool() = 0;
19 | };
20 |
21 | #endif
22 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/IPAddress.cpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 |
5 | IPAddress::IPAddress()
6 | {
7 | memset(_address, 0, sizeof(_address));
8 | }
9 |
10 | IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
11 | {
12 | _address[0] = first_octet;
13 | _address[1] = second_octet;
14 | _address[2] = third_octet;
15 | _address[3] = fourth_octet;
16 | }
17 |
18 | IPAddress::IPAddress(uint32_t address)
19 | {
20 | memcpy(_address, &address, sizeof(_address));
21 | }
22 |
23 | IPAddress::IPAddress(const uint8_t *address)
24 | {
25 | memcpy(_address, address, sizeof(_address));
26 | }
27 |
28 | IPAddress& IPAddress::operator=(const uint8_t *address)
29 | {
30 | memcpy(_address, address, sizeof(_address));
31 | return *this;
32 | }
33 |
34 | IPAddress& IPAddress::operator=(uint32_t address)
35 | {
36 | memcpy(_address, (const uint8_t *)&address, sizeof(_address));
37 | return *this;
38 | }
39 |
40 | bool IPAddress::operator==(const uint8_t* addr)
41 | {
42 | return memcmp(addr, _address, sizeof(_address)) == 0;
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/IPAddress.h:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * MIT License:
4 | * Copyright (c) 2011 Adrian McEwen
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | *
23 | * adrianm@mcqn.com 1/1/2011
24 | */
25 |
26 | #ifndef IPAddress_h
27 | #define IPAddress_h
28 |
29 |
30 | // A class to make it easier to handle and pass around IP addresses
31 |
32 | class IPAddress {
33 | private:
34 | uint8_t _address[4]; // IPv4 address
35 | // Access the raw byte array containing the address. Because this returns a pointer
36 | // to the internal structure rather than a copy of the address this function should only
37 | // be used when you know that the usage of the returned uint8_t* will be transient and not
38 | // stored.
39 | uint8_t* raw_address() { return _address; };
40 |
41 | public:
42 | // Constructors
43 | IPAddress();
44 | IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
45 | IPAddress(uint32_t address);
46 | IPAddress(const uint8_t *address);
47 |
48 | // Overloaded cast operator to allow IPAddress objects to be used where a pointer
49 | // to a four-byte uint8_t array is expected
50 | operator uint32_t() { return *((uint32_t*)_address); };
51 | bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
52 | bool operator==(const uint8_t* addr);
53 |
54 | // Overloaded index operator to allow getting and setting individual octets of the address
55 | uint8_t operator[](int index) const { return _address[index]; };
56 | uint8_t& operator[](int index) { return _address[index]; };
57 |
58 | // Overloaded copy operators to allow initialisation of IPAddress objects from other types
59 | IPAddress& operator=(const uint8_t *address);
60 | IPAddress& operator=(uint32_t address);
61 |
62 |
63 | friend class EthernetClass;
64 | friend class UDP;
65 | friend class Client;
66 | friend class Server;
67 | friend class DhcpClass;
68 | friend class DNSClient;
69 | };
70 |
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/ShimClient.cpp:
--------------------------------------------------------------------------------
1 | #include "ShimClient.h"
2 | #include "trace.h"
3 | #include
4 | #include
5 | #include
6 |
7 | extern "C" {
8 | uint32_t millis(void) {
9 | return time(0)*1000;
10 | }
11 | }
12 |
13 | ShimClient::ShimClient() {
14 | this->responseBuffer = new Buffer();
15 | this->expectBuffer = new Buffer();
16 | this->_allowConnect = true;
17 | this->_connected = false;
18 | this->_error = false;
19 | this->expectAnything = true;
20 | this->_received = 0;
21 | this->_expectedPort = 0;
22 | }
23 |
24 | int ShimClient::connect(IPAddress ip, uint16_t port) {
25 | if (this->_allowConnect) {
26 | this->_connected = true;
27 | }
28 | if (this->_expectedPort !=0) {
29 | // if (memcmp(ip,this->_expectedIP,4) != 0) {
30 | // TRACE( "ip mismatch\n");
31 | // this->_error = true;
32 | // }
33 | if (port != this->_expectedPort) {
34 | TRACE( "port mismatch\n");
35 | this->_error = true;
36 | }
37 | }
38 | return this->_connected;
39 | }
40 | int ShimClient::connect(const char *host, uint16_t port) {
41 | if (this->_allowConnect) {
42 | this->_connected = true;
43 | }
44 | if (this->_expectedPort !=0) {
45 | if (strcmp(host,this->_expectedHost) != 0) {
46 | TRACE( "host mismatch\n");
47 | this->_error = true;
48 | }
49 | if (port != this->_expectedPort) {
50 | TRACE( "port mismatch\n");
51 | this->_error = true;
52 | }
53 |
54 | }
55 | return this->_connected;
56 | }
57 | size_t ShimClient::write(uint8_t b) {
58 | this->_received += 1;
59 | TRACE(std::hex << (unsigned int)b);
60 | if (!this->expectAnything) {
61 | if (this->expectBuffer->available()) {
62 | uint8_t expected = this->expectBuffer->next();
63 | if (expected != b) {
64 | this->_error = true;
65 | TRACE("!=" << (unsigned int)expected);
66 | }
67 | } else {
68 | this->_error = true;
69 | }
70 | }
71 | TRACE("\n"<< std::dec);
72 | return 1;
73 | }
74 | size_t ShimClient::write(const uint8_t *buf, size_t size) {
75 | this->_received += size;
76 | TRACE( "[" << std::dec << (unsigned int)(size) << "] ");
77 | uint16_t i=0;
78 | for (;i0) {
80 | TRACE(":");
81 | }
82 | TRACE(std::hex << (unsigned int)(buf[i]));
83 |
84 | if (!this->expectAnything) {
85 | if (this->expectBuffer->available()) {
86 | uint8_t expected = this->expectBuffer->next();
87 | if (expected != buf[i]) {
88 | this->_error = true;
89 | TRACE("!=" << (unsigned int)expected);
90 | }
91 | } else {
92 | this->_error = true;
93 | }
94 | }
95 | }
96 | TRACE("\n"<responseBuffer->available();
101 | }
102 | int ShimClient::read() { return this->responseBuffer->next(); }
103 | int ShimClient::read(uint8_t *buf, size_t size) {
104 | uint16_t i = 0;
105 | for (;iread();
107 | }
108 | return size;
109 | }
110 | int ShimClient::peek() { return 0; }
111 | void ShimClient::flush() {}
112 | void ShimClient::stop() {
113 | this->setConnected(false);
114 | }
115 | uint8_t ShimClient::connected() { return this->_connected; }
116 | ShimClient::operator bool() { return true; }
117 |
118 |
119 | ShimClient* ShimClient::respond(uint8_t *buf, size_t size) {
120 | this->responseBuffer->add(buf,size);
121 | return this;
122 | }
123 |
124 | ShimClient* ShimClient::expect(uint8_t *buf, size_t size) {
125 | this->expectAnything = false;
126 | this->expectBuffer->add(buf,size);
127 | return this;
128 | }
129 |
130 | void ShimClient::setConnected(bool b) {
131 | this->_connected = b;
132 | }
133 | void ShimClient::setAllowConnect(bool b) {
134 | this->_allowConnect = b;
135 | }
136 |
137 | bool ShimClient::error() {
138 | return this->_error;
139 | }
140 |
141 | uint16_t ShimClient::received() {
142 | return this->_received;
143 | }
144 |
145 | void ShimClient::expectConnect(IPAddress ip, uint16_t port) {
146 | this->_expectedIP = ip;
147 | this->_expectedPort = port;
148 | }
149 |
150 | void ShimClient::expectConnect(const char *host, uint16_t port) {
151 | this->_expectedHost = host;
152 | this->_expectedPort = port;
153 | }
154 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/ShimClient.h:
--------------------------------------------------------------------------------
1 | #ifndef shimclient_h
2 | #define shimclient_h
3 |
4 | #include "Arduino.h"
5 | #include "Client.h"
6 | #include "IPAddress.h"
7 | #include "Buffer.h"
8 |
9 |
10 | class ShimClient : public Client {
11 | private:
12 | Buffer* responseBuffer;
13 | Buffer* expectBuffer;
14 | bool _allowConnect;
15 | bool _connected;
16 | bool expectAnything;
17 | bool _error;
18 | uint16_t _received;
19 | IPAddress _expectedIP;
20 | uint16_t _expectedPort;
21 | const char* _expectedHost;
22 |
23 | public:
24 | ShimClient();
25 | virtual int connect(IPAddress ip, uint16_t port);
26 | virtual int connect(const char *host, uint16_t port);
27 | virtual size_t write(uint8_t);
28 | virtual size_t write(const uint8_t *buf, size_t size);
29 | virtual int available();
30 | virtual int read();
31 | virtual int read(uint8_t *buf, size_t size);
32 | virtual int peek();
33 | virtual void flush();
34 | virtual void stop();
35 | virtual uint8_t connected();
36 | virtual operator bool();
37 |
38 | virtual ShimClient* respond(uint8_t *buf, size_t size);
39 | virtual ShimClient* expect(uint8_t *buf, size_t size);
40 |
41 | virtual void expectConnect(IPAddress ip, uint16_t port);
42 | virtual void expectConnect(const char *host, uint16_t port);
43 |
44 | virtual uint16_t received();
45 | virtual bool error();
46 |
47 | virtual void setAllowConnect(bool b);
48 | virtual void setConnected(bool b);
49 | };
50 |
51 | #endif
52 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/Stream.cpp:
--------------------------------------------------------------------------------
1 | #include "Stream.h"
2 | #include "trace.h"
3 | #include
4 | #include
5 |
6 | Stream::Stream() {
7 | this->expectBuffer = new Buffer();
8 | this->_error = false;
9 | this->_written = 0;
10 | }
11 |
12 | size_t Stream::write(uint8_t b) {
13 | this->_written++;
14 | TRACE(std::hex << (unsigned int)b);
15 | if (this->expectBuffer->available()) {
16 | uint8_t expected = this->expectBuffer->next();
17 | if (expected != b) {
18 | this->_error = true;
19 | TRACE("!=" << (unsigned int)expected);
20 | }
21 | } else {
22 | this->_error = true;
23 | }
24 | TRACE("\n"<< std::dec);
25 | return 1;
26 | }
27 |
28 |
29 | bool Stream::error() {
30 | return this->_error;
31 | }
32 |
33 | void Stream::expect(uint8_t *buf, size_t size) {
34 | this->expectBuffer->add(buf,size);
35 | }
36 |
37 | uint16_t Stream::length() {
38 | return this->_written;
39 | }
40 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/Stream.h:
--------------------------------------------------------------------------------
1 | #ifndef Stream_h
2 | #define Stream_h
3 |
4 | #include "Arduino.h"
5 | #include "Buffer.h"
6 |
7 | class Stream {
8 | private:
9 | Buffer* expectBuffer;
10 | bool _error;
11 | uint16_t _written;
12 |
13 | public:
14 | Stream();
15 | virtual size_t write(uint8_t);
16 |
17 | virtual bool error();
18 | virtual void expect(uint8_t *buf, size_t size);
19 | virtual uint16_t length();
20 | };
21 |
22 | #endif
23 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/lib/trace.h:
--------------------------------------------------------------------------------
1 | #ifndef trace_h
2 | #define trace_h
3 | #include
4 |
5 | #include
6 |
7 | #define LOG(x) {std::cout << x << std::flush; }
8 | #define TRACE(x) {if (getenv("TRACE")) { std::cout << x << std::flush; }}
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/publish_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 | int test_publish() {
15 | IT("publishes a null-terminated string");
16 | ShimClient shimClient;
17 | shimClient.setAllowConnect(true);
18 |
19 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
20 | shimClient.respond(connack,4);
21 |
22 | PubSubClient client(server, 1883, callback, shimClient);
23 | int rc = client.connect((char*)"client_test1");
24 | IS_TRUE(rc);
25 |
26 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
27 | shimClient.expect(publish,16);
28 |
29 | rc = client.publish((char*)"topic",(char*)"payload");
30 | IS_TRUE(rc);
31 |
32 | IS_FALSE(shimClient.error());
33 |
34 | END_IT
35 | }
36 |
37 |
38 | int test_publish_bytes() {
39 | IT("publishes a byte array");
40 | ShimClient shimClient;
41 | shimClient.setAllowConnect(true);
42 |
43 | byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
44 | int length = 5;
45 |
46 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
47 | shimClient.respond(connack,4);
48 |
49 | PubSubClient client(server, 1883, callback, shimClient);
50 | int rc = client.connect((char*)"client_test1");
51 | IS_TRUE(rc);
52 |
53 | byte publish[] = {0x30,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
54 | shimClient.expect(publish,14);
55 |
56 | rc = client.publish((char*)"topic",payload,length);
57 | IS_TRUE(rc);
58 |
59 | IS_FALSE(shimClient.error());
60 |
61 | END_IT
62 | }
63 |
64 |
65 | int test_publish_retained() {
66 | IT("publishes retained - 1");
67 | ShimClient shimClient;
68 | shimClient.setAllowConnect(true);
69 |
70 | byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
71 | int length = 5;
72 |
73 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
74 | shimClient.respond(connack,4);
75 |
76 | PubSubClient client(server, 1883, callback, shimClient);
77 | int rc = client.connect((char*)"client_test1");
78 | IS_TRUE(rc);
79 |
80 | byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
81 | shimClient.expect(publish,14);
82 |
83 | rc = client.publish((char*)"topic",payload,length,true);
84 | IS_TRUE(rc);
85 |
86 | IS_FALSE(shimClient.error());
87 |
88 | END_IT
89 | }
90 |
91 | int test_publish_retained_2() {
92 | IT("publishes retained - 2");
93 | ShimClient shimClient;
94 | shimClient.setAllowConnect(true);
95 |
96 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
97 | shimClient.respond(connack,4);
98 |
99 | PubSubClient client(server, 1883, callback, shimClient);
100 | int rc = client.connect((char*)"client_test1");
101 | IS_TRUE(rc);
102 |
103 | byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,'A','B','C','D','E'};
104 | shimClient.expect(publish,14);
105 |
106 | rc = client.publish((char*)"topic",(char*)"ABCDE",true);
107 | IS_TRUE(rc);
108 |
109 | IS_FALSE(shimClient.error());
110 |
111 | END_IT
112 | }
113 |
114 | int test_publish_not_connected() {
115 | IT("publish fails when not connected");
116 | ShimClient shimClient;
117 |
118 | PubSubClient client(server, 1883, callback, shimClient);
119 |
120 | int rc = client.publish((char*)"topic",(char*)"payload");
121 | IS_FALSE(rc);
122 |
123 | IS_FALSE(shimClient.error());
124 |
125 | END_IT
126 | }
127 |
128 | int test_publish_too_long() {
129 | IT("publish fails when topic/payload are too long");
130 | ShimClient shimClient;
131 | shimClient.setAllowConnect(true);
132 |
133 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
134 | shimClient.respond(connack,4);
135 |
136 | PubSubClient client(server, 1883, callback, shimClient);
137 | int rc = client.connect((char*)"client_test1");
138 | IS_TRUE(rc);
139 |
140 | // 0 1 2 3 4 5 6 7 8 9 0 1 2
141 | rc = client.publish((char*)"topic",(char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
142 | IS_FALSE(rc);
143 |
144 | IS_FALSE(shimClient.error());
145 |
146 | END_IT
147 | }
148 |
149 | int test_publish_P() {
150 | IT("publishes using PROGMEM");
151 | ShimClient shimClient;
152 | shimClient.setAllowConnect(true);
153 |
154 | byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
155 | int length = 5;
156 |
157 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
158 | shimClient.respond(connack,4);
159 |
160 | PubSubClient client(server, 1883, callback, shimClient);
161 | int rc = client.connect((char*)"client_test1");
162 | IS_TRUE(rc);
163 |
164 | byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
165 | shimClient.expect(publish,14);
166 |
167 | rc = client.publish_P((char*)"topic",payload,length,true);
168 | IS_TRUE(rc);
169 |
170 | IS_FALSE(shimClient.error());
171 |
172 | END_IT
173 | }
174 |
175 |
176 |
177 |
178 | int main()
179 | {
180 | SUITE("Publish");
181 | test_publish();
182 | test_publish_bytes();
183 | test_publish_retained();
184 | test_publish_retained_2();
185 | test_publish_not_connected();
186 | test_publish_too_long();
187 | test_publish_P();
188 |
189 | FINISH
190 | }
191 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/receive_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | bool callback_called = false;
11 | char lastTopic[1024];
12 | char lastPayload[1024];
13 | unsigned int lastLength;
14 |
15 | void reset_callback() {
16 | callback_called = false;
17 | lastTopic[0] = '\0';
18 | lastPayload[0] = '\0';
19 | lastLength = 0;
20 | }
21 |
22 | void callback(char* topic, byte* payload, unsigned int length) {
23 | callback_called = true;
24 | strcpy(lastTopic,topic);
25 | memcpy(lastPayload,payload,length);
26 | lastLength = length;
27 | }
28 |
29 | int test_receive_callback() {
30 | IT("receives a callback message");
31 | reset_callback();
32 |
33 | ShimClient shimClient;
34 | shimClient.setAllowConnect(true);
35 |
36 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
37 | shimClient.respond(connack,4);
38 |
39 | PubSubClient client(server, 1883, callback, shimClient);
40 | int rc = client.connect((char*)"client_test1");
41 | IS_TRUE(rc);
42 |
43 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
44 | shimClient.respond(publish,16);
45 |
46 | rc = client.loop();
47 |
48 | IS_TRUE(rc);
49 |
50 | IS_TRUE(callback_called);
51 | IS_TRUE(strcmp(lastTopic,"topic")==0);
52 | IS_TRUE(memcmp(lastPayload,"payload",7)==0);
53 | IS_TRUE(lastLength == 7);
54 |
55 | IS_FALSE(shimClient.error());
56 |
57 | END_IT
58 | }
59 |
60 | int test_receive_stream() {
61 | IT("receives a streamed callback message");
62 | reset_callback();
63 |
64 | Stream stream;
65 | stream.expect((uint8_t*)"payload",7);
66 |
67 | ShimClient shimClient;
68 | shimClient.setAllowConnect(true);
69 |
70 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
71 | shimClient.respond(connack,4);
72 |
73 | PubSubClient client(server, 1883, callback, shimClient, stream);
74 | int rc = client.connect((char*)"client_test1");
75 | IS_TRUE(rc);
76 |
77 | byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
78 | shimClient.respond(publish,16);
79 |
80 | rc = client.loop();
81 |
82 | IS_TRUE(rc);
83 |
84 | IS_TRUE(callback_called);
85 | IS_TRUE(strcmp(lastTopic,"topic")==0);
86 | IS_TRUE(lastLength == 7);
87 |
88 | IS_FALSE(stream.error());
89 | IS_FALSE(shimClient.error());
90 |
91 | END_IT
92 | }
93 |
94 | int test_receive_max_sized_message() {
95 | IT("receives an max-sized message");
96 | reset_callback();
97 |
98 | ShimClient shimClient;
99 | shimClient.setAllowConnect(true);
100 |
101 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
102 | shimClient.respond(connack,4);
103 |
104 | PubSubClient client(server, 1883, callback, shimClient);
105 | int rc = client.connect((char*)"client_test1");
106 | IS_TRUE(rc);
107 |
108 | int length = MQTT_MAX_PACKET_SIZE;
109 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
110 | byte bigPublish[length];
111 | memset(bigPublish,'A',length);
112 | bigPublish[length] = 'B';
113 | memcpy(bigPublish,publish,16);
114 | shimClient.respond(bigPublish,length);
115 |
116 | rc = client.loop();
117 |
118 | IS_TRUE(rc);
119 |
120 | IS_TRUE(callback_called);
121 | IS_TRUE(strcmp(lastTopic,"topic")==0);
122 | IS_TRUE(lastLength == length-9);
123 | IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0);
124 |
125 | IS_FALSE(shimClient.error());
126 |
127 | END_IT
128 | }
129 |
130 | int test_receive_oversized_message() {
131 | IT("drops an oversized message");
132 | reset_callback();
133 |
134 | ShimClient shimClient;
135 | shimClient.setAllowConnect(true);
136 |
137 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
138 | shimClient.respond(connack,4);
139 |
140 | PubSubClient client(server, 1883, callback, shimClient);
141 | int rc = client.connect((char*)"client_test1");
142 | IS_TRUE(rc);
143 |
144 | int length = MQTT_MAX_PACKET_SIZE+1;
145 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
146 | byte bigPublish[length];
147 | memset(bigPublish,'A',length);
148 | bigPublish[length] = 'B';
149 | memcpy(bigPublish,publish,16);
150 | shimClient.respond(bigPublish,length);
151 |
152 | rc = client.loop();
153 |
154 | IS_TRUE(rc);
155 |
156 | IS_FALSE(callback_called);
157 |
158 | IS_FALSE(shimClient.error());
159 |
160 | END_IT
161 | }
162 |
163 | int test_receive_oversized_stream_message() {
164 | IT("drops an oversized message");
165 | reset_callback();
166 |
167 | Stream stream;
168 |
169 | ShimClient shimClient;
170 | shimClient.setAllowConnect(true);
171 |
172 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
173 | shimClient.respond(connack,4);
174 |
175 | PubSubClient client(server, 1883, callback, shimClient, stream);
176 | int rc = client.connect((char*)"client_test1");
177 | IS_TRUE(rc);
178 |
179 | int length = MQTT_MAX_PACKET_SIZE+1;
180 | byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
181 |
182 | byte bigPublish[length];
183 | memset(bigPublish,'A',length);
184 | bigPublish[length] = 'B';
185 | memcpy(bigPublish,publish,16);
186 |
187 | shimClient.respond(bigPublish,length);
188 | stream.expect(bigPublish+9,length-9);
189 |
190 | rc = client.loop();
191 |
192 | IS_TRUE(rc);
193 |
194 | IS_TRUE(callback_called);
195 | IS_TRUE(strcmp(lastTopic,"topic")==0);
196 | IS_TRUE(lastLength == length-9);
197 |
198 | IS_FALSE(stream.error());
199 | IS_FALSE(shimClient.error());
200 |
201 | END_IT
202 | }
203 |
204 | int test_receive_qos1() {
205 | IT("receives a qos1 message");
206 | reset_callback();
207 |
208 | ShimClient shimClient;
209 | shimClient.setAllowConnect(true);
210 |
211 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
212 | shimClient.respond(connack,4);
213 |
214 | PubSubClient client(server, 1883, callback, shimClient);
215 | int rc = client.connect((char*)"client_test1");
216 | IS_TRUE(rc);
217 |
218 | byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
219 | shimClient.respond(publish,18);
220 |
221 | byte puback[] = {0x40,0x2,0x12,0x34};
222 | shimClient.expect(puback,4);
223 |
224 | rc = client.loop();
225 |
226 | IS_TRUE(rc);
227 |
228 | IS_TRUE(callback_called);
229 | IS_TRUE(strcmp(lastTopic,"topic")==0);
230 | IS_TRUE(memcmp(lastPayload,"payload",7)==0);
231 | IS_TRUE(lastLength == 7);
232 |
233 | IS_FALSE(shimClient.error());
234 |
235 | END_IT
236 | }
237 |
238 | int main()
239 | {
240 | SUITE("Receive");
241 | test_receive_callback();
242 | test_receive_stream();
243 | test_receive_max_sized_message();
244 | test_receive_oversized_message();
245 | test_receive_oversized_stream_message();
246 | test_receive_qos1();
247 |
248 | FINISH
249 | }
250 |
--------------------------------------------------------------------------------
/PubSubClient/tests/src/subscribe_spec.cpp:
--------------------------------------------------------------------------------
1 | #include "PubSubClient.h"
2 | #include "ShimClient.h"
3 | #include "Buffer.h"
4 | #include "BDDTest.h"
5 | #include "trace.h"
6 |
7 |
8 | byte server[] = { 172, 16, 0, 2 };
9 |
10 | void callback(char* topic, byte* payload, unsigned int length) {
11 | // handle message arrived
12 | }
13 |
14 | int test_subscribe_no_qos() {
15 | IT("subscribe without qos defaults to 0");
16 | ShimClient shimClient;
17 | shimClient.setAllowConnect(true);
18 |
19 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
20 | shimClient.respond(connack,4);
21 |
22 | PubSubClient client(server, 1883, callback, shimClient);
23 | int rc = client.connect((char*)"client_test1");
24 | IS_TRUE(rc);
25 |
26 | byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x0 };
27 | shimClient.expect(subscribe,12);
28 | byte suback[] = { 0x90,0x3,0x0,0x2,0x0 };
29 | shimClient.respond(suback,5);
30 |
31 | rc = client.subscribe((char*)"topic");
32 | IS_TRUE(rc);
33 |
34 | IS_FALSE(shimClient.error());
35 |
36 | END_IT
37 | }
38 |
39 | int test_subscribe_qos_1() {
40 | IT("subscribes qos 1");
41 | ShimClient shimClient;
42 | shimClient.setAllowConnect(true);
43 |
44 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
45 | shimClient.respond(connack,4);
46 |
47 | PubSubClient client(server, 1883, callback, shimClient);
48 | int rc = client.connect((char*)"client_test1");
49 | IS_TRUE(rc);
50 |
51 | byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1 };
52 | shimClient.expect(subscribe,12);
53 | byte suback[] = { 0x90,0x3,0x0,0x2,0x1 };
54 | shimClient.respond(suback,5);
55 |
56 | rc = client.subscribe((char*)"topic",1);
57 | IS_TRUE(rc);
58 |
59 | IS_FALSE(shimClient.error());
60 |
61 | END_IT
62 | }
63 |
64 | int test_subscribe_not_connected() {
65 | IT("subscribe fails when not connected");
66 | ShimClient shimClient;
67 |
68 | PubSubClient client(server, 1883, callback, shimClient);
69 |
70 | int rc = client.subscribe((char*)"topic");
71 | IS_FALSE(rc);
72 |
73 | IS_FALSE(shimClient.error());
74 |
75 | END_IT
76 | }
77 |
78 | int test_subscribe_invalid_qos() {
79 | IT("subscribe fails with invalid qos values");
80 | ShimClient shimClient;
81 | shimClient.setAllowConnect(true);
82 |
83 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
84 | shimClient.respond(connack,4);
85 |
86 | PubSubClient client(server, 1883, callback, shimClient);
87 | int rc = client.connect((char*)"client_test1");
88 | IS_TRUE(rc);
89 |
90 | rc = client.subscribe((char*)"topic",2);
91 | IS_FALSE(rc);
92 | rc = client.subscribe((char*)"topic",254);
93 | IS_FALSE(rc);
94 |
95 | IS_FALSE(shimClient.error());
96 |
97 | END_IT
98 | }
99 |
100 | int test_subscribe_too_long() {
101 | IT("subscribe fails with too long topic");
102 | ShimClient shimClient;
103 | shimClient.setAllowConnect(true);
104 |
105 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
106 | shimClient.respond(connack,4);
107 |
108 | PubSubClient client(server, 1883, callback, shimClient);
109 | int rc = client.connect((char*)"client_test1");
110 | IS_TRUE(rc);
111 |
112 | // max length should be allowed
113 | // 0 1 2 3 4 5 6 7 8 9 0 1 2
114 | rc = client.subscribe((char*)"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
115 | IS_TRUE(rc);
116 |
117 | // 0 1 2 3 4 5 6 7 8 9 0 1 2
118 | rc = client.subscribe((char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
119 | IS_FALSE(rc);
120 |
121 | IS_FALSE(shimClient.error());
122 |
123 | END_IT
124 | }
125 |
126 |
127 | int test_unsubscribe() {
128 | IT("unsubscribes");
129 | ShimClient shimClient;
130 | shimClient.setAllowConnect(true);
131 |
132 | byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
133 | shimClient.respond(connack,4);
134 |
135 | PubSubClient client(server, 1883, callback, shimClient);
136 | int rc = client.connect((char*)"client_test1");
137 | IS_TRUE(rc);
138 |
139 | byte unsubscribe[] = { 0xA2,0x9,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63 };
140 | shimClient.expect(unsubscribe,12);
141 | byte unsuback[] = { 0xB0,0x2,0x0,0x2 };
142 | shimClient.respond(unsuback,4);
143 |
144 | rc = client.unsubscribe((char*)"topic");
145 | IS_TRUE(rc);
146 |
147 | IS_FALSE(shimClient.error());
148 |
149 | END_IT
150 | }
151 |
152 | int test_unsubscribe_not_connected() {
153 | IT("unsubscribe fails when not connected");
154 | ShimClient shimClient;
155 |
156 | PubSubClient client(server, 1883, callback, shimClient);
157 |
158 | int rc = client.unsubscribe((char*)"topic");
159 | IS_FALSE(rc);
160 |
161 | IS_FALSE(shimClient.error());
162 |
163 | END_IT
164 | }
165 |
166 | int main()
167 | {
168 | SUITE("Subscribe");
169 | test_subscribe_no_qos();
170 | test_subscribe_qos_1();
171 | test_subscribe_not_connected();
172 | test_subscribe_invalid_qos();
173 | test_subscribe_too_long();
174 | test_unsubscribe();
175 | test_unsubscribe_not_connected();
176 | FINISH
177 | }
178 |
--------------------------------------------------------------------------------
/PubSubClient/tests/testcases/mqtt_basic.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import settings
3 |
4 | import time
5 | import mosquitto
6 |
7 | import serial
8 |
9 | def on_message(mosq, obj, msg):
10 | obj.message_queue.append(msg)
11 |
12 | class mqtt_basic(unittest.TestCase):
13 |
14 | message_queue = []
15 |
16 | @classmethod
17 | def setUpClass(self):
18 | self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self)
19 | self.client.connect(settings.server_ip)
20 | self.client.on_message = on_message
21 | self.client.subscribe("outTopic",0)
22 |
23 | @classmethod
24 | def tearDownClass(self):
25 | self.client.disconnect()
26 |
27 | def test_one(self):
28 | i=30
29 | while len(self.message_queue) == 0 and i > 0:
30 | self.client.loop()
31 | time.sleep(0.5)
32 | i -= 1
33 | self.assertTrue(i>0, "message receive timed-out")
34 | self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
35 | msg = self.message_queue[0]
36 | self.assertEqual(msg.mid,0,"message id not 0")
37 | self.assertEqual(msg.topic,"outTopic","message topic incorrect")
38 | self.assertEqual(msg.payload,"hello world")
39 | self.assertEqual(msg.qos,0,"message qos not 0")
40 | self.assertEqual(msg.retain,False,"message retain flag incorrect")
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/PubSubClient/tests/testcases/mqtt_publish_in_callback.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import settings
3 |
4 | import time
5 | import mosquitto
6 |
7 | import serial
8 |
9 | def on_message(mosq, obj, msg):
10 | obj.message_queue.append(msg)
11 |
12 | class mqtt_publish_in_callback(unittest.TestCase):
13 |
14 | message_queue = []
15 |
16 | @classmethod
17 | def setUpClass(self):
18 | self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True,obj=self)
19 | self.client.connect(settings.server_ip)
20 | self.client.on_message = on_message
21 | self.client.subscribe("outTopic",0)
22 |
23 | @classmethod
24 | def tearDownClass(self):
25 | self.client.disconnect()
26 |
27 | def test_connect(self):
28 | i=30
29 | while len(self.message_queue) == 0 and i > 0:
30 | self.client.loop()
31 | time.sleep(0.5)
32 | i -= 1
33 | self.assertTrue(i>0, "message receive timed-out")
34 | self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
35 | msg = self.message_queue.pop(0)
36 | self.assertEqual(msg.mid,0,"message id not 0")
37 | self.assertEqual(msg.topic,"outTopic","message topic incorrect")
38 | self.assertEqual(msg.payload,"hello world")
39 | self.assertEqual(msg.qos,0,"message qos not 0")
40 | self.assertEqual(msg.retain,False,"message retain flag incorrect")
41 |
42 |
43 | def test_publish(self):
44 | self.assertEqual(len(self.message_queue), 0, "message queue not empty")
45 | payload = "abcdefghij"
46 | self.client.publish("inTopic",payload)
47 |
48 | i=30
49 | while len(self.message_queue) == 0 and i > 0:
50 | self.client.loop()
51 | time.sleep(0.5)
52 | i -= 1
53 |
54 | self.assertTrue(i>0, "message receive timed-out")
55 | self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
56 | msg = self.message_queue.pop(0)
57 | self.assertEqual(msg.mid,0,"message id not 0")
58 | self.assertEqual(msg.topic,"outTopic","message topic incorrect")
59 | self.assertEqual(msg.payload,payload)
60 | self.assertEqual(msg.qos,0,"message qos not 0")
61 | self.assertEqual(msg.retain,False,"message retain flag incorrect")
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/PubSubClient/tests/testcases/settings.py:
--------------------------------------------------------------------------------
1 | server_ip = "172.16.0.2"
2 | arduino_ip = "172.16.0.100"
3 |
--------------------------------------------------------------------------------
/PubSubClient/tests/testsuite.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import os.path
4 | import sys
5 | import shutil
6 | from subprocess import call
7 | import importlib
8 | import unittest
9 | import re
10 |
11 | from testcases import settings
12 |
13 | class Workspace(object):
14 |
15 | def __init__(self):
16 | self.root_dir = os.getcwd()
17 | self.build_dir = os.path.join(self.root_dir,"tmpbin");
18 | self.log_dir = os.path.join(self.root_dir,"logs");
19 | self.tests_dir = os.path.join(self.root_dir,"testcases");
20 | self.examples_dir = os.path.join(self.root_dir,"../PubSubClient/examples")
21 | self.examples = []
22 | self.tests = []
23 | if not os.path.isdir("../PubSubClient"):
24 | raise Exception("Cannot find PubSubClient library")
25 | try:
26 | import ino
27 | except:
28 | raise Exception("ino tool not installed")
29 |
30 | def init(self):
31 | if os.path.isdir(self.build_dir):
32 | shutil.rmtree(self.build_dir)
33 | os.mkdir(self.build_dir)
34 | if os.path.isdir(self.log_dir):
35 | shutil.rmtree(self.log_dir)
36 | os.mkdir(self.log_dir)
37 |
38 | os.chdir(self.build_dir)
39 | call(["ino","init"])
40 |
41 | shutil.copytree("../../PubSubClient","lib/PubSubClient")
42 |
43 | filenames = []
44 | for root, dirs, files in os.walk(self.examples_dir):
45 | filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
46 | filenames.sort()
47 | for e in filenames:
48 | self.examples.append(Sketch(self,e))
49 |
50 | filenames = []
51 | for root, dirs, files in os.walk(self.tests_dir):
52 | filenames += [os.path.join(root,f) for f in files if f.endswith(".ino")]
53 | filenames.sort()
54 | for e in filenames:
55 | self.tests.append(Sketch(self,e))
56 |
57 | def clean(self):
58 | shutil.rmtree(self.build_dir)
59 |
60 | class Sketch(object):
61 | def __init__(self,wksp,fn):
62 | self.w = wksp
63 | self.filename = fn
64 | self.basename = os.path.basename(self.filename)
65 | self.build_log = os.path.join(self.w.log_dir,"%s.log"%(os.path.basename(self.filename),))
66 | self.build_err_log = os.path.join(self.w.log_dir,"%s.err.log"%(os.path.basename(self.filename),))
67 | self.build_upload_log = os.path.join(self.w.log_dir,"%s.upload.log"%(os.path.basename(self.filename),))
68 |
69 | def build(self):
70 | sys.stdout.write(" Build: ")
71 | sys.stdout.flush()
72 |
73 | # Copy sketch over, replacing IP addresses as necessary
74 | fin = open(self.filename,"r")
75 | lines = fin.readlines()
76 | fin.close()
77 | fout = open(os.path.join(self.w.build_dir,"src","sketch.ino"),"w")
78 | for l in lines:
79 | if re.match(r"^byte server\[\] = {",l):
80 | fout.write("byte server[] = { %s };\n"%(settings.server_ip.replace(".",", "),))
81 | elif re.match(r"^byte ip\[\] = {",l):
82 | fout.write("byte ip[] = { %s };\n"%(settings.arduino_ip.replace(".",", "),))
83 | else:
84 | fout.write(l)
85 | fout.flush()
86 | fout.close()
87 |
88 | # Run build
89 | fout = open(self.build_log, "w")
90 | ferr = open(self.build_err_log, "w")
91 | rc = call(["ino","build"],stdout=fout,stderr=ferr)
92 | fout.close()
93 | ferr.close()
94 | if rc == 0:
95 | sys.stdout.write("pass")
96 | sys.stdout.write("\n")
97 | return True
98 | else:
99 | sys.stdout.write("fail")
100 | sys.stdout.write("\n")
101 | with open(self.build_err_log) as f:
102 | for line in f:
103 | print " ",line,
104 | return False
105 |
106 | def upload(self):
107 | sys.stdout.write(" Upload: ")
108 | sys.stdout.flush()
109 | fout = open(self.build_upload_log, "w")
110 | rc = call(["ino","upload"],stdout=fout,stderr=fout)
111 | fout.close()
112 | if rc == 0:
113 | sys.stdout.write("pass")
114 | sys.stdout.write("\n")
115 | return True
116 | else:
117 | sys.stdout.write("fail")
118 | sys.stdout.write("\n")
119 | with open(self.build_upload_log) as f:
120 | for line in f:
121 | print " ",line,
122 | return False
123 |
124 |
125 | def test(self):
126 | # import the matching test case, if it exists
127 | try:
128 | basename = os.path.basename(self.filename)[:-4]
129 | i = importlib.import_module("testcases."+basename)
130 | except:
131 | sys.stdout.write(" Test: no tests found")
132 | sys.stdout.write("\n")
133 | return
134 | c = getattr(i,basename)
135 |
136 | testmethods = [m for m in dir(c) if m.startswith("test_")]
137 | testmethods.sort()
138 | tests = []
139 | for m in testmethods:
140 | tests.append(c(m))
141 |
142 | result = unittest.TestResult()
143 | c.setUpClass()
144 | if self.upload():
145 | sys.stdout.write(" Test: ")
146 | sys.stdout.flush()
147 | for t in tests:
148 | t.run(result)
149 | print "%d/%d"%(result.testsRun-len(result.failures)-len(result.errors),result.testsRun)
150 | if not result.wasSuccessful():
151 | if len(result.failures) > 0:
152 | for f in result.failures:
153 | print "-- %s"%(str(f[0]),)
154 | print f[1]
155 | if len(result.errors) > 0:
156 | print " Errors:"
157 | for f in result.errors:
158 | print "-- %s"%(str(f[0]),)
159 | print f[1]
160 | c.tearDownClass()
161 |
162 | if __name__ == '__main__':
163 | run_tests = True
164 |
165 | w = Workspace()
166 | w.init()
167 |
168 | for e in w.examples:
169 | print "--------------------------------------"
170 | print "[%s]"%(e.basename,)
171 | if e.build() and run_tests:
172 | e.test()
173 | for e in w.tests:
174 | print "--------------------------------------"
175 | print "[%s]"%(e.basename,)
176 | if e.build() and run_tests:
177 | e.test()
178 |
179 | w.clean()
180 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
ESP8266 http Web and MQTT Server: Update 2017-04-26
2 |
3 | In addition to the capabilities noted below, this project has been recently updated to support secure TLS connections to an MQTT broker. The revised sketch and supporting files are in the folder "http_mqtt_server_SSL". Setup is the same except the compiler directive "MQTT_SVR_ENABLE" is no longer used.
4 |
5 | This sketch was compiled using Arduino IDE 1.8.2.
6 |
7 |
ESP8266 http Web and MQTT Server: Original Release 2016-01-06
8 |
9 | This project provides a Web Server Framework supporting http and MQTT requests.
10 |
11 | Features Include:
12 |
13 | 1. ESP8266 Analog Reads (8 with multiplexer)
14 | 2. Arduino Serial Port Interface to access Analog and Digital resources
15 | 3. Webpage Configuration Control stored in ESP8266 EEPROM
16 |
17 |
Setup:
18 |
19 | 1. Copy the http_mqtt_server folder to your Arduino sketch folder.
20 | 2. Copy the UtilityFunctions folder to your Arduino libraries folder.
21 | 3. Copy the webserver folder to your Arduino libraries folder.
22 | 4. Change the following in the http_mqtt_server sketch to match your network settings:
23 |
24 |
25 | char init_ssid[32] = "YOURWIFISSID";
26 | char init_pass[32] = "YOURWIFIPASSWORD";
27 | //Your assigned ESP8266 static IP
28 | const char* init_ip0 = "192";
29 | const char* init_ip1 = "168";
30 | const char* init_ip2 = "0";
31 | const char* init_ip3 = "132";
32 | .
33 | //Your assigned WIFI LAN router IP
34 | const char* init_gw0 = "192";
35 | const char* init_gw1 = "168";
36 | const char* init_gw2 = "0";
37 | const char* init_gw3 = "1";
38 | .
39 | //Web Server port
40 | const char* init_port = "9701";
41 | //Your MQTT broker
42 | const char* init_bk = "test.mosquitto.org";
43 |
44 |
45 |
Server Setting:
46 |
47 | If you wish to disable the MQTT server, change the following in sketch.h
48 |
49 | from:
50 |
51 | define MQTT_SVR_ENABLE 1
52 |
53 | to:
54 |
55 | define MQTT_SVR_ENABLE 0
56 |
57 |
Operation:
58 |
59 | The ESP8266 performs as a server, receiving URL commands and either:
60 |
61 | 1. Sets or gets the requested ESP8266 resource and returns the appropriate reply.
62 | 2. Creates a small request string and forwards the request to an Arduino via the serial interface.
63 |
64 | In order to test this, the ESP8266 Tx needs to be connected to an Arduino Rx.
65 | Likewise, the ESP8266 Rx needs to be connected to an Arduino Tx.
66 |
67 | The subject Arduino should have the sketch "ArduinoHomeAutomation.ino" installed and running.
68 | This sketch uses the Arduino software serial interface using digital pins 10 and 11. It was
69 | tested using an Arduino nano. Using an Arduino with more serial ports, such as a MEGA can be used
70 | and utilize the built-in hardware serial port. This would require some adjustment to the sketch.
71 |
72 |
Web Server test:
73 |
74 | With both the Arduino and ESP8266 connected and running,
75 |
76 | DIGITAL CHANNEL GET:
77 |
78 | Enter the following URL in a web browser (adjust IP & port to your settings):
79 |
80 | http://192.168.0.132:9701/?arduino=GetDigital&chan=04
81 |
82 | The returned value in the web browser should be:
83 |
84 | Digital Channel 04 is LO
85 |
86 | DIGITAL CHANNEL SET:
87 |
88 | Now set the channel HI by entering the following URL:
89 |
90 | http://192.168.0.132:9701/?arduino=SetDigital&chan=04&state=1
91 |
92 | The returned value in the web browser should be:
93 |
94 | Digital Channel 04 is HI
95 |
96 | And if something is connected to channel 4, such as an LED, it should illuminate with this command.
97 |
98 | ANALOG CHANNEL GET:
99 |
100 | Enter the following URL to get the Arduino Analog channel reading:
101 |
102 | http://192.168.0.132:9701/?arduino=GetAnalog&chan=04
103 |
104 | The returned value in the web browser should be similar to (units returned are volts):
105 |
106 | Analog Channel 04 is 2.48
107 |
108 |
Expansion:
109 |
110 | Simply add the input/output configuration in your Arduino sketch setup() function to enable
111 | additional resource control such as 1-wire or i2c connected devices.
112 |
113 |
Web configuration test:
114 |
115 | This sketch is provided with the operation of loading the ESP8266 internal EEPROM with the default
116 | values hard-coded in the sketch.
117 |
118 | Once the sketch has been loaded and executed at least once, the EEPROM is set as needed. In order to
119 | disable the initialization EEPROM writes so that the EEPROM values are used, the following line
120 | in sketch.h needs to be revised:
121 |
122 | From:
123 |
124 | define EEPROM_INIT 1
125 |
126 | To:
127 |
128 | define EEPROM_INIT 0
129 |
130 | With this change, the sketch will obviously need to be reloaded into the ESP8266.
131 |
132 | This will allow configuration changes to be made using the sketch's build-in configuration webpage.
133 |
134 | In order to access the configuration screen, enter the following URL into a web browser:
135 |
136 | http://192.168.0.132:9701/config
137 |
138 | Once you have entered any desired changes, click on the SAVE button to write these new values to
139 | EEPROM, They will used the next time the ESP8266 is started. Clicking on the RESET ESP8266 button will
140 | restart it.
141 |
142 |
Configuration in AP mode:
143 |
144 | If the ESP8266 is moved to a WIFI with a different ssid/password, it will not be able to connect using
145 | it's current settings. Upon startup, the ESP attempts to connect to the Wifi that is stored in EEPROM. This
146 | attempts are aborted if unsucessful after 10 seconds. The ESP will then start up in AP mode. This provides
147 | a method of accessing the ESP to modify the Wifi parameters as needed. To access the ESP in AP mode,
148 | connect to the ESP with the ssid "_AP". Then simply open a web browser (from computer,
149 | tablet, or smartphone) and enter:
150 |
151 | http://192.168.4.1:9701/config
152 |
153 | This will open the configuration panel. The AP IP "192.168.4.1" can also be modified through the configuration
154 | panel..
155 |
156 |