mirror of
https://github.com/schinken/esp8266-geigercounter.git
synced 2024-11-21 16:10:20 +01:00
Major Rewrite
This commit is contained in:
parent
0c10ef4ba9
commit
8a47d2a644
56 changed files with 286 additions and 5678 deletions
20
README.md
20
README.md
|
@ -8,11 +8,27 @@ Here's some code and schematic to connect your mightyohm.com geiger counter to t
|
||||||
|
|
||||||
## compile
|
## compile
|
||||||
|
|
||||||
To compile this code, just clone or download the master branch as a zip and ramp up your arduino library. For this code to work, you need the SimpleTimer-library (http://playground.arduino.cc/Code/SimpleTimer#Download) and the ESP8266 added to your Arduino-IDE as a board (https://github.com/esp8266/Arduino, see install instructions)
|
The firmware can be built and flashed using the Arduino IDE.
|
||||||
|
|
||||||
|
For this, you will need to add ESP8266 support to it by [using the Boards Manager](https://github.com/esp8266/Arduino#installing-with-boards-manager).
|
||||||
|
|
||||||
|
Furthermore, you will also need to install the following libraries using the Library Manager:
|
||||||
|
|
||||||
|
* ArduinoJSON 6.10.1
|
||||||
|
* PubSubClient 2.8.0
|
||||||
|
* WiFiManager 0.15.0
|
||||||
|
|
||||||
## settings
|
## settings
|
||||||
|
|
||||||
You need to adjust the settings in the settings.h file.
|
Since this project is using the WifiManager library, the ESP8266 will open up a WiFi Access Point for its initial configuration
|
||||||
|
or if it is unable to connect to the previously configured WiFi.
|
||||||
|
|
||||||
|
The library pretends that said WiFi AP requires a captive portal which triggers a notification on recent android phones.
|
||||||
|
Simply connect to the AP with your phone, tap the "Login required"-notification and you should be able to configure everything.
|
||||||
|
|
||||||
|
## usage
|
||||||
|
|
||||||
|
Since we're using the Home Assistant Autodiscovery feature, everything should just work™.
|
||||||
|
|
||||||
## license and credits
|
## license and credits
|
||||||
|
|
||||||
|
|
37
configFileHandling.ino
Normal file
37
configFileHandling.ino
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
void saveConfig() {
|
||||||
|
DynamicJsonDocument json(512);
|
||||||
|
json["mqtt_server"] = mqtt_server;
|
||||||
|
json["username"] = username;
|
||||||
|
json["password"] = password;
|
||||||
|
|
||||||
|
File configFile = SPIFFS.open("/config.json", "w");
|
||||||
|
if (!configFile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serializeJson(json, configFile);
|
||||||
|
configFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadConfig() {
|
||||||
|
if (SPIFFS.begin()) {
|
||||||
|
|
||||||
|
if (SPIFFS.exists("/config.json")) {
|
||||||
|
File configFile = SPIFFS.open("/config.json", "r");
|
||||||
|
|
||||||
|
if (configFile) {
|
||||||
|
const size_t size = configFile.size();
|
||||||
|
std::unique_ptr<char[]> buf(new char[size]);
|
||||||
|
|
||||||
|
configFile.readBytes(buf.get(), size);
|
||||||
|
DynamicJsonDocument json(512);
|
||||||
|
|
||||||
|
if (DeserializationError::Ok == deserializeJson(json, buf.get())) {
|
||||||
|
strcpy(mqtt_server, json["mqtt_server"]);
|
||||||
|
strcpy(username, json["username"]);
|
||||||
|
strcpy(password, json["password"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
# SimpleTimer for Arduino
|
|
||||||
|
|
||||||
This is a fork of the SimpleTimer library from Marcello Romani (http://playground.arduino.cc/Code/SimpleTimer). Sadly there is no original repository to apply pull requests.
|
|
||||||
|
|
||||||
## Additional Features
|
|
||||||
|
|
||||||
* Support lambda-expressions / std::function callbacks
|
|
|
@ -1,241 +0,0 @@
|
||||||
/*
|
|
||||||
* SimpleTimer.cpp
|
|
||||||
*
|
|
||||||
* SimpleTimer - A timer library for Arduino.
|
|
||||||
* Author: mromani@ottotecnica.com
|
|
||||||
* Copyright (c) 2010 OTTOTECNICA Italy
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it
|
|
||||||
* and/or modify it under the terms of the GNU Lesser
|
|
||||||
* General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 2.1 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will
|
|
||||||
* be useful, but WITHOUT ANY WARRANTY; without even the
|
|
||||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser
|
|
||||||
* General Public License along with this library; if not,
|
|
||||||
* write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "SimpleTimer.h"
|
|
||||||
|
|
||||||
|
|
||||||
// Select time function:
|
|
||||||
//static inline unsigned long elapsed() { return micros(); }
|
|
||||||
static inline unsigned long elapsed() { return millis(); }
|
|
||||||
|
|
||||||
|
|
||||||
SimpleTimer::SimpleTimer() {
|
|
||||||
unsigned long current_millis = elapsed();
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_TIMERS; i++) {
|
|
||||||
enabled[i] = false;
|
|
||||||
callbacks[i] = 0; // if the callback pointer is zero, the slot is free, i.e. doesn't "contain" any timer
|
|
||||||
prev_millis[i] = current_millis;
|
|
||||||
numRuns[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
numTimers = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SimpleTimer::run() {
|
|
||||||
int i;
|
|
||||||
unsigned long current_millis;
|
|
||||||
|
|
||||||
// get current time
|
|
||||||
current_millis = elapsed();
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_TIMERS; i++) {
|
|
||||||
|
|
||||||
toBeCalled[i] = DEFCALL_DONTRUN;
|
|
||||||
|
|
||||||
// no callback == no timer, i.e. jump over empty slots
|
|
||||||
if (callbacks[i]) {
|
|
||||||
|
|
||||||
// is it time to process this timer ?
|
|
||||||
// see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592
|
|
||||||
|
|
||||||
if (current_millis - prev_millis[i] >= delays[i]) {
|
|
||||||
|
|
||||||
// update time
|
|
||||||
//prev_millis[i] = current_millis;
|
|
||||||
prev_millis[i] += delays[i];
|
|
||||||
|
|
||||||
// check if the timer callback has to be executed
|
|
||||||
if (enabled[i]) {
|
|
||||||
|
|
||||||
// "run forever" timers must always be executed
|
|
||||||
if (maxNumRuns[i] == RUN_FOREVER) {
|
|
||||||
toBeCalled[i] = DEFCALL_RUNONLY;
|
|
||||||
}
|
|
||||||
// other timers get executed the specified number of times
|
|
||||||
else if (numRuns[i] < maxNumRuns[i]) {
|
|
||||||
toBeCalled[i] = DEFCALL_RUNONLY;
|
|
||||||
numRuns[i]++;
|
|
||||||
|
|
||||||
// after the last run, delete the timer
|
|
||||||
if (numRuns[i] >= maxNumRuns[i]) {
|
|
||||||
toBeCalled[i] = DEFCALL_RUNANDDEL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_TIMERS; i++) {
|
|
||||||
switch(toBeCalled[i]) {
|
|
||||||
case DEFCALL_DONTRUN:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DEFCALL_RUNONLY:
|
|
||||||
callbacks[i]();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DEFCALL_RUNANDDEL:
|
|
||||||
callbacks[i]();
|
|
||||||
deleteTimer(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// find the first available slot
|
|
||||||
// return -1 if none found
|
|
||||||
int SimpleTimer::findFirstFreeSlot() {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// all slots are used
|
|
||||||
if (numTimers >= MAX_TIMERS) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the first slot with no callback (i.e. free)
|
|
||||||
for (i = 0; i < MAX_TIMERS; i++) {
|
|
||||||
if (callbacks[i] == 0) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// no free slots found
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SimpleTimer::setTimer(unsigned long d, timer_callback f, int n) {
|
|
||||||
int freeTimer;
|
|
||||||
|
|
||||||
freeTimer = findFirstFreeSlot();
|
|
||||||
if (freeTimer < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
delays[freeTimer] = d;
|
|
||||||
callbacks[freeTimer] = f;
|
|
||||||
maxNumRuns[freeTimer] = n;
|
|
||||||
enabled[freeTimer] = true;
|
|
||||||
prev_millis[freeTimer] = elapsed();
|
|
||||||
|
|
||||||
numTimers++;
|
|
||||||
|
|
||||||
return freeTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SimpleTimer::setInterval(unsigned long d, timer_callback f) {
|
|
||||||
return setTimer(d, f, RUN_FOREVER);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SimpleTimer::setTimeout(unsigned long d, timer_callback f) {
|
|
||||||
return setTimer(d, f, RUN_ONCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SimpleTimer::deleteTimer(int timerId) {
|
|
||||||
if (timerId >= MAX_TIMERS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// nothing to delete if no timers are in use
|
|
||||||
if (numTimers == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't decrease the number of timers if the
|
|
||||||
// specified slot is already empty
|
|
||||||
if (callbacks[timerId] != NULL) {
|
|
||||||
callbacks[timerId] = 0;
|
|
||||||
enabled[timerId] = false;
|
|
||||||
toBeCalled[timerId] = DEFCALL_DONTRUN;
|
|
||||||
delays[timerId] = 0;
|
|
||||||
numRuns[timerId] = 0;
|
|
||||||
|
|
||||||
// update number of timers
|
|
||||||
numTimers--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// function contributed by code@rowansimms.com
|
|
||||||
void SimpleTimer::restartTimer(int numTimer) {
|
|
||||||
if (numTimer >= MAX_TIMERS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev_millis[numTimer] = elapsed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean SimpleTimer::isEnabled(int numTimer) {
|
|
||||||
if (numTimer >= MAX_TIMERS) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return enabled[numTimer];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SimpleTimer::enable(int numTimer) {
|
|
||||||
if (numTimer >= MAX_TIMERS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled[numTimer] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SimpleTimer::disable(int numTimer) {
|
|
||||||
if (numTimer >= MAX_TIMERS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled[numTimer] = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void SimpleTimer::toggle(int numTimer) {
|
|
||||||
if (numTimer >= MAX_TIMERS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enabled[numTimer] = !enabled[numTimer];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int SimpleTimer::getNumTimers() {
|
|
||||||
return numTimers;
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* SimpleTimer.h
|
|
||||||
*
|
|
||||||
* SimpleTimer - A timer library for Arduino.
|
|
||||||
* Author: mromani@ottotecnica.com
|
|
||||||
* Copyright (c) 2010 OTTOTECNICA Italy
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it
|
|
||||||
* and/or modify it under the terms of the GNU Lesser
|
|
||||||
* General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 2.1 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will
|
|
||||||
* be useful, but WITHOUT ANY WARRANTY; without even the
|
|
||||||
* implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser
|
|
||||||
* General Public License along with this library; if not,
|
|
||||||
* write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef SIMPLETIMER_H
|
|
||||||
#define SIMPLETIMER_H
|
|
||||||
|
|
||||||
#ifndef __AVR__
|
|
||||||
#include <functional>
|
|
||||||
#endif // __AVR__
|
|
||||||
|
|
||||||
#if defined(ARDUINO) && ARDUINO >= 100
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <WProgram.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __AVR__
|
|
||||||
typedef std::function<void(void)> timer_callback;
|
|
||||||
#else
|
|
||||||
typedef void (*timer_callback)();
|
|
||||||
#endif // __AVR__
|
|
||||||
|
|
||||||
class SimpleTimer {
|
|
||||||
|
|
||||||
public:
|
|
||||||
// maximum number of timers
|
|
||||||
const static int MAX_TIMERS = 10;
|
|
||||||
|
|
||||||
// setTimer() constants
|
|
||||||
const static int RUN_FOREVER = 0;
|
|
||||||
const static int RUN_ONCE = 1;
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
SimpleTimer();
|
|
||||||
|
|
||||||
// this function must be called inside loop()
|
|
||||||
void run();
|
|
||||||
|
|
||||||
// call function f every d milliseconds
|
|
||||||
int setInterval(unsigned long d, timer_callback f);
|
|
||||||
|
|
||||||
// call function f once after d milliseconds
|
|
||||||
int setTimeout(unsigned long d, timer_callback f);
|
|
||||||
|
|
||||||
// call function f every d milliseconds for n times
|
|
||||||
int setTimer(unsigned long d, timer_callback f, int n);
|
|
||||||
|
|
||||||
// destroy the specified timer
|
|
||||||
void deleteTimer(int numTimer);
|
|
||||||
|
|
||||||
// restart the specified timer
|
|
||||||
void restartTimer(int numTimer);
|
|
||||||
|
|
||||||
// returns true if the specified timer is enabled
|
|
||||||
boolean isEnabled(int numTimer);
|
|
||||||
|
|
||||||
// enables the specified timer
|
|
||||||
void enable(int numTimer);
|
|
||||||
|
|
||||||
// disables the specified timer
|
|
||||||
void disable(int numTimer);
|
|
||||||
|
|
||||||
// enables the specified timer if it's currently disabled,
|
|
||||||
// and vice-versa
|
|
||||||
void toggle(int numTimer);
|
|
||||||
|
|
||||||
// returns the number of used timers
|
|
||||||
int getNumTimers();
|
|
||||||
|
|
||||||
// returns the number of available timers
|
|
||||||
int getNumAvailableTimers() { return MAX_TIMERS - numTimers; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
// deferred call constants
|
|
||||||
const static int DEFCALL_DONTRUN = 0; // don't call the callback function
|
|
||||||
const static int DEFCALL_RUNONLY = 1; // call the callback function but don't delete the timer
|
|
||||||
const static int DEFCALL_RUNANDDEL = 2; // call the callback function and delete the timer
|
|
||||||
|
|
||||||
// find the first available slot
|
|
||||||
int findFirstFreeSlot();
|
|
||||||
|
|
||||||
// value returned by the millis() function
|
|
||||||
// in the previous run() call
|
|
||||||
unsigned long prev_millis[MAX_TIMERS];
|
|
||||||
|
|
||||||
// pointers to the callback functions
|
|
||||||
timer_callback callbacks[MAX_TIMERS];
|
|
||||||
|
|
||||||
// delay values
|
|
||||||
unsigned long delays[MAX_TIMERS];
|
|
||||||
|
|
||||||
// number of runs to be executed for each timer
|
|
||||||
int maxNumRuns[MAX_TIMERS];
|
|
||||||
|
|
||||||
// number of executed runs for each timer
|
|
||||||
int numRuns[MAX_TIMERS];
|
|
||||||
|
|
||||||
// which timers are enabled
|
|
||||||
boolean enabled[MAX_TIMERS];
|
|
||||||
|
|
||||||
// deferred function call (sort of) - N.B.: this array is only used in run()
|
|
||||||
int toBeCalled[MAX_TIMERS];
|
|
||||||
|
|
||||||
// actual number of timers in use
|
|
||||||
int numTimers;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,22 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2015 tzapu
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
|
@ -1,847 +0,0 @@
|
||||||
/**************************************************************
|
|
||||||
WiFiManager is a library for the ESP8266/Arduino platform
|
|
||||||
(https://github.com/esp8266/Arduino) to enable easy
|
|
||||||
configuration and reconfiguration of WiFi credentials using a Captive Portal
|
|
||||||
inspired by:
|
|
||||||
http://www.esp8266.com/viewtopic.php?f=29&t=2520
|
|
||||||
https://github.com/chriscook8/esp-arduino-apboot
|
|
||||||
https://github.com/esp8266/Arduino/tree/master/libraries/DNSServer/examples/CaptivePortalAdvanced
|
|
||||||
Built by AlexT https://github.com/tzapu
|
|
||||||
Licensed under MIT license
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
#include "WiFiManager.h"
|
|
||||||
|
|
||||||
WiFiManagerParameter::WiFiManagerParameter(const char *custom) {
|
|
||||||
_id = NULL;
|
|
||||||
_placeholder = NULL;
|
|
||||||
_length = 0;
|
|
||||||
_value = NULL;
|
|
||||||
|
|
||||||
_customHTML = custom;
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiManagerParameter::WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length) {
|
|
||||||
init(id, placeholder, defaultValue, length, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiManagerParameter::WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom) {
|
|
||||||
init(id, placeholder, defaultValue, length, custom);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManagerParameter::init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom) {
|
|
||||||
_id = id;
|
|
||||||
_placeholder = placeholder;
|
|
||||||
_length = length;
|
|
||||||
_value = new char[length + 1];
|
|
||||||
for (int i = 0; i < length + 1; i++) {
|
|
||||||
_value[i] = 0;
|
|
||||||
}
|
|
||||||
if (defaultValue != NULL) {
|
|
||||||
strncpy(_value, defaultValue, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
_customHTML = custom;
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiManagerParameter::~WiFiManagerParameter() {
|
|
||||||
if (_value != NULL) {
|
|
||||||
delete[] _value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* WiFiManagerParameter::getValue() {
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
const char* WiFiManagerParameter::getID() {
|
|
||||||
return _id;
|
|
||||||
}
|
|
||||||
const char* WiFiManagerParameter::getPlaceholder() {
|
|
||||||
return _placeholder;
|
|
||||||
}
|
|
||||||
int WiFiManagerParameter::getValueLength() {
|
|
||||||
return _length;
|
|
||||||
}
|
|
||||||
const char* WiFiManagerParameter::getCustomHTML() {
|
|
||||||
return _customHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WiFiManager::WiFiManager() {
|
|
||||||
_max_params = WIFI_MANAGER_MAX_PARAMS;
|
|
||||||
_params = (WiFiManagerParameter**)malloc(_max_params * sizeof(WiFiManagerParameter*));
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFiManager::~WiFiManager()
|
|
||||||
{
|
|
||||||
if (_params != NULL)
|
|
||||||
{
|
|
||||||
DEBUG_WM(F("freeing allocated params!"));
|
|
||||||
free(_params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WiFiManager::addParameter(WiFiManagerParameter *p) {
|
|
||||||
if(_paramsCount + 1 > _max_params)
|
|
||||||
{
|
|
||||||
// rezise the params array
|
|
||||||
_max_params += WIFI_MANAGER_MAX_PARAMS;
|
|
||||||
DEBUG_WM(F("Increasing _max_params to:"));
|
|
||||||
DEBUG_WM(_max_params);
|
|
||||||
WiFiManagerParameter** new_params = (WiFiManagerParameter**)realloc(_params, _max_params * sizeof(WiFiManagerParameter*));
|
|
||||||
if (new_params != NULL) {
|
|
||||||
_params = new_params;
|
|
||||||
} else {
|
|
||||||
DEBUG_WM(F("ERROR: failed to realloc params, size not increased!"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_params[_paramsCount] = p;
|
|
||||||
_paramsCount++;
|
|
||||||
DEBUG_WM(F("Adding parameter"));
|
|
||||||
DEBUG_WM(p->getID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setupConfigPortal() {
|
|
||||||
dnsServer.reset(new DNSServer());
|
|
||||||
server.reset(new ESP8266WebServer(80));
|
|
||||||
|
|
||||||
DEBUG_WM(F(""));
|
|
||||||
_configPortalStart = millis();
|
|
||||||
|
|
||||||
DEBUG_WM(F("Configuring access point... "));
|
|
||||||
DEBUG_WM(_apName);
|
|
||||||
if (_apPassword != NULL) {
|
|
||||||
if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63) {
|
|
||||||
// fail passphrase to short or long!
|
|
||||||
DEBUG_WM(F("Invalid AccessPoint password. Ignoring"));
|
|
||||||
_apPassword = NULL;
|
|
||||||
}
|
|
||||||
DEBUG_WM(_apPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
//optional soft ip config
|
|
||||||
if (_ap_static_ip) {
|
|
||||||
DEBUG_WM(F("Custom AP IP/GW/Subnet"));
|
|
||||||
WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_apPassword != NULL) {
|
|
||||||
WiFi.softAP(_apName, _apPassword);//password option
|
|
||||||
} else {
|
|
||||||
WiFi.softAP(_apName);
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(500); // Without delay I've seen the IP address blank
|
|
||||||
DEBUG_WM(F("AP IP address: "));
|
|
||||||
DEBUG_WM(WiFi.softAPIP());
|
|
||||||
|
|
||||||
/* Setup the DNS server redirecting all the domains to the apIP */
|
|
||||||
dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
|
|
||||||
dnsServer->start(DNS_PORT, "*", WiFi.softAPIP());
|
|
||||||
|
|
||||||
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */
|
|
||||||
server->on(String(F("/")).c_str(), std::bind(&WiFiManager::handleRoot, this));
|
|
||||||
server->on(String(F("/wifi")).c_str(), std::bind(&WiFiManager::handleWifi, this, true));
|
|
||||||
server->on(String(F("/0wifi")).c_str(), std::bind(&WiFiManager::handleWifi, this, false));
|
|
||||||
server->on(String(F("/wifisave")).c_str(), std::bind(&WiFiManager::handleWifiSave, this));
|
|
||||||
server->on(String(F("/i")).c_str(), std::bind(&WiFiManager::handleInfo, this));
|
|
||||||
server->on(String(F("/r")).c_str(), std::bind(&WiFiManager::handleReset, this));
|
|
||||||
//server->on("/generate_204", std::bind(&WiFiManager::handle204, this)); //Android/Chrome OS captive portal check.
|
|
||||||
server->on(String(F("/fwlink")).c_str(), std::bind(&WiFiManager::handleRoot, this)); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
|
|
||||||
server->onNotFound (std::bind(&WiFiManager::handleNotFound, this));
|
|
||||||
server->begin(); // Web server start
|
|
||||||
DEBUG_WM(F("HTTP server started"));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean WiFiManager::autoConnect() {
|
|
||||||
String ssid = "ESP" + String(ESP.getChipId());
|
|
||||||
return autoConnect(ssid.c_str(), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean WiFiManager::autoConnect(char const *apName, char const *apPassword) {
|
|
||||||
DEBUG_WM(F(""));
|
|
||||||
DEBUG_WM(F("AutoConnect"));
|
|
||||||
|
|
||||||
// read eeprom for ssid and pass
|
|
||||||
//String ssid = getSSID();
|
|
||||||
//String pass = getPassword();
|
|
||||||
|
|
||||||
// attempt to connect; should it fail, fall back to AP
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
|
|
||||||
if (connectWifi("", "") == WL_CONNECTED) {
|
|
||||||
DEBUG_WM(F("IP Address:"));
|
|
||||||
DEBUG_WM(WiFi.localIP());
|
|
||||||
//connected
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return startConfigPortal(apName, apPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean WiFiManager::configPortalHasTimeout(){
|
|
||||||
if(_configPortalTimeout == 0 || wifi_softap_get_station_num() > 0){
|
|
||||||
_configPortalStart = millis(); // kludge, bump configportal start time to skew timeouts
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (millis() > _configPortalStart + _configPortalTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean WiFiManager::startConfigPortal() {
|
|
||||||
String ssid = "ESP" + String(ESP.getChipId());
|
|
||||||
return startConfigPortal(ssid.c_str(), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean WiFiManager::startConfigPortal(char const *apName, char const *apPassword) {
|
|
||||||
|
|
||||||
if(!WiFi.isConnected()){
|
|
||||||
WiFi.persistent(false);
|
|
||||||
// disconnect sta, start ap
|
|
||||||
WiFi.disconnect(); // this alone is not enough to stop the autoconnecter
|
|
||||||
WiFi.mode(WIFI_AP);
|
|
||||||
WiFi.persistent(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//setup AP
|
|
||||||
WiFi.mode(WIFI_AP_STA);
|
|
||||||
DEBUG_WM(F("SET AP STA"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_apName = apName;
|
|
||||||
_apPassword = apPassword;
|
|
||||||
|
|
||||||
//notify we entered AP mode
|
|
||||||
if ( _apcallback != NULL) {
|
|
||||||
_apcallback(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
connect = false;
|
|
||||||
setupConfigPortal();
|
|
||||||
|
|
||||||
while(1){
|
|
||||||
|
|
||||||
// check if timeout
|
|
||||||
if(configPortalHasTimeout()) break;
|
|
||||||
|
|
||||||
//DNS
|
|
||||||
dnsServer->processNextRequest();
|
|
||||||
//HTTP
|
|
||||||
server->handleClient();
|
|
||||||
|
|
||||||
if (connect) {
|
|
||||||
delay(1000);
|
|
||||||
connect = false;
|
|
||||||
|
|
||||||
// if saving with no ssid filled in, reconnect to ssid
|
|
||||||
// will not exit cp
|
|
||||||
if(_ssid == ""){
|
|
||||||
DEBUG_WM(F("No ssid, skipping wifi"));
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
DEBUG_WM(F("Connecting to new AP"));
|
|
||||||
if (connectWifi(_ssid, _pass) != WL_CONNECTED) {
|
|
||||||
delay(2000);
|
|
||||||
// using user-provided _ssid, _pass in place of system-stored ssid and pass
|
|
||||||
DEBUG_WM(F("Failed to connect."));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//connected
|
|
||||||
WiFi.mode(WIFI_STA);
|
|
||||||
//notify that configuration has changed and any optional parameters should be saved
|
|
||||||
if ( _savecallback != NULL) {
|
|
||||||
//todo: check if any custom parameters actually exist, and check if they really changed maybe
|
|
||||||
_savecallback();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_shouldBreakAfterConfig) {
|
|
||||||
//flag set to exit after config after trying to connect
|
|
||||||
//notify that configuration has changed and any optional parameters should be saved
|
|
||||||
if ( _savecallback != NULL) {
|
|
||||||
//todo: check if any custom parameters actually exist, and check if they really changed maybe
|
|
||||||
_savecallback();
|
|
||||||
}
|
|
||||||
WiFi.mode(WIFI_STA); // turn off ap
|
|
||||||
// reconnect to ssid
|
|
||||||
// int res = WiFi.begin();
|
|
||||||
// attempt connect for 10 seconds
|
|
||||||
DEBUG_WM(F("Waiting for sta (10 secs) ......."));
|
|
||||||
for(size_t i = 0 ; i<100;i++){
|
|
||||||
if(WiFi.status() == WL_CONNECTED) break;
|
|
||||||
DEBUG_WM(".");
|
|
||||||
// Serial.println(WiFi.status());
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
delay(1000);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
server.reset();
|
|
||||||
dnsServer.reset();
|
|
||||||
|
|
||||||
return WiFi.status() == WL_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int WiFiManager::connectWifi(String ssid, String pass) {
|
|
||||||
DEBUG_WM(F("Connecting as wifi client..."));
|
|
||||||
|
|
||||||
// check if we've got static_ip settings, if we do, use those.
|
|
||||||
if (_sta_static_ip) {
|
|
||||||
DEBUG_WM(F("Custom STA IP/GW/Subnet"));
|
|
||||||
WiFi.config(_sta_static_ip, _sta_static_gw, _sta_static_sn);
|
|
||||||
DEBUG_WM(WiFi.localIP());
|
|
||||||
}
|
|
||||||
//fix for auto connect racing issue
|
|
||||||
if (WiFi.status() == WL_CONNECTED && (WiFi.SSID() == ssid)) {
|
|
||||||
DEBUG_WM(F("Already connected. Bailing out."));
|
|
||||||
return WL_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WM(F("Status:"));
|
|
||||||
DEBUG_WM(WiFi.status());
|
|
||||||
|
|
||||||
wl_status_t res;
|
|
||||||
//check if we have ssid and pass and force those, if not, try with last saved values
|
|
||||||
if (ssid != "") {
|
|
||||||
//trying to fix connection in progress hanging
|
|
||||||
ETS_UART_INTR_DISABLE();
|
|
||||||
wifi_station_disconnect();
|
|
||||||
ETS_UART_INTR_ENABLE();
|
|
||||||
res = WiFi.begin(ssid.c_str(), pass.c_str(),0,NULL,true);
|
|
||||||
if(res != WL_CONNECTED){
|
|
||||||
DEBUG_WM(F("[ERROR] WiFi.begin res:"));
|
|
||||||
DEBUG_WM(res);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (WiFi.SSID() != "") {
|
|
||||||
DEBUG_WM(F("Using last saved values, should be faster"));
|
|
||||||
//trying to fix connection in progress hanging
|
|
||||||
ETS_UART_INTR_DISABLE();
|
|
||||||
wifi_station_disconnect();
|
|
||||||
ETS_UART_INTR_ENABLE();
|
|
||||||
res = WiFi.begin();
|
|
||||||
} else {
|
|
||||||
DEBUG_WM(F("No saved credentials"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int connRes = waitForConnectResult();
|
|
||||||
DEBUG_WM ("Connection result: ");
|
|
||||||
DEBUG_WM ( connRes );
|
|
||||||
//not connected, WPS enabled, no pass - first attempt
|
|
||||||
#ifdef NO_EXTRA_4K_HEAP
|
|
||||||
if (_tryWPS && connRes != WL_CONNECTED && pass == "") {
|
|
||||||
startWPS();
|
|
||||||
//should be connected at the end of WPS
|
|
||||||
connRes = waitForConnectResult();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return connRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t WiFiManager::waitForConnectResult() {
|
|
||||||
if (_connectTimeout == 0) {
|
|
||||||
return WiFi.waitForConnectResult();
|
|
||||||
} else {
|
|
||||||
DEBUG_WM (F("Waiting for connection result with time out"));
|
|
||||||
unsigned long start = millis();
|
|
||||||
boolean keepConnecting = true;
|
|
||||||
uint8_t status;
|
|
||||||
while (keepConnecting) {
|
|
||||||
status = WiFi.status();
|
|
||||||
if (millis() > start + _connectTimeout) {
|
|
||||||
keepConnecting = false;
|
|
||||||
DEBUG_WM (F("Connection timed out"));
|
|
||||||
}
|
|
||||||
if (status == WL_CONNECTED) {
|
|
||||||
keepConnecting = false;
|
|
||||||
}
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::startWPS() {
|
|
||||||
DEBUG_WM(F("START WPS"));
|
|
||||||
WiFi.beginWPSConfig();
|
|
||||||
DEBUG_WM(F("END WPS"));
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
String WiFiManager::getSSID() {
|
|
||||||
if (_ssid == "") {
|
|
||||||
DEBUG_WM(F("Reading SSID"));
|
|
||||||
_ssid = WiFi.SSID();
|
|
||||||
DEBUG_WM(F("SSID: "));
|
|
||||||
DEBUG_WM(_ssid);
|
|
||||||
}
|
|
||||||
return _ssid;
|
|
||||||
}
|
|
||||||
|
|
||||||
String WiFiManager::getPassword() {
|
|
||||||
if (_pass == "") {
|
|
||||||
DEBUG_WM(F("Reading Password"));
|
|
||||||
_pass = WiFi.psk();
|
|
||||||
DEBUG_WM("Password: " + _pass);
|
|
||||||
//DEBUG_WM(_pass);
|
|
||||||
}
|
|
||||||
return _pass;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
String WiFiManager::getConfigPortalSSID() {
|
|
||||||
return _apName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::resetSettings() {
|
|
||||||
DEBUG_WM(F("settings invalidated"));
|
|
||||||
DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA."));
|
|
||||||
WiFi.disconnect(true);
|
|
||||||
//delay(200);
|
|
||||||
}
|
|
||||||
void WiFiManager::setTimeout(unsigned long seconds) {
|
|
||||||
setConfigPortalTimeout(seconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setConfigPortalTimeout(unsigned long seconds) {
|
|
||||||
_configPortalTimeout = seconds * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setConnectTimeout(unsigned long seconds) {
|
|
||||||
_connectTimeout = seconds * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setDebugOutput(boolean debug) {
|
|
||||||
_debug = debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn) {
|
|
||||||
_ap_static_ip = ip;
|
|
||||||
_ap_static_gw = gw;
|
|
||||||
_ap_static_sn = sn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn) {
|
|
||||||
_sta_static_ip = ip;
|
|
||||||
_sta_static_gw = gw;
|
|
||||||
_sta_static_sn = sn;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setMinimumSignalQuality(int quality) {
|
|
||||||
_minimumQuality = quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::setBreakAfterConfig(boolean shouldBreak) {
|
|
||||||
_shouldBreakAfterConfig = shouldBreak;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Handle root or redirect to captive portal */
|
|
||||||
void WiFiManager::handleRoot() {
|
|
||||||
DEBUG_WM(F("Handle root"));
|
|
||||||
if (captivePortal()) { // If caprive portal redirect instead of displaying the page.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String page = FPSTR(HTTP_HEADER);
|
|
||||||
page.replace("{v}", "Options");
|
|
||||||
page += FPSTR(HTTP_SCRIPT);
|
|
||||||
page += FPSTR(HTTP_STYLE);
|
|
||||||
page += _customHeadElement;
|
|
||||||
page += FPSTR(HTTP_HEADER_END);
|
|
||||||
page += String(F("<h1>"));
|
|
||||||
page += _apName;
|
|
||||||
page += String(F("</h1>"));
|
|
||||||
page += String(F("<h3>WiFiManager</h3>"));
|
|
||||||
page += FPSTR(HTTP_PORTAL_OPTIONS);
|
|
||||||
page += FPSTR(HTTP_END);
|
|
||||||
|
|
||||||
server->sendHeader("Content-Length", String(page.length()));
|
|
||||||
server->send(200, "text/html", page);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Wifi config page handler */
|
|
||||||
void WiFiManager::handleWifi(boolean scan) {
|
|
||||||
|
|
||||||
String page = FPSTR(HTTP_HEADER);
|
|
||||||
page.replace("{v}", "Config ESP");
|
|
||||||
page += FPSTR(HTTP_SCRIPT);
|
|
||||||
page += FPSTR(HTTP_STYLE);
|
|
||||||
page += _customHeadElement;
|
|
||||||
page += FPSTR(HTTP_HEADER_END);
|
|
||||||
|
|
||||||
if (scan) {
|
|
||||||
int n = WiFi.scanNetworks();
|
|
||||||
DEBUG_WM(F("Scan done"));
|
|
||||||
if (n == 0) {
|
|
||||||
DEBUG_WM(F("No networks found"));
|
|
||||||
page += F("No networks found. Refresh to scan again.");
|
|
||||||
} else {
|
|
||||||
|
|
||||||
//sort networks
|
|
||||||
int indices[n];
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
indices[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RSSI SORT
|
|
||||||
|
|
||||||
// old sort
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
for (int j = i + 1; j < n; j++) {
|
|
||||||
if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) {
|
|
||||||
std::swap(indices[i], indices[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*std::sort(indices, indices + n, [](const int & a, const int & b) -> bool
|
|
||||||
{
|
|
||||||
return WiFi.RSSI(a) > WiFi.RSSI(b);
|
|
||||||
});*/
|
|
||||||
|
|
||||||
// remove duplicates ( must be RSSI sorted )
|
|
||||||
if (_removeDuplicateAPs) {
|
|
||||||
String cssid;
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
if (indices[i] == -1) continue;
|
|
||||||
cssid = WiFi.SSID(indices[i]);
|
|
||||||
for (int j = i + 1; j < n; j++) {
|
|
||||||
if (cssid == WiFi.SSID(indices[j])) {
|
|
||||||
DEBUG_WM("DUP AP: " + WiFi.SSID(indices[j]));
|
|
||||||
indices[j] = -1; // set dup aps to index -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//display networks in page
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
if (indices[i] == -1) continue; // skip dups
|
|
||||||
DEBUG_WM(WiFi.SSID(indices[i]));
|
|
||||||
DEBUG_WM(WiFi.RSSI(indices[i]));
|
|
||||||
int quality = getRSSIasQuality(WiFi.RSSI(indices[i]));
|
|
||||||
|
|
||||||
if (_minimumQuality == -1 || _minimumQuality < quality) {
|
|
||||||
String item = FPSTR(HTTP_ITEM);
|
|
||||||
String rssiQ;
|
|
||||||
rssiQ += quality;
|
|
||||||
item.replace("{v}", WiFi.SSID(indices[i]));
|
|
||||||
item.replace("{r}", rssiQ);
|
|
||||||
if (WiFi.encryptionType(indices[i]) != ENC_TYPE_NONE) {
|
|
||||||
item.replace("{i}", "l");
|
|
||||||
} else {
|
|
||||||
item.replace("{i}", "");
|
|
||||||
}
|
|
||||||
//DEBUG_WM(item);
|
|
||||||
page += item;
|
|
||||||
delay(0);
|
|
||||||
} else {
|
|
||||||
DEBUG_WM(F("Skipping due to quality"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
page += "<br/>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
page += FPSTR(HTTP_FORM_START);
|
|
||||||
char parLength[5];
|
|
||||||
// add the extra parameters to the form
|
|
||||||
for (int i = 0; i < _paramsCount; i++) {
|
|
||||||
if (_params[i] == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String pitem = FPSTR(HTTP_FORM_PARAM);
|
|
||||||
if (_params[i]->getID() != NULL) {
|
|
||||||
pitem.replace("{i}", _params[i]->getID());
|
|
||||||
pitem.replace("{n}", _params[i]->getID());
|
|
||||||
pitem.replace("{p}", _params[i]->getPlaceholder());
|
|
||||||
snprintf(parLength, 5, "%d", _params[i]->getValueLength());
|
|
||||||
pitem.replace("{l}", parLength);
|
|
||||||
pitem.replace("{v}", _params[i]->getValue());
|
|
||||||
pitem.replace("{c}", _params[i]->getCustomHTML());
|
|
||||||
} else {
|
|
||||||
pitem = _params[i]->getCustomHTML();
|
|
||||||
}
|
|
||||||
|
|
||||||
page += pitem;
|
|
||||||
}
|
|
||||||
if (_params[0] != NULL) {
|
|
||||||
page += "<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sta_static_ip) {
|
|
||||||
|
|
||||||
String item = FPSTR(HTTP_FORM_PARAM);
|
|
||||||
item.replace("{i}", "ip");
|
|
||||||
item.replace("{n}", "ip");
|
|
||||||
item.replace("{p}", "Static IP");
|
|
||||||
item.replace("{l}", "15");
|
|
||||||
item.replace("{v}", _sta_static_ip.toString());
|
|
||||||
|
|
||||||
page += item;
|
|
||||||
|
|
||||||
item = FPSTR(HTTP_FORM_PARAM);
|
|
||||||
item.replace("{i}", "gw");
|
|
||||||
item.replace("{n}", "gw");
|
|
||||||
item.replace("{p}", "Static Gateway");
|
|
||||||
item.replace("{l}", "15");
|
|
||||||
item.replace("{v}", _sta_static_gw.toString());
|
|
||||||
|
|
||||||
page += item;
|
|
||||||
|
|
||||||
item = FPSTR(HTTP_FORM_PARAM);
|
|
||||||
item.replace("{i}", "sn");
|
|
||||||
item.replace("{n}", "sn");
|
|
||||||
item.replace("{p}", "Subnet");
|
|
||||||
item.replace("{l}", "15");
|
|
||||||
item.replace("{v}", _sta_static_sn.toString());
|
|
||||||
|
|
||||||
page += item;
|
|
||||||
|
|
||||||
page += "<br/>";
|
|
||||||
}
|
|
||||||
|
|
||||||
page += FPSTR(HTTP_FORM_END);
|
|
||||||
page += FPSTR(HTTP_SCAN_LINK);
|
|
||||||
|
|
||||||
page += FPSTR(HTTP_END);
|
|
||||||
|
|
||||||
server->sendHeader("Content-Length", String(page.length()));
|
|
||||||
server->send(200, "text/html", page);
|
|
||||||
|
|
||||||
|
|
||||||
DEBUG_WM(F("Sent config page"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Handle the WLAN save form and redirect to WLAN config page again */
|
|
||||||
void WiFiManager::handleWifiSave() {
|
|
||||||
DEBUG_WM(F("WiFi save"));
|
|
||||||
|
|
||||||
//SAVE/connect here
|
|
||||||
_ssid = server->arg("s").c_str();
|
|
||||||
_pass = server->arg("p").c_str();
|
|
||||||
|
|
||||||
//parameters
|
|
||||||
for (int i = 0; i < _paramsCount; i++) {
|
|
||||||
if (_params[i] == NULL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//read parameter
|
|
||||||
String value = server->arg(_params[i]->getID()).c_str();
|
|
||||||
//store it in array
|
|
||||||
value.toCharArray(_params[i]->_value, _params[i]->_length + 1);
|
|
||||||
DEBUG_WM(F("Parameter"));
|
|
||||||
DEBUG_WM(_params[i]->getID());
|
|
||||||
DEBUG_WM(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server->arg("ip") != "") {
|
|
||||||
DEBUG_WM(F("static ip"));
|
|
||||||
DEBUG_WM(server->arg("ip"));
|
|
||||||
//_sta_static_ip.fromString(server->arg("ip"));
|
|
||||||
String ip = server->arg("ip");
|
|
||||||
optionalIPFromString(&_sta_static_ip, ip.c_str());
|
|
||||||
}
|
|
||||||
if (server->arg("gw") != "") {
|
|
||||||
DEBUG_WM(F("static gateway"));
|
|
||||||
DEBUG_WM(server->arg("gw"));
|
|
||||||
String gw = server->arg("gw");
|
|
||||||
optionalIPFromString(&_sta_static_gw, gw.c_str());
|
|
||||||
}
|
|
||||||
if (server->arg("sn") != "") {
|
|
||||||
DEBUG_WM(F("static netmask"));
|
|
||||||
DEBUG_WM(server->arg("sn"));
|
|
||||||
String sn = server->arg("sn");
|
|
||||||
optionalIPFromString(&_sta_static_sn, sn.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
String page = FPSTR(HTTP_HEADER);
|
|
||||||
page.replace("{v}", "Credentials Saved");
|
|
||||||
page += FPSTR(HTTP_SCRIPT);
|
|
||||||
page += FPSTR(HTTP_STYLE);
|
|
||||||
page += _customHeadElement;
|
|
||||||
page += FPSTR(HTTP_HEADER_END);
|
|
||||||
page += FPSTR(HTTP_SAVED);
|
|
||||||
page += FPSTR(HTTP_END);
|
|
||||||
|
|
||||||
server->sendHeader("Content-Length", String(page.length()));
|
|
||||||
server->send(200, "text/html", page);
|
|
||||||
|
|
||||||
DEBUG_WM(F("Sent wifi save page"));
|
|
||||||
|
|
||||||
connect = true; //signal ready to connect/reset
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Handle the info page */
|
|
||||||
void WiFiManager::handleInfo() {
|
|
||||||
DEBUG_WM(F("Info"));
|
|
||||||
|
|
||||||
String page = FPSTR(HTTP_HEADER);
|
|
||||||
page.replace("{v}", "Info");
|
|
||||||
page += FPSTR(HTTP_SCRIPT);
|
|
||||||
page += FPSTR(HTTP_STYLE);
|
|
||||||
page += _customHeadElement;
|
|
||||||
page += FPSTR(HTTP_HEADER_END);
|
|
||||||
page += F("<dl>");
|
|
||||||
page += F("<dt>Chip ID</dt><dd>");
|
|
||||||
page += ESP.getChipId();
|
|
||||||
page += F("</dd>");
|
|
||||||
page += F("<dt>Flash Chip ID</dt><dd>");
|
|
||||||
page += ESP.getFlashChipId();
|
|
||||||
page += F("</dd>");
|
|
||||||
page += F("<dt>IDE Flash Size</dt><dd>");
|
|
||||||
page += ESP.getFlashChipSize();
|
|
||||||
page += F(" bytes</dd>");
|
|
||||||
page += F("<dt>Real Flash Size</dt><dd>");
|
|
||||||
page += ESP.getFlashChipRealSize();
|
|
||||||
page += F(" bytes</dd>");
|
|
||||||
page += F("<dt>Soft AP IP</dt><dd>");
|
|
||||||
page += WiFi.softAPIP().toString();
|
|
||||||
page += F("</dd>");
|
|
||||||
page += F("<dt>Soft AP MAC</dt><dd>");
|
|
||||||
page += WiFi.softAPmacAddress();
|
|
||||||
page += F("</dd>");
|
|
||||||
page += F("<dt>Station MAC</dt><dd>");
|
|
||||||
page += WiFi.macAddress();
|
|
||||||
page += F("</dd>");
|
|
||||||
page += F("</dl>");
|
|
||||||
page += FPSTR(HTTP_END);
|
|
||||||
|
|
||||||
server->sendHeader("Content-Length", String(page.length()));
|
|
||||||
server->send(200, "text/html", page);
|
|
||||||
|
|
||||||
DEBUG_WM(F("Sent info page"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Handle the reset page */
|
|
||||||
void WiFiManager::handleReset() {
|
|
||||||
DEBUG_WM(F("Reset"));
|
|
||||||
|
|
||||||
String page = FPSTR(HTTP_HEADER);
|
|
||||||
page.replace("{v}", "Info");
|
|
||||||
page += FPSTR(HTTP_SCRIPT);
|
|
||||||
page += FPSTR(HTTP_STYLE);
|
|
||||||
page += _customHeadElement;
|
|
||||||
page += FPSTR(HTTP_HEADER_END);
|
|
||||||
page += F("Module will reset in a few seconds.");
|
|
||||||
page += FPSTR(HTTP_END);
|
|
||||||
|
|
||||||
server->sendHeader("Content-Length", String(page.length()));
|
|
||||||
server->send(200, "text/html", page);
|
|
||||||
|
|
||||||
DEBUG_WM(F("Sent reset page"));
|
|
||||||
delay(5000);
|
|
||||||
ESP.reset();
|
|
||||||
delay(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WiFiManager::handleNotFound() {
|
|
||||||
if (captivePortal()) { // If captive portal redirect instead of displaying the error page.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String message = "File Not Found\n\n";
|
|
||||||
message += "URI: ";
|
|
||||||
message += server->uri();
|
|
||||||
message += "\nMethod: ";
|
|
||||||
message += ( server->method() == HTTP_GET ) ? "GET" : "POST";
|
|
||||||
message += "\nArguments: ";
|
|
||||||
message += server->args();
|
|
||||||
message += "\n";
|
|
||||||
|
|
||||||
for ( uint8_t i = 0; i < server->args(); i++ ) {
|
|
||||||
message += " " + server->argName ( i ) + ": " + server->arg ( i ) + "\n";
|
|
||||||
}
|
|
||||||
server->sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
||||||
server->sendHeader("Pragma", "no-cache");
|
|
||||||
server->sendHeader("Expires", "-1");
|
|
||||||
server->sendHeader("Content-Length", String(message.length()));
|
|
||||||
server->send ( 404, "text/plain", message );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */
|
|
||||||
boolean WiFiManager::captivePortal() {
|
|
||||||
if (!isIp(server->hostHeader()) ) {
|
|
||||||
DEBUG_WM(F("Request redirected to captive portal"));
|
|
||||||
server->sendHeader("Location", String("http://") + toStringIp(server->client().localIP()), true);
|
|
||||||
server->send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves.
|
|
||||||
server->client().stop(); // Stop is needed because we sent no content length
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//start up config portal callback
|
|
||||||
void WiFiManager::setAPCallback( void (*func)(WiFiManager* myWiFiManager) ) {
|
|
||||||
_apcallback = func;
|
|
||||||
}
|
|
||||||
|
|
||||||
//start up save config callback
|
|
||||||
void WiFiManager::setSaveConfigCallback( void (*func)(void) ) {
|
|
||||||
_savecallback = func;
|
|
||||||
}
|
|
||||||
|
|
||||||
//sets a custom element to add to head, like a new style tag
|
|
||||||
void WiFiManager::setCustomHeadElement(const char* element) {
|
|
||||||
_customHeadElement = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if this is true, remove duplicated Access Points - defaut true
|
|
||||||
void WiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates) {
|
|
||||||
_removeDuplicateAPs = removeDuplicates;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename Generic>
|
|
||||||
void WiFiManager::DEBUG_WM(Generic text) {
|
|
||||||
if (_debug) {
|
|
||||||
Serial.print("*WM: ");
|
|
||||||
Serial.println(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int WiFiManager::getRSSIasQuality(int RSSI) {
|
|
||||||
int quality = 0;
|
|
||||||
|
|
||||||
if (RSSI <= -100) {
|
|
||||||
quality = 0;
|
|
||||||
} else if (RSSI >= -50) {
|
|
||||||
quality = 100;
|
|
||||||
} else {
|
|
||||||
quality = 2 * (RSSI + 100);
|
|
||||||
}
|
|
||||||
return quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Is this an IP? */
|
|
||||||
boolean WiFiManager::isIp(String str) {
|
|
||||||
for (size_t i = 0; i < str.length(); i++) {
|
|
||||||
int c = str.charAt(i);
|
|
||||||
if (c != '.' && (c < '0' || c > '9')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** IP to String? */
|
|
||||||
String WiFiManager::toStringIp(IPAddress ip) {
|
|
||||||
String res = "";
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
res += String((ip >> (8 * i)) & 0xFF) + ".";
|
|
||||||
}
|
|
||||||
res += String(((ip >> 8 * 3)) & 0xFF);
|
|
||||||
return res;
|
|
||||||
}
|
|
|
@ -1,203 +0,0 @@
|
||||||
/**************************************************************
|
|
||||||
WiFiManager is a library for the ESP8266/Arduino platform
|
|
||||||
(https://github.com/esp8266/Arduino) to enable easy
|
|
||||||
configuration and reconfiguration of WiFi credentials using a Captive Portal
|
|
||||||
inspired by:
|
|
||||||
http://www.esp8266.com/viewtopic.php?f=29&t=2520
|
|
||||||
https://github.com/chriscook8/esp-arduino-apboot
|
|
||||||
https://github.com/esp8266/Arduino/tree/master/libraries/DNSServer/examples/CaptivePortalAdvanced
|
|
||||||
Built by AlexT https://github.com/tzapu
|
|
||||||
Licensed under MIT license
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
#ifndef WiFiManager_h
|
|
||||||
#define WiFiManager_h
|
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESP8266WebServer.h>
|
|
||||||
#include <DNSServer.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "user_interface.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
const char HTTP_HEADER[] PROGMEM = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/><title>{v}</title>";
|
|
||||||
const char HTTP_STYLE[] PROGMEM = "<style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;font-family:verdana;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} .q{float: right;width: 64px;text-align: right;} .l{background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==\") no-repeat left center;background-size: 1em;}</style>";
|
|
||||||
const char HTTP_SCRIPT[] PROGMEM = "<script>function c(l){document.getElementById('s').value=l.innerText||l.textContent;document.getElementById('p').focus();}</script>";
|
|
||||||
const char HTTP_HEADER_END[] PROGMEM = "</head><body><div style='text-align:left;display:inline-block;min-width:260px;'>";
|
|
||||||
const char HTTP_PORTAL_OPTIONS[] PROGMEM = "<form action=\"/wifi\" method=\"get\"><button>Configure WiFi</button></form><br/><form action=\"/0wifi\" method=\"get\"><button>Configure WiFi (No Scan)</button></form><br/><form action=\"/i\" method=\"get\"><button>Info</button></form><br/><form action=\"/r\" method=\"post\"><button>Reset</button></form>";
|
|
||||||
const char HTTP_ITEM[] PROGMEM = "<div><a href='#p' onclick='c(this)'>{v}</a> <span class='q {i}'>{r}%</span></div>";
|
|
||||||
const char HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><input id='s' name='s' length=32 placeholder='SSID'><br/><input id='p' name='p' length=64 type='password' placeholder='password'><br/>";
|
|
||||||
const char HTTP_FORM_PARAM[] PROGMEM = "<br/><input id='{i}' name='{n}' maxlength={l} placeholder='{p}' value='{v}' {c}>";
|
|
||||||
const char HTTP_FORM_END[] PROGMEM = "<br/><button type='submit'>save</button></form>";
|
|
||||||
const char HTTP_SCAN_LINK[] PROGMEM = "<br/><div class=\"c\"><a href=\"/wifi\">Scan</a></div>";
|
|
||||||
const char HTTP_SAVED[] PROGMEM = "<div>Credentials Saved<br />Trying to connect ESP to network.<br />If it fails reconnect to AP to try again</div>";
|
|
||||||
const char HTTP_END[] PROGMEM = "</div></body></html>";
|
|
||||||
|
|
||||||
#ifndef WIFI_MANAGER_MAX_PARAMS
|
|
||||||
#define WIFI_MANAGER_MAX_PARAMS 10
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class WiFiManagerParameter {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
Create custom parameters that can be added to the WiFiManager setup web page
|
|
||||||
@id is used for HTTP queries and must not contain spaces nor other special characters
|
|
||||||
*/
|
|
||||||
WiFiManagerParameter(const char *custom);
|
|
||||||
WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length);
|
|
||||||
WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
|
|
||||||
~WiFiManagerParameter();
|
|
||||||
|
|
||||||
const char *getID();
|
|
||||||
const char *getValue();
|
|
||||||
const char *getPlaceholder();
|
|
||||||
int getValueLength();
|
|
||||||
const char *getCustomHTML();
|
|
||||||
private:
|
|
||||||
const char *_id;
|
|
||||||
const char *_placeholder;
|
|
||||||
char *_value;
|
|
||||||
int _length;
|
|
||||||
const char *_customHTML;
|
|
||||||
|
|
||||||
void init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
|
|
||||||
|
|
||||||
friend class WiFiManager;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class WiFiManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WiFiManager();
|
|
||||||
~WiFiManager();
|
|
||||||
|
|
||||||
boolean autoConnect();
|
|
||||||
boolean autoConnect(char const *apName, char const *apPassword = NULL);
|
|
||||||
|
|
||||||
//if you want to always start the config portal, without trying to connect first
|
|
||||||
boolean startConfigPortal();
|
|
||||||
boolean startConfigPortal(char const *apName, char const *apPassword = NULL);
|
|
||||||
|
|
||||||
// get the AP name of the config portal, so it can be used in the callback
|
|
||||||
String getConfigPortalSSID();
|
|
||||||
|
|
||||||
void resetSettings();
|
|
||||||
|
|
||||||
//sets timeout before webserver loop ends and exits even if there has been no setup.
|
|
||||||
//useful for devices that failed to connect at some point and got stuck in a webserver loop
|
|
||||||
//in seconds setConfigPortalTimeout is a new name for setTimeout
|
|
||||||
void setConfigPortalTimeout(unsigned long seconds);
|
|
||||||
void setTimeout(unsigned long seconds);
|
|
||||||
|
|
||||||
//sets timeout for which to attempt connecting, useful if you get a lot of failed connects
|
|
||||||
void setConnectTimeout(unsigned long seconds);
|
|
||||||
|
|
||||||
|
|
||||||
void setDebugOutput(boolean debug);
|
|
||||||
//defaults to not showing anything under 8% signal quality if called
|
|
||||||
void setMinimumSignalQuality(int quality = 8);
|
|
||||||
//sets a custom ip /gateway /subnet configuration
|
|
||||||
void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
|
|
||||||
//sets config for a static IP
|
|
||||||
void setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
|
|
||||||
//called when AP mode and config portal is started
|
|
||||||
void setAPCallback( void (*func)(WiFiManager*) );
|
|
||||||
//called when settings have been changed and connection was successful
|
|
||||||
void setSaveConfigCallback( void (*func)(void) );
|
|
||||||
//adds a custom parameter, returns false on failure
|
|
||||||
bool addParameter(WiFiManagerParameter *p);
|
|
||||||
//if this is set, it will exit after config, even if connection is unsuccessful.
|
|
||||||
void setBreakAfterConfig(boolean shouldBreak);
|
|
||||||
//if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins)
|
|
||||||
//TODO
|
|
||||||
//if this is set, customise style
|
|
||||||
void setCustomHeadElement(const char* element);
|
|
||||||
//if this is true, remove duplicated Access Points - defaut true
|
|
||||||
void setRemoveDuplicateAPs(boolean removeDuplicates);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<DNSServer> dnsServer;
|
|
||||||
std::unique_ptr<ESP8266WebServer> server;
|
|
||||||
|
|
||||||
//const int WM_DONE = 0;
|
|
||||||
//const int WM_WAIT = 10;
|
|
||||||
|
|
||||||
//const String HTTP_HEADER = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>{v}</title>";
|
|
||||||
|
|
||||||
void setupConfigPortal();
|
|
||||||
void startWPS();
|
|
||||||
|
|
||||||
const char* _apName = "no-net";
|
|
||||||
const char* _apPassword = NULL;
|
|
||||||
String _ssid = "";
|
|
||||||
String _pass = "";
|
|
||||||
unsigned long _configPortalTimeout = 0;
|
|
||||||
unsigned long _connectTimeout = 0;
|
|
||||||
unsigned long _configPortalStart = 0;
|
|
||||||
|
|
||||||
IPAddress _ap_static_ip;
|
|
||||||
IPAddress _ap_static_gw;
|
|
||||||
IPAddress _ap_static_sn;
|
|
||||||
IPAddress _sta_static_ip;
|
|
||||||
IPAddress _sta_static_gw;
|
|
||||||
IPAddress _sta_static_sn;
|
|
||||||
|
|
||||||
int _paramsCount = 0;
|
|
||||||
int _minimumQuality = -1;
|
|
||||||
boolean _removeDuplicateAPs = true;
|
|
||||||
boolean _shouldBreakAfterConfig = false;
|
|
||||||
boolean _tryWPS = false;
|
|
||||||
|
|
||||||
const char* _customHeadElement = "";
|
|
||||||
|
|
||||||
//String getEEPROMString(int start, int len);
|
|
||||||
//void setEEPROMString(int start, int len, String string);
|
|
||||||
|
|
||||||
int status = WL_IDLE_STATUS;
|
|
||||||
int connectWifi(String ssid, String pass);
|
|
||||||
uint8_t waitForConnectResult();
|
|
||||||
|
|
||||||
void handleRoot();
|
|
||||||
void handleWifi(boolean scan);
|
|
||||||
void handleWifiSave();
|
|
||||||
void handleInfo();
|
|
||||||
void handleReset();
|
|
||||||
void handleNotFound();
|
|
||||||
void handle204();
|
|
||||||
boolean captivePortal();
|
|
||||||
boolean configPortalHasTimeout();
|
|
||||||
|
|
||||||
// DNS server
|
|
||||||
const byte DNS_PORT = 53;
|
|
||||||
|
|
||||||
//helpers
|
|
||||||
int getRSSIasQuality(int RSSI);
|
|
||||||
boolean isIp(String str);
|
|
||||||
String toStringIp(IPAddress ip);
|
|
||||||
|
|
||||||
boolean connect;
|
|
||||||
boolean _debug = true;
|
|
||||||
|
|
||||||
void (*_apcallback)(WiFiManager*) = NULL;
|
|
||||||
void (*_savecallback)(void) = NULL;
|
|
||||||
|
|
||||||
int _max_params;
|
|
||||||
WiFiManagerParameter** _params;
|
|
||||||
|
|
||||||
template <typename Generic>
|
|
||||||
void DEBUG_WM(Generic text);
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
auto optionalIPFromString(T *obj, const char *s) -> decltype( obj->fromString(s) ) {
|
|
||||||
return obj->fromString(s);
|
|
||||||
}
|
|
||||||
auto optionalIPFromString(...) -> bool {
|
|
||||||
DEBUG_WM("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"name": "WifiManager",
|
|
||||||
"keywords": "wifi, wi-fi",
|
|
||||||
"description": "ESP8266 WiFi Connection manager with fallback web configuration portal",
|
|
||||||
"repository":
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/tzapu/WiFiManager.git"
|
|
||||||
},
|
|
||||||
"frameworks": "arduino",
|
|
||||||
"platforms": "espressif8266",
|
|
||||||
"version": "0.15.0"
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
name=WiFiManager
|
|
||||||
version=0.15.0
|
|
||||||
author=tzapu
|
|
||||||
maintainer=tzapu
|
|
||||||
sentence=ESP8266 WiFi Connection manager with fallback web configuration portal
|
|
||||||
paragraph=Library for configuring ESP8266 modules WiFi credentials at runtime.
|
|
||||||
category=Communication
|
|
||||||
url=https://github.com/tzapu/WiFiManager.git
|
|
||||||
architectures=esp8266
|
|
1
dep/pubsubclient-2.7/.gitignore
vendored
1
dep/pubsubclient-2.7/.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
tests/bin
|
|
|
@ -1,7 +0,0 @@
|
||||||
sudo: false
|
|
||||||
language: cpp
|
|
||||||
compiler:
|
|
||||||
- g++
|
|
||||||
script: cd tests && make && make test
|
|
||||||
os:
|
|
||||||
- linux
|
|
|
@ -1,76 +0,0 @@
|
||||||
2.7
|
|
||||||
* Fix remaining-length handling to prevent buffer overrun
|
|
||||||
* Add large-payload API - beginPublish/write/publish/endPublish
|
|
||||||
* Add yield call to improve reliability on ESP
|
|
||||||
* Add Clean Session flag to connect options
|
|
||||||
* Add ESP32 support for functional callback signature
|
|
||||||
* Various other fixes
|
|
||||||
|
|
||||||
2.4
|
|
||||||
* Add MQTT_SOCKET_TIMEOUT to prevent it blocking indefinitely
|
|
||||||
whilst waiting for inbound data
|
|
||||||
* Fixed return code when publishing >256 bytes
|
|
||||||
|
|
||||||
2.3
|
|
||||||
* Add publish(topic,payload,retained) function
|
|
||||||
|
|
||||||
2.2
|
|
||||||
* Change code layout to match Arduino Library reqs
|
|
||||||
|
|
||||||
2.1
|
|
||||||
* Add MAX_TRANSFER_SIZE def to chunk messages if needed
|
|
||||||
* Reject topic/payloads that exceed MQTT_MAX_PACKET_SIZE
|
|
||||||
|
|
||||||
2.0
|
|
||||||
* Add (and default to) MQTT 3.1.1 support
|
|
||||||
* Fix PROGMEM handling for Intel Galileo/ESP8266
|
|
||||||
* Add overloaded constructors for convenience
|
|
||||||
* Add chainable setters for server/callback/client/stream
|
|
||||||
* Add state function to return connack return code
|
|
||||||
|
|
||||||
1.9
|
|
||||||
* Do not split MQTT packets over multiple calls to _client->write()
|
|
||||||
* API change: All constructors now require an instance of Client
|
|
||||||
to be passed in.
|
|
||||||
* Fixed example to match 1.8 api changes - dpslwk
|
|
||||||
* Added username/password support - WilHall
|
|
||||||
* Added publish_P - publishes messages from PROGMEM - jobytaffey
|
|
||||||
|
|
||||||
1.8
|
|
||||||
* KeepAlive interval is configurable in PubSubClient.h
|
|
||||||
* Maximum packet size is configurable in PubSubClient.h
|
|
||||||
* API change: Return boolean rather than int from various functions
|
|
||||||
* API change: Length parameter in message callback changed
|
|
||||||
from int to unsigned int
|
|
||||||
* Various internal tidy-ups around types
|
|
||||||
1.7
|
|
||||||
* Improved keepalive handling
|
|
||||||
* Updated to the Arduino-1.0 API
|
|
||||||
1.6
|
|
||||||
* Added the ability to publish a retained message
|
|
||||||
|
|
||||||
1.5
|
|
||||||
* Added default constructor
|
|
||||||
* Fixed compile error when used with arduino-0021 or later
|
|
||||||
|
|
||||||
1.4
|
|
||||||
* Fixed connection lost handling
|
|
||||||
|
|
||||||
1.3
|
|
||||||
* Fixed packet reading bug in PubSubClient.readPacket
|
|
||||||
|
|
||||||
1.2
|
|
||||||
* Fixed compile error when used with arduino-0016 or later
|
|
||||||
|
|
||||||
|
|
||||||
1.1
|
|
||||||
* Reduced size of library
|
|
||||||
* Added support for Will messages
|
|
||||||
* Clarified licensing - see LICENSE.txt
|
|
||||||
|
|
||||||
|
|
||||||
1.0
|
|
||||||
* Only Quality of Service (QOS) 0 messaging is supported
|
|
||||||
* The maximum message size, including header, is 128 bytes
|
|
||||||
* The keepalive interval is set to 30 seconds
|
|
||||||
* No support for Will messages
|
|
|
@ -1,20 +0,0 @@
|
||||||
Copyright (c) 2008-2015 Nicholas O'Leary
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
Basic MQTT example with Authentication
|
|
||||||
|
|
||||||
- connects to an MQTT server, providing username
|
|
||||||
and password
|
|
||||||
- publishes "hello world" to the topic "outTopic"
|
|
||||||
- subscribes to the topic "inTopic"
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your network.
|
|
||||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
|
||||||
IPAddress ip(172, 16, 0, 100);
|
|
||||||
IPAddress server(172, 16, 0, 2);
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
EthernetClient ethClient;
|
|
||||||
PubSubClient client(server, 1883, callback, ethClient);
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
// Note - the default maximum packet size is 128 bytes. If the
|
|
||||||
// combined length of clientId, username and password exceed this,
|
|
||||||
// you will need to increase the value of MQTT_MAX_PACKET_SIZE in
|
|
||||||
// PubSubClient.h
|
|
||||||
|
|
||||||
if (client.connect("arduinoClient", "testuser", "testpass")) {
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
client.loop();
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
/*
|
|
||||||
Basic MQTT example
|
|
||||||
|
|
||||||
This sketch demonstrates the basic capabilities of the library.
|
|
||||||
It connects to an MQTT server then:
|
|
||||||
- publishes "hello world" to the topic "outTopic"
|
|
||||||
- subscribes to the topic "inTopic", printing out any messages
|
|
||||||
it receives. NB - it assumes the received payloads are strings not binary
|
|
||||||
|
|
||||||
It will reconnect to the server if the connection is lost using a blocking
|
|
||||||
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
|
|
||||||
achieve the same result without blocking the main loop.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your network.
|
|
||||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
|
||||||
IPAddress ip(172, 16, 0, 100);
|
|
||||||
IPAddress server(172, 16, 0, 2);
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
Serial.print("Message arrived [");
|
|
||||||
Serial.print(topic);
|
|
||||||
Serial.print("] ");
|
|
||||||
for (int i=0;i<length;i++) {
|
|
||||||
Serial.print((char)payload[i]);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
}
|
|
||||||
|
|
||||||
EthernetClient ethClient;
|
|
||||||
PubSubClient client(ethClient);
|
|
||||||
|
|
||||||
void reconnect() {
|
|
||||||
// Loop until we're reconnected
|
|
||||||
while (!client.connected()) {
|
|
||||||
Serial.print("Attempting MQTT connection...");
|
|
||||||
// Attempt to connect
|
|
||||||
if (client.connect("arduinoClient")) {
|
|
||||||
Serial.println("connected");
|
|
||||||
// Once connected, publish an announcement...
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
// ... and resubscribe
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
} else {
|
|
||||||
Serial.print("failed, rc=");
|
|
||||||
Serial.print(client.state());
|
|
||||||
Serial.println(" try again in 5 seconds");
|
|
||||||
// Wait 5 seconds before retrying
|
|
||||||
delay(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Serial.begin(57600);
|
|
||||||
|
|
||||||
client.setServer(server, 1883);
|
|
||||||
client.setCallback(callback);
|
|
||||||
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
// Allow the hardware to sort itself out
|
|
||||||
delay(1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
if (!client.connected()) {
|
|
||||||
reconnect();
|
|
||||||
}
|
|
||||||
client.loop();
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
Basic ESP8266 MQTT example
|
|
||||||
|
|
||||||
This sketch demonstrates the capabilities of the pubsub library in combination
|
|
||||||
with the ESP8266 board/library.
|
|
||||||
|
|
||||||
It connects to an MQTT server then:
|
|
||||||
- publishes "hello world" to the topic "outTopic" every two seconds
|
|
||||||
- subscribes to the topic "inTopic", printing out any messages
|
|
||||||
it receives. NB - it assumes the received payloads are strings not binary
|
|
||||||
- If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
|
|
||||||
else switch it off
|
|
||||||
|
|
||||||
It will reconnect to the server if the connection is lost using a blocking
|
|
||||||
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
|
|
||||||
achieve the same result without blocking the main loop.
|
|
||||||
|
|
||||||
To install the ESP8266 board, (using Arduino 1.6.4+):
|
|
||||||
- Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
|
|
||||||
http://arduino.esp8266.com/stable/package_esp8266com_index.json
|
|
||||||
- Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
|
|
||||||
- Select your ESP8266 in "Tools -> Board"
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your network.
|
|
||||||
|
|
||||||
const char* ssid = "........";
|
|
||||||
const char* password = "........";
|
|
||||||
const char* mqtt_server = "broker.mqtt-dashboard.com";
|
|
||||||
|
|
||||||
WiFiClient espClient;
|
|
||||||
PubSubClient client(espClient);
|
|
||||||
long lastMsg = 0;
|
|
||||||
char msg[50];
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
void setup_wifi() {
|
|
||||||
|
|
||||||
delay(10);
|
|
||||||
// We start by connecting to a WiFi network
|
|
||||||
Serial.println();
|
|
||||||
Serial.print("Connecting to ");
|
|
||||||
Serial.println(ssid);
|
|
||||||
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
|
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
|
||||||
delay(500);
|
|
||||||
Serial.print(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
randomSeed(micros());
|
|
||||||
|
|
||||||
Serial.println("");
|
|
||||||
Serial.println("WiFi connected");
|
|
||||||
Serial.println("IP address: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
}
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
Serial.print("Message arrived [");
|
|
||||||
Serial.print(topic);
|
|
||||||
Serial.print("] ");
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
Serial.print((char)payload[i]);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// Switch on the LED if an 1 was received as first character
|
|
||||||
if ((char)payload[0] == '1') {
|
|
||||||
digitalWrite(BUILTIN_LED, LOW); // Turn the LED on (Note that LOW is the voltage level
|
|
||||||
// but actually the LED is on; this is because
|
|
||||||
// it is active low on the ESP-01)
|
|
||||||
} else {
|
|
||||||
digitalWrite(BUILTIN_LED, HIGH); // Turn the LED off by making the voltage HIGH
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void reconnect() {
|
|
||||||
// Loop until we're reconnected
|
|
||||||
while (!client.connected()) {
|
|
||||||
Serial.print("Attempting MQTT connection...");
|
|
||||||
// Create a random client ID
|
|
||||||
String clientId = "ESP8266Client-";
|
|
||||||
clientId += String(random(0xffff), HEX);
|
|
||||||
// Attempt to connect
|
|
||||||
if (client.connect(clientId.c_str())) {
|
|
||||||
Serial.println("connected");
|
|
||||||
// Once connected, publish an announcement...
|
|
||||||
client.publish("outTopic", "hello world");
|
|
||||||
// ... and resubscribe
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
} else {
|
|
||||||
Serial.print("failed, rc=");
|
|
||||||
Serial.print(client.state());
|
|
||||||
Serial.println(" try again in 5 seconds");
|
|
||||||
// Wait 5 seconds before retrying
|
|
||||||
delay(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
|
|
||||||
Serial.begin(115200);
|
|
||||||
setup_wifi();
|
|
||||||
client.setServer(mqtt_server, 1883);
|
|
||||||
client.setCallback(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
|
|
||||||
if (!client.connected()) {
|
|
||||||
reconnect();
|
|
||||||
}
|
|
||||||
client.loop();
|
|
||||||
|
|
||||||
long now = millis();
|
|
||||||
if (now - lastMsg > 2000) {
|
|
||||||
lastMsg = now;
|
|
||||||
++value;
|
|
||||||
snprintf (msg, 50, "hello world #%ld", value);
|
|
||||||
Serial.print("Publish message: ");
|
|
||||||
Serial.println(msg);
|
|
||||||
client.publish("outTopic", msg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
/*
|
|
||||||
Long message ESP8266 MQTT example
|
|
||||||
|
|
||||||
This sketch demonstrates sending arbitrarily large messages in combination
|
|
||||||
with the ESP8266 board/library.
|
|
||||||
|
|
||||||
It connects to an MQTT server then:
|
|
||||||
- publishes "hello world" to the topic "outTopic"
|
|
||||||
- subscribes to the topic "greenBottles/#", printing out any messages
|
|
||||||
it receives. NB - it assumes the received payloads are strings not binary
|
|
||||||
- If the sub-topic is a number, it publishes a "greenBottles/lyrics" message
|
|
||||||
with a payload consisting of the lyrics to "10 green bottles", replacing
|
|
||||||
10 with the number given in the sub-topic.
|
|
||||||
|
|
||||||
It will reconnect to the server if the connection is lost using a blocking
|
|
||||||
reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
|
|
||||||
achieve the same result without blocking the main loop.
|
|
||||||
|
|
||||||
To install the ESP8266 board, (using Arduino 1.6.4+):
|
|
||||||
- Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
|
|
||||||
http://arduino.esp8266.com/stable/package_esp8266com_index.json
|
|
||||||
- Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
|
|
||||||
- Select your ESP8266 in "Tools -> Board"
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your network.
|
|
||||||
|
|
||||||
const char* ssid = "........";
|
|
||||||
const char* password = "........";
|
|
||||||
const char* mqtt_server = "broker.mqtt-dashboard.com";
|
|
||||||
|
|
||||||
WiFiClient espClient;
|
|
||||||
PubSubClient client(espClient);
|
|
||||||
long lastMsg = 0;
|
|
||||||
char msg[50];
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
void setup_wifi() {
|
|
||||||
|
|
||||||
delay(10);
|
|
||||||
// We start by connecting to a WiFi network
|
|
||||||
Serial.println();
|
|
||||||
Serial.print("Connecting to ");
|
|
||||||
Serial.println(ssid);
|
|
||||||
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
|
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
|
||||||
delay(500);
|
|
||||||
Serial.print(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
randomSeed(micros());
|
|
||||||
|
|
||||||
Serial.println("");
|
|
||||||
Serial.println("WiFi connected");
|
|
||||||
Serial.println("IP address: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
}
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
Serial.print("Message arrived [");
|
|
||||||
Serial.print(topic);
|
|
||||||
Serial.print("] ");
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
Serial.print((char)payload[i]);
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// Find out how many bottles we should generate lyrics for
|
|
||||||
String topicStr(topic);
|
|
||||||
int bottleCount = 0; // assume no bottles unless we correctly parse a value from the topic
|
|
||||||
if (topicStr.indexOf('/') >= 0) {
|
|
||||||
// The topic includes a '/', we'll try to read the number of bottles from just after that
|
|
||||||
topicStr.remove(0, topicStr.indexOf('/')+1);
|
|
||||||
// Now see if there's a number of bottles after the '/'
|
|
||||||
bottleCount = topicStr.toInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bottleCount > 0) {
|
|
||||||
// Work out how big our resulting message will be
|
|
||||||
int msgLen = 0;
|
|
||||||
for (int i = bottleCount; i > 0; i--) {
|
|
||||||
String numBottles(i);
|
|
||||||
msgLen += 2*numBottles.length();
|
|
||||||
if (i == 1) {
|
|
||||||
msgLen += 2*String(" green bottle, standing on the wall\n").length();
|
|
||||||
} else {
|
|
||||||
msgLen += 2*String(" green bottles, standing on the wall\n").length();
|
|
||||||
}
|
|
||||||
msgLen += String("And if one green bottle should accidentally fall\nThere'll be ").length();
|
|
||||||
switch (i) {
|
|
||||||
case 1:
|
|
||||||
msgLen += String("no green bottles, standing on the wall\n\n").length();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
msgLen += String("1 green bottle, standing on the wall\n\n").length();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
numBottles = i-1;
|
|
||||||
msgLen += numBottles.length();
|
|
||||||
msgLen += String(" green bottles, standing on the wall\n\n").length();
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we can start to publish the message
|
|
||||||
client.beginPublish("greenBottles/lyrics", msgLen, false);
|
|
||||||
for (int i = bottleCount; i > 0; i--) {
|
|
||||||
for (int j = 0; j < 2; j++) {
|
|
||||||
client.print(i);
|
|
||||||
if (i == 1) {
|
|
||||||
client.print(" green bottle, standing on the wall\n");
|
|
||||||
} else {
|
|
||||||
client.print(" green bottles, standing on the wall\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
client.print("And if one green bottle should accidentally fall\nThere'll be ");
|
|
||||||
switch (i) {
|
|
||||||
case 1:
|
|
||||||
client.print("no green bottles, standing on the wall\n\n");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
client.print("1 green bottle, standing on the wall\n\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
client.print(i-1);
|
|
||||||
client.print(" green bottles, standing on the wall\n\n");
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// Now we're done!
|
|
||||||
client.endPublish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void reconnect() {
|
|
||||||
// Loop until we're reconnected
|
|
||||||
while (!client.connected()) {
|
|
||||||
Serial.print("Attempting MQTT connection...");
|
|
||||||
// Create a random client ID
|
|
||||||
String clientId = "ESP8266Client-";
|
|
||||||
clientId += String(random(0xffff), HEX);
|
|
||||||
// Attempt to connect
|
|
||||||
if (client.connect(clientId.c_str())) {
|
|
||||||
Serial.println("connected");
|
|
||||||
// Once connected, publish an announcement...
|
|
||||||
client.publish("outTopic", "hello world");
|
|
||||||
// ... and resubscribe
|
|
||||||
client.subscribe("greenBottles/#");
|
|
||||||
} else {
|
|
||||||
Serial.print("failed, rc=");
|
|
||||||
Serial.print(client.state());
|
|
||||||
Serial.println(" try again in 5 seconds");
|
|
||||||
// Wait 5 seconds before retrying
|
|
||||||
delay(5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output
|
|
||||||
Serial.begin(115200);
|
|
||||||
setup_wifi();
|
|
||||||
client.setServer(mqtt_server, 1883);
|
|
||||||
client.setCallback(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
|
|
||||||
if (!client.connected()) {
|
|
||||||
reconnect();
|
|
||||||
}
|
|
||||||
client.loop();
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
Publishing in the callback
|
|
||||||
|
|
||||||
- connects to an MQTT server
|
|
||||||
- subscribes to the topic "inTopic"
|
|
||||||
- when a message is received, republishes it to "outTopic"
|
|
||||||
|
|
||||||
This example shows how to publish messages within the
|
|
||||||
callback function. The callback function header needs to
|
|
||||||
be declared before the PubSubClient constructor and the
|
|
||||||
actual callback defined afterwards.
|
|
||||||
This ensures the client reference in the callback function
|
|
||||||
is valid.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your network.
|
|
||||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
|
||||||
IPAddress ip(172, 16, 0, 100);
|
|
||||||
IPAddress server(172, 16, 0, 2);
|
|
||||||
|
|
||||||
// Callback function header
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length);
|
|
||||||
|
|
||||||
EthernetClient ethClient;
|
|
||||||
PubSubClient client(server, 1883, callback, ethClient);
|
|
||||||
|
|
||||||
// Callback function
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// In order to republish this payload, a copy must be made
|
|
||||||
// as the orignal payload buffer will be overwritten whilst
|
|
||||||
// constructing the PUBLISH packet.
|
|
||||||
|
|
||||||
// Allocate the correct amount of memory for the payload copy
|
|
||||||
byte* p = (byte*)malloc(length);
|
|
||||||
// Copy the payload to the new buffer
|
|
||||||
memcpy(p,payload,length);
|
|
||||||
client.publish("outTopic", p, length);
|
|
||||||
// Free the memory
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
if (client.connect("arduinoClient")) {
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
client.loop();
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
Reconnecting MQTT example - non-blocking
|
|
||||||
|
|
||||||
This sketch demonstrates how to keep the client connected
|
|
||||||
using a non-blocking reconnect function. If the client loses
|
|
||||||
its connection, it attempts to reconnect every 5 seconds
|
|
||||||
without blocking the main loop.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your hardware/network.
|
|
||||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
|
||||||
IPAddress ip(172, 16, 0, 100);
|
|
||||||
IPAddress server(172, 16, 0, 2);
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
EthernetClient ethClient;
|
|
||||||
PubSubClient client(ethClient);
|
|
||||||
|
|
||||||
long lastReconnectAttempt = 0;
|
|
||||||
|
|
||||||
boolean reconnect() {
|
|
||||||
if (client.connect("arduinoClient")) {
|
|
||||||
// Once connected, publish an announcement...
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
// ... and resubscribe
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
}
|
|
||||||
return client.connected();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
client.setServer(server, 1883);
|
|
||||||
client.setCallback(callback);
|
|
||||||
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
delay(1500);
|
|
||||||
lastReconnectAttempt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
if (!client.connected()) {
|
|
||||||
long now = millis();
|
|
||||||
if (now - lastReconnectAttempt > 5000) {
|
|
||||||
lastReconnectAttempt = now;
|
|
||||||
// Attempt to reconnect
|
|
||||||
if (reconnect()) {
|
|
||||||
lastReconnectAttempt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Client connected
|
|
||||||
|
|
||||||
client.loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
Example of using a Stream object to store the message payload
|
|
||||||
|
|
||||||
Uses SRAM library: https://github.com/ennui2342/arduino-sram
|
|
||||||
but could use any Stream based class such as SD
|
|
||||||
|
|
||||||
- connects to an MQTT server
|
|
||||||
- publishes "hello world" to the topic "outTopic"
|
|
||||||
- subscribes to the topic "inTopic"
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
#include <SRAM.h>
|
|
||||||
|
|
||||||
// Update these with values suitable for your network.
|
|
||||||
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };
|
|
||||||
IPAddress ip(172, 16, 0, 100);
|
|
||||||
IPAddress server(172, 16, 0, 2);
|
|
||||||
|
|
||||||
SRAM sram(4, SRAM_1024);
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
sram.seek(1);
|
|
||||||
|
|
||||||
// do something with the message
|
|
||||||
for(uint8_t i=0; i<length; i++) {
|
|
||||||
Serial.write(sram.read());
|
|
||||||
}
|
|
||||||
Serial.println();
|
|
||||||
|
|
||||||
// Reset position for the next message to be stored
|
|
||||||
sram.seek(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
EthernetClient ethClient;
|
|
||||||
PubSubClient client(server, 1883, callback, ethClient, sram);
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
if (client.connect("arduinoClient")) {
|
|
||||||
client.publish("outTopic","hello world");
|
|
||||||
client.subscribe("inTopic");
|
|
||||||
}
|
|
||||||
|
|
||||||
sram.begin();
|
|
||||||
sram.seek(1);
|
|
||||||
|
|
||||||
Serial.begin(9600);
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop()
|
|
||||||
{
|
|
||||||
client.loop();
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#######################################
|
|
||||||
# Syntax Coloring Map For PubSubClient
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Datatypes (KEYWORD1)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
PubSubClient KEYWORD1
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Methods and Functions (KEYWORD2)
|
|
||||||
#######################################
|
|
||||||
|
|
||||||
connect KEYWORD2
|
|
||||||
disconnect KEYWORD2
|
|
||||||
publish KEYWORD2
|
|
||||||
publish_P KEYWORD2
|
|
||||||
beginPublish KEYWORD2
|
|
||||||
endPublish KEYWORD2
|
|
||||||
write KEYWORD2
|
|
||||||
subscribe KEYWORD2
|
|
||||||
unsubscribe KEYWORD2
|
|
||||||
loop KEYWORD2
|
|
||||||
connected KEYWORD2
|
|
||||||
setServer KEYWORD2
|
|
||||||
setCallback KEYWORD2
|
|
||||||
setClient KEYWORD2
|
|
||||||
setStream KEYWORD2
|
|
||||||
|
|
||||||
#######################################
|
|
||||||
# Constants (LITERAL1)
|
|
||||||
#######################################
|
|
|
@ -1,653 +0,0 @@
|
||||||
/*
|
|
||||||
PubSubClient.cpp - A simple client for MQTT.
|
|
||||||
Nick O'Leary
|
|
||||||
http://knolleary.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient() {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
this->_client = NULL;
|
|
||||||
this->stream = NULL;
|
|
||||||
setCallback(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient(Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(addr, port);
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(addr,port);
|
|
||||||
setClient(client);
|
|
||||||
setStream(stream);
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(addr, port);
|
|
||||||
setCallback(callback);
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(addr,port);
|
|
||||||
setCallback(callback);
|
|
||||||
setClient(client);
|
|
||||||
setStream(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(ip, port);
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(ip,port);
|
|
||||||
setClient(client);
|
|
||||||
setStream(stream);
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(ip, port);
|
|
||||||
setCallback(callback);
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(ip,port);
|
|
||||||
setCallback(callback);
|
|
||||||
setClient(client);
|
|
||||||
setStream(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(domain,port);
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(domain,port);
|
|
||||||
setClient(client);
|
|
||||||
setStream(stream);
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(domain,port);
|
|
||||||
setCallback(callback);
|
|
||||||
setClient(client);
|
|
||||||
this->stream = NULL;
|
|
||||||
}
|
|
||||||
PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
|
|
||||||
this->_state = MQTT_DISCONNECTED;
|
|
||||||
setServer(domain,port);
|
|
||||||
setCallback(callback);
|
|
||||||
setClient(client);
|
|
||||||
setStream(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::connect(const char *id) {
|
|
||||||
return connect(id,NULL,NULL,0,0,0,0,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
|
|
||||||
return connect(id,user,pass,0,0,0,0,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
|
||||||
return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
|
|
||||||
return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
|
|
||||||
if (!connected()) {
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
if (domain != NULL) {
|
|
||||||
result = _client->connect(this->domain, this->port);
|
|
||||||
} else {
|
|
||||||
result = _client->connect(this->ip, this->port);
|
|
||||||
}
|
|
||||||
if (result == 1) {
|
|
||||||
nextMsgId = 1;
|
|
||||||
// Leave room in the buffer for header and variable length field
|
|
||||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
|
||||||
unsigned int j;
|
|
||||||
|
|
||||||
#if MQTT_VERSION == MQTT_VERSION_3_1
|
|
||||||
uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
|
|
||||||
#define MQTT_HEADER_VERSION_LENGTH 9
|
|
||||||
#elif MQTT_VERSION == MQTT_VERSION_3_1_1
|
|
||||||
uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
|
|
||||||
#define MQTT_HEADER_VERSION_LENGTH 7
|
|
||||||
#endif
|
|
||||||
for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
|
|
||||||
buffer[length++] = d[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t v;
|
|
||||||
if (willTopic) {
|
|
||||||
v = 0x04|(willQos<<3)|(willRetain<<5);
|
|
||||||
} else {
|
|
||||||
v = 0x00;
|
|
||||||
}
|
|
||||||
if (cleanSession) {
|
|
||||||
v = v|0x02;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(user != NULL) {
|
|
||||||
v = v|0x80;
|
|
||||||
|
|
||||||
if(pass != NULL) {
|
|
||||||
v = v|(0x80>>1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[length++] = v;
|
|
||||||
|
|
||||||
buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
|
|
||||||
buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
|
|
||||||
|
|
||||||
CHECK_STRING_LENGTH(length,id)
|
|
||||||
length = writeString(id,buffer,length);
|
|
||||||
if (willTopic) {
|
|
||||||
CHECK_STRING_LENGTH(length,willTopic)
|
|
||||||
length = writeString(willTopic,buffer,length);
|
|
||||||
CHECK_STRING_LENGTH(length,willMessage)
|
|
||||||
length = writeString(willMessage,buffer,length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(user != NULL) {
|
|
||||||
CHECK_STRING_LENGTH(length,user)
|
|
||||||
length = writeString(user,buffer,length);
|
|
||||||
if(pass != NULL) {
|
|
||||||
CHECK_STRING_LENGTH(length,pass)
|
|
||||||
length = writeString(pass,buffer,length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
|
|
||||||
|
|
||||||
lastInActivity = lastOutActivity = millis();
|
|
||||||
|
|
||||||
while (!_client->available()) {
|
|
||||||
unsigned long t = millis();
|
|
||||||
if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
|
|
||||||
_state = MQTT_CONNECTION_TIMEOUT;
|
|
||||||
_client->stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t llen;
|
|
||||||
uint16_t len = readPacket(&llen);
|
|
||||||
|
|
||||||
if (len == 4) {
|
|
||||||
if (buffer[3] == 0) {
|
|
||||||
lastInActivity = millis();
|
|
||||||
pingOutstanding = false;
|
|
||||||
_state = MQTT_CONNECTED;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
_state = buffer[3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_client->stop();
|
|
||||||
} else {
|
|
||||||
_state = MQTT_CONNECT_FAILED;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reads a byte into result
|
|
||||||
boolean PubSubClient::readByte(uint8_t * result) {
|
|
||||||
uint32_t previousMillis = millis();
|
|
||||||
while(!_client->available()) {
|
|
||||||
yield();
|
|
||||||
uint32_t currentMillis = millis();
|
|
||||||
if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*result = _client->read();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reads a byte into result[*index] and increments index
|
|
||||||
boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
|
|
||||||
uint16_t current_index = *index;
|
|
||||||
uint8_t * write_address = &(result[current_index]);
|
|
||||||
if(readByte(write_address)){
|
|
||||||
*index = current_index + 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
|
|
||||||
uint16_t len = 0;
|
|
||||||
if(!readByte(buffer, &len)) return 0;
|
|
||||||
bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
|
|
||||||
uint32_t multiplier = 1;
|
|
||||||
uint16_t length = 0;
|
|
||||||
uint8_t digit = 0;
|
|
||||||
uint16_t skip = 0;
|
|
||||||
uint8_t start = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (len == 5) {
|
|
||||||
// Invalid remaining length encoding - kill the connection
|
|
||||||
_state = MQTT_DISCONNECTED;
|
|
||||||
_client->stop();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(!readByte(&digit)) return 0;
|
|
||||||
buffer[len++] = digit;
|
|
||||||
length += (digit & 127) * multiplier;
|
|
||||||
multiplier *= 128;
|
|
||||||
} while ((digit & 128) != 0);
|
|
||||||
*lengthLength = len-1;
|
|
||||||
|
|
||||||
if (isPublish) {
|
|
||||||
// Read in topic length to calculate bytes to skip over for Stream writing
|
|
||||||
if(!readByte(buffer, &len)) return 0;
|
|
||||||
if(!readByte(buffer, &len)) return 0;
|
|
||||||
skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
|
|
||||||
start = 2;
|
|
||||||
if (buffer[0]&MQTTQOS1) {
|
|
||||||
// skip message id
|
|
||||||
skip += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint16_t i = start;i<length;i++) {
|
|
||||||
if(!readByte(&digit)) return 0;
|
|
||||||
if (this->stream) {
|
|
||||||
if (isPublish && len-*lengthLength-2>skip) {
|
|
||||||
this->stream->write(digit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len < MQTT_MAX_PACKET_SIZE) {
|
|
||||||
buffer[len] = digit;
|
|
||||||
}
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
|
|
||||||
len = 0; // This will cause the packet to be ignored.
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::loop() {
|
|
||||||
if (connected()) {
|
|
||||||
unsigned long t = millis();
|
|
||||||
if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
|
|
||||||
if (pingOutstanding) {
|
|
||||||
this->_state = MQTT_CONNECTION_TIMEOUT;
|
|
||||||
_client->stop();
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
buffer[0] = MQTTPINGREQ;
|
|
||||||
buffer[1] = 0;
|
|
||||||
_client->write(buffer,2);
|
|
||||||
lastOutActivity = t;
|
|
||||||
lastInActivity = t;
|
|
||||||
pingOutstanding = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_client->available()) {
|
|
||||||
uint8_t llen;
|
|
||||||
uint16_t len = readPacket(&llen);
|
|
||||||
uint16_t msgId = 0;
|
|
||||||
uint8_t *payload;
|
|
||||||
if (len > 0) {
|
|
||||||
lastInActivity = t;
|
|
||||||
uint8_t type = buffer[0]&0xF0;
|
|
||||||
if (type == MQTTPUBLISH) {
|
|
||||||
if (callback) {
|
|
||||||
uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
|
|
||||||
memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
|
|
||||||
buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
|
|
||||||
char *topic = (char*) buffer+llen+2;
|
|
||||||
// msgId only present for QOS>0
|
|
||||||
if ((buffer[0]&0x06) == MQTTQOS1) {
|
|
||||||
msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
|
|
||||||
payload = buffer+llen+3+tl+2;
|
|
||||||
callback(topic,payload,len-llen-3-tl-2);
|
|
||||||
|
|
||||||
buffer[0] = MQTTPUBACK;
|
|
||||||
buffer[1] = 2;
|
|
||||||
buffer[2] = (msgId >> 8);
|
|
||||||
buffer[3] = (msgId & 0xFF);
|
|
||||||
_client->write(buffer,4);
|
|
||||||
lastOutActivity = t;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
payload = buffer+llen+3+tl;
|
|
||||||
callback(topic,payload,len-llen-3-tl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type == MQTTPINGREQ) {
|
|
||||||
buffer[0] = MQTTPINGRESP;
|
|
||||||
buffer[1] = 0;
|
|
||||||
_client->write(buffer,2);
|
|
||||||
} else if (type == MQTTPINGRESP) {
|
|
||||||
pingOutstanding = false;
|
|
||||||
}
|
|
||||||
} else if (!connected()) {
|
|
||||||
// readPacket has closed the connection
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(const char* topic, const char* payload) {
|
|
||||||
return publish(topic,(const uint8_t*)payload,strlen(payload),false);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
|
|
||||||
return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
|
|
||||||
return publish(topic, payload, plength, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
|
||||||
if (connected()) {
|
|
||||||
if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) {
|
|
||||||
// Too long
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Leave room in the buffer for header and variable length field
|
|
||||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
|
||||||
length = writeString(topic,buffer,length);
|
|
||||||
uint16_t i;
|
|
||||||
for (i=0;i<plength;i++) {
|
|
||||||
buffer[length++] = payload[i];
|
|
||||||
}
|
|
||||||
uint8_t header = MQTTPUBLISH;
|
|
||||||
if (retained) {
|
|
||||||
header |= 1;
|
|
||||||
}
|
|
||||||
return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
|
|
||||||
return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
|
|
||||||
uint8_t llen = 0;
|
|
||||||
uint8_t digit;
|
|
||||||
unsigned int rc = 0;
|
|
||||||
uint16_t tlen;
|
|
||||||
unsigned int pos = 0;
|
|
||||||
unsigned int i;
|
|
||||||
uint8_t header;
|
|
||||||
unsigned int len;
|
|
||||||
|
|
||||||
if (!connected()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tlen = strlen(topic);
|
|
||||||
|
|
||||||
header = MQTTPUBLISH;
|
|
||||||
if (retained) {
|
|
||||||
header |= 1;
|
|
||||||
}
|
|
||||||
buffer[pos++] = header;
|
|
||||||
len = plength + 2 + tlen;
|
|
||||||
do {
|
|
||||||
digit = len % 128;
|
|
||||||
len = len / 128;
|
|
||||||
if (len > 0) {
|
|
||||||
digit |= 0x80;
|
|
||||||
}
|
|
||||||
buffer[pos++] = digit;
|
|
||||||
llen++;
|
|
||||||
} while(len>0);
|
|
||||||
|
|
||||||
pos = writeString(topic,buffer,pos);
|
|
||||||
|
|
||||||
rc += _client->write(buffer,pos);
|
|
||||||
|
|
||||||
for (i=0;i<plength;i++) {
|
|
||||||
rc += _client->write((char)pgm_read_byte_near(payload + i));
|
|
||||||
}
|
|
||||||
|
|
||||||
lastOutActivity = millis();
|
|
||||||
|
|
||||||
return rc == tlen + 4 + plength;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
|
|
||||||
if (connected()) {
|
|
||||||
// Send the header and variable length field
|
|
||||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
|
||||||
length = writeString(topic,buffer,length);
|
|
||||||
uint16_t i;
|
|
||||||
uint8_t header = MQTTPUBLISH;
|
|
||||||
if (retained) {
|
|
||||||
header |= 1;
|
|
||||||
}
|
|
||||||
size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
|
|
||||||
uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
|
|
||||||
lastOutActivity = millis();
|
|
||||||
return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PubSubClient::endPublish() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PubSubClient::write(uint8_t data) {
|
|
||||||
lastOutActivity = millis();
|
|
||||||
return _client->write(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
|
|
||||||
lastOutActivity = millis();
|
|
||||||
return _client->write(buffer,size);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
|
|
||||||
uint8_t lenBuf[4];
|
|
||||||
uint8_t llen = 0;
|
|
||||||
uint8_t digit;
|
|
||||||
uint8_t pos = 0;
|
|
||||||
uint16_t len = length;
|
|
||||||
do {
|
|
||||||
digit = len % 128;
|
|
||||||
len = len / 128;
|
|
||||||
if (len > 0) {
|
|
||||||
digit |= 0x80;
|
|
||||||
}
|
|
||||||
lenBuf[pos++] = digit;
|
|
||||||
llen++;
|
|
||||||
} while(len>0);
|
|
||||||
|
|
||||||
buf[4-llen] = header;
|
|
||||||
for (int i=0;i<llen;i++) {
|
|
||||||
buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
|
|
||||||
}
|
|
||||||
return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
|
|
||||||
uint16_t rc;
|
|
||||||
uint8_t hlen = buildHeader(header, buf, length);
|
|
||||||
|
|
||||||
#ifdef MQTT_MAX_TRANSFER_SIZE
|
|
||||||
uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
|
|
||||||
uint16_t bytesRemaining = length+hlen; //Match the length type
|
|
||||||
uint8_t bytesToWrite;
|
|
||||||
boolean result = true;
|
|
||||||
while((bytesRemaining > 0) && result) {
|
|
||||||
bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
|
|
||||||
rc = _client->write(writeBuf,bytesToWrite);
|
|
||||||
result = (rc == bytesToWrite);
|
|
||||||
bytesRemaining -= rc;
|
|
||||||
writeBuf += rc;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
#else
|
|
||||||
rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
|
|
||||||
lastOutActivity = millis();
|
|
||||||
return (rc == hlen+length);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::subscribe(const char* topic) {
|
|
||||||
return subscribe(topic, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
|
|
||||||
if (qos > 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
|
||||||
// Too long
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (connected()) {
|
|
||||||
// Leave room in the buffer for header and variable length field
|
|
||||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
|
||||||
nextMsgId++;
|
|
||||||
if (nextMsgId == 0) {
|
|
||||||
nextMsgId = 1;
|
|
||||||
}
|
|
||||||
buffer[length++] = (nextMsgId >> 8);
|
|
||||||
buffer[length++] = (nextMsgId & 0xFF);
|
|
||||||
length = writeString((char*)topic, buffer,length);
|
|
||||||
buffer[length++] = qos;
|
|
||||||
return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean PubSubClient::unsubscribe(const char* topic) {
|
|
||||||
if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
|
|
||||||
// Too long
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (connected()) {
|
|
||||||
uint16_t length = MQTT_MAX_HEADER_SIZE;
|
|
||||||
nextMsgId++;
|
|
||||||
if (nextMsgId == 0) {
|
|
||||||
nextMsgId = 1;
|
|
||||||
}
|
|
||||||
buffer[length++] = (nextMsgId >> 8);
|
|
||||||
buffer[length++] = (nextMsgId & 0xFF);
|
|
||||||
length = writeString(topic, buffer,length);
|
|
||||||
return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PubSubClient::disconnect() {
|
|
||||||
buffer[0] = MQTTDISCONNECT;
|
|
||||||
buffer[1] = 0;
|
|
||||||
_client->write(buffer,2);
|
|
||||||
_state = MQTT_DISCONNECTED;
|
|
||||||
_client->flush();
|
|
||||||
_client->stop();
|
|
||||||
lastInActivity = lastOutActivity = millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
|
|
||||||
const char* idp = string;
|
|
||||||
uint16_t i = 0;
|
|
||||||
pos += 2;
|
|
||||||
while (*idp) {
|
|
||||||
buf[pos++] = *idp++;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
buf[pos-i-2] = (i >> 8);
|
|
||||||
buf[pos-i-1] = (i & 0xFF);
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
boolean PubSubClient::connected() {
|
|
||||||
boolean rc;
|
|
||||||
if (_client == NULL ) {
|
|
||||||
rc = false;
|
|
||||||
} else {
|
|
||||||
rc = (int)_client->connected();
|
|
||||||
if (!rc) {
|
|
||||||
if (this->_state == MQTT_CONNECTED) {
|
|
||||||
this->_state = MQTT_CONNECTION_LOST;
|
|
||||||
_client->flush();
|
|
||||||
_client->stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
|
|
||||||
IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
|
|
||||||
return setServer(addr,port);
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
|
|
||||||
this->ip = ip;
|
|
||||||
this->port = port;
|
|
||||||
this->domain = NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
|
|
||||||
this->domain = domain;
|
|
||||||
this->port = port;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
|
|
||||||
this->callback = callback;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient& PubSubClient::setClient(Client& client){
|
|
||||||
this->_client = &client;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PubSubClient& PubSubClient::setStream(Stream& stream){
|
|
||||||
this->stream = &stream;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PubSubClient::state() {
|
|
||||||
return this->_state;
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
PubSubClient.h - A simple client for MQTT.
|
|
||||||
Nick O'Leary
|
|
||||||
http://knolleary.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PubSubClient_h
|
|
||||||
#define PubSubClient_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "IPAddress.h"
|
|
||||||
#include "Client.h"
|
|
||||||
#include "Stream.h"
|
|
||||||
|
|
||||||
#define MQTT_VERSION_3_1 3
|
|
||||||
#define MQTT_VERSION_3_1_1 4
|
|
||||||
|
|
||||||
// MQTT_VERSION : Pick the version
|
|
||||||
//#define MQTT_VERSION MQTT_VERSION_3_1
|
|
||||||
#ifndef MQTT_VERSION
|
|
||||||
#define MQTT_VERSION MQTT_VERSION_3_1_1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size
|
|
||||||
#ifndef MQTT_MAX_PACKET_SIZE
|
|
||||||
//#define MQTT_MAX_PACKET_SIZE 128
|
|
||||||
#define MQTT_MAX_PACKET_SIZE 1200
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds
|
|
||||||
#ifndef MQTT_KEEPALIVE
|
|
||||||
#define MQTT_KEEPALIVE 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
|
|
||||||
#ifndef MQTT_SOCKET_TIMEOUT
|
|
||||||
#define MQTT_SOCKET_TIMEOUT 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
|
|
||||||
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
|
|
||||||
// pass the entire MQTT packet in each write call.
|
|
||||||
//#define MQTT_MAX_TRANSFER_SIZE 80
|
|
||||||
|
|
||||||
// Possible values for client.state()
|
|
||||||
#define MQTT_CONNECTION_TIMEOUT -4
|
|
||||||
#define MQTT_CONNECTION_LOST -3
|
|
||||||
#define MQTT_CONNECT_FAILED -2
|
|
||||||
#define MQTT_DISCONNECTED -1
|
|
||||||
#define MQTT_CONNECTED 0
|
|
||||||
#define MQTT_CONNECT_BAD_PROTOCOL 1
|
|
||||||
#define MQTT_CONNECT_BAD_CLIENT_ID 2
|
|
||||||
#define MQTT_CONNECT_UNAVAILABLE 3
|
|
||||||
#define MQTT_CONNECT_BAD_CREDENTIALS 4
|
|
||||||
#define MQTT_CONNECT_UNAUTHORIZED 5
|
|
||||||
|
|
||||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
|
||||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
|
||||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
|
||||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
|
||||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
|
||||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
|
||||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
|
||||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
|
||||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
|
||||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
|
||||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
|
||||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
|
||||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
|
||||||
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
|
|
||||||
#define MQTTReserved 15 << 4 // Reserved
|
|
||||||
|
|
||||||
#define MQTTQOS0 (0 << 1)
|
|
||||||
#define MQTTQOS1 (1 << 1)
|
|
||||||
#define MQTTQOS2 (2 << 1)
|
|
||||||
|
|
||||||
// Maximum size of fixed header and variable length size header
|
|
||||||
#define MQTT_MAX_HEADER_SIZE 5
|
|
||||||
|
|
||||||
#if defined(ESP8266) || defined(ESP32)
|
|
||||||
#include <functional>
|
|
||||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
|
||||||
#else
|
|
||||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
|
|
||||||
|
|
||||||
class PubSubClient : public Print {
|
|
||||||
private:
|
|
||||||
Client* _client;
|
|
||||||
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
|
||||||
uint16_t nextMsgId;
|
|
||||||
unsigned long lastOutActivity;
|
|
||||||
unsigned long lastInActivity;
|
|
||||||
bool pingOutstanding;
|
|
||||||
MQTT_CALLBACK_SIGNATURE;
|
|
||||||
uint16_t readPacket(uint8_t*);
|
|
||||||
boolean readByte(uint8_t * result);
|
|
||||||
boolean readByte(uint8_t * result, uint16_t * index);
|
|
||||||
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
|
|
||||||
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
|
|
||||||
// Build up the header ready to send
|
|
||||||
// Returns the size of the header
|
|
||||||
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
|
|
||||||
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
|
|
||||||
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
|
|
||||||
IPAddress ip;
|
|
||||||
const char* domain;
|
|
||||||
uint16_t port;
|
|
||||||
Stream* stream;
|
|
||||||
int _state;
|
|
||||||
public:
|
|
||||||
PubSubClient();
|
|
||||||
PubSubClient(Client& client);
|
|
||||||
PubSubClient(IPAddress, uint16_t, Client& client);
|
|
||||||
PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
|
|
||||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
|
||||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, Client& client);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
|
||||||
PubSubClient(const char*, uint16_t, Client& client);
|
|
||||||
PubSubClient(const char*, uint16_t, Client& client, Stream&);
|
|
||||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
|
||||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
|
||||||
|
|
||||||
PubSubClient& setServer(IPAddress ip, uint16_t port);
|
|
||||||
PubSubClient& setServer(uint8_t * ip, uint16_t port);
|
|
||||||
PubSubClient& setServer(const char * domain, uint16_t port);
|
|
||||||
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
|
|
||||||
PubSubClient& setClient(Client& client);
|
|
||||||
PubSubClient& setStream(Stream& stream);
|
|
||||||
|
|
||||||
boolean connect(const char* id);
|
|
||||||
boolean connect(const char* id, const char* user, const char* pass);
|
|
||||||
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
|
||||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
|
||||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
|
|
||||||
void disconnect();
|
|
||||||
boolean publish(const char* topic, const char* payload);
|
|
||||||
boolean publish(const char* topic, const char* payload, boolean retained);
|
|
||||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
|
|
||||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
|
||||||
boolean publish_P(const char* topic, const char* payload, boolean retained);
|
|
||||||
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
|
||||||
// Start to publish a message.
|
|
||||||
// This API:
|
|
||||||
// beginPublish(...)
|
|
||||||
// one or more calls to write(...)
|
|
||||||
// endPublish()
|
|
||||||
// Allows for arbitrarily large payloads to be sent without them having to be copied into
|
|
||||||
// a new buffer and held in memory at one time
|
|
||||||
// Returns 1 if the message was started successfully, 0 if there was an error
|
|
||||||
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
|
|
||||||
// Finish off this publish message (started with beginPublish)
|
|
||||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
|
||||||
int endPublish();
|
|
||||||
// Write a single byte of payload (only to be used with beginPublish/endPublish)
|
|
||||||
virtual size_t write(uint8_t);
|
|
||||||
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
|
|
||||||
// Returns the number of bytes written
|
|
||||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
|
||||||
boolean subscribe(const char* topic);
|
|
||||||
boolean subscribe(const char* topic, uint8_t qos);
|
|
||||||
boolean unsubscribe(const char* topic);
|
|
||||||
boolean loop();
|
|
||||||
boolean connected();
|
|
||||||
int state();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,173 +0,0 @@
|
||||||
/*
|
|
||||||
PubSubClient.h - A simple client for MQTT.
|
|
||||||
Nick O'Leary
|
|
||||||
http://knolleary.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PubSubClient_h
|
|
||||||
#define PubSubClient_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "IPAddress.h"
|
|
||||||
#include "Client.h"
|
|
||||||
#include "Stream.h"
|
|
||||||
|
|
||||||
#define MQTT_VERSION_3_1 3
|
|
||||||
#define MQTT_VERSION_3_1_1 4
|
|
||||||
|
|
||||||
// MQTT_VERSION : Pick the version
|
|
||||||
//#define MQTT_VERSION MQTT_VERSION_3_1
|
|
||||||
#ifndef MQTT_VERSION
|
|
||||||
#define MQTT_VERSION MQTT_VERSION_3_1_1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_MAX_PACKET_SIZE : Maximum packet size
|
|
||||||
#ifndef MQTT_MAX_PACKET_SIZE
|
|
||||||
#define MQTT_MAX_PACKET_SIZE 128
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_KEEPALIVE : keepAlive interval in Seconds
|
|
||||||
#ifndef MQTT_KEEPALIVE
|
|
||||||
#define MQTT_KEEPALIVE 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
|
|
||||||
#ifndef MQTT_SOCKET_TIMEOUT
|
|
||||||
#define MQTT_SOCKET_TIMEOUT 15
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
|
|
||||||
// in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
|
|
||||||
// pass the entire MQTT packet in each write call.
|
|
||||||
//#define MQTT_MAX_TRANSFER_SIZE 80
|
|
||||||
|
|
||||||
// Possible values for client.state()
|
|
||||||
#define MQTT_CONNECTION_TIMEOUT -4
|
|
||||||
#define MQTT_CONNECTION_LOST -3
|
|
||||||
#define MQTT_CONNECT_FAILED -2
|
|
||||||
#define MQTT_DISCONNECTED -1
|
|
||||||
#define MQTT_CONNECTED 0
|
|
||||||
#define MQTT_CONNECT_BAD_PROTOCOL 1
|
|
||||||
#define MQTT_CONNECT_BAD_CLIENT_ID 2
|
|
||||||
#define MQTT_CONNECT_UNAVAILABLE 3
|
|
||||||
#define MQTT_CONNECT_BAD_CREDENTIALS 4
|
|
||||||
#define MQTT_CONNECT_UNAUTHORIZED 5
|
|
||||||
|
|
||||||
#define MQTTCONNECT 1 << 4 // Client request to connect to Server
|
|
||||||
#define MQTTCONNACK 2 << 4 // Connect Acknowledgment
|
|
||||||
#define MQTTPUBLISH 3 << 4 // Publish message
|
|
||||||
#define MQTTPUBACK 4 << 4 // Publish Acknowledgment
|
|
||||||
#define MQTTPUBREC 5 << 4 // Publish Received (assured delivery part 1)
|
|
||||||
#define MQTTPUBREL 6 << 4 // Publish Release (assured delivery part 2)
|
|
||||||
#define MQTTPUBCOMP 7 << 4 // Publish Complete (assured delivery part 3)
|
|
||||||
#define MQTTSUBSCRIBE 8 << 4 // Client Subscribe request
|
|
||||||
#define MQTTSUBACK 9 << 4 // Subscribe Acknowledgment
|
|
||||||
#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
|
|
||||||
#define MQTTUNSUBACK 11 << 4 // Unsubscribe Acknowledgment
|
|
||||||
#define MQTTPINGREQ 12 << 4 // PING Request
|
|
||||||
#define MQTTPINGRESP 13 << 4 // PING Response
|
|
||||||
#define MQTTDISCONNECT 14 << 4 // Client is Disconnecting
|
|
||||||
#define MQTTReserved 15 << 4 // Reserved
|
|
||||||
|
|
||||||
#define MQTTQOS0 (0 << 1)
|
|
||||||
#define MQTTQOS1 (1 << 1)
|
|
||||||
#define MQTTQOS2 (2 << 1)
|
|
||||||
|
|
||||||
// Maximum size of fixed header and variable length size header
|
|
||||||
#define MQTT_MAX_HEADER_SIZE 5
|
|
||||||
|
|
||||||
#if defined(ESP8266) || defined(ESP32)
|
|
||||||
#include <functional>
|
|
||||||
#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
|
|
||||||
#else
|
|
||||||
#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
|
|
||||||
|
|
||||||
class PubSubClient : public Print {
|
|
||||||
private:
|
|
||||||
Client* _client;
|
|
||||||
uint8_t buffer[MQTT_MAX_PACKET_SIZE];
|
|
||||||
uint16_t nextMsgId;
|
|
||||||
unsigned long lastOutActivity;
|
|
||||||
unsigned long lastInActivity;
|
|
||||||
bool pingOutstanding;
|
|
||||||
MQTT_CALLBACK_SIGNATURE;
|
|
||||||
uint16_t readPacket(uint8_t*);
|
|
||||||
boolean readByte(uint8_t * result);
|
|
||||||
boolean readByte(uint8_t * result, uint16_t * index);
|
|
||||||
boolean write(uint8_t header, uint8_t* buf, uint16_t length);
|
|
||||||
uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
|
|
||||||
// Build up the header ready to send
|
|
||||||
// Returns the size of the header
|
|
||||||
// Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
|
|
||||||
// (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
|
|
||||||
size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
|
|
||||||
IPAddress ip;
|
|
||||||
const char* domain;
|
|
||||||
uint16_t port;
|
|
||||||
Stream* stream;
|
|
||||||
int _state;
|
|
||||||
public:
|
|
||||||
PubSubClient();
|
|
||||||
PubSubClient(Client& client);
|
|
||||||
PubSubClient(IPAddress, uint16_t, Client& client);
|
|
||||||
PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
|
|
||||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
|
||||||
PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, Client& client);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
|
||||||
PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
|
||||||
PubSubClient(const char*, uint16_t, Client& client);
|
|
||||||
PubSubClient(const char*, uint16_t, Client& client, Stream&);
|
|
||||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
|
|
||||||
PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
|
|
||||||
|
|
||||||
PubSubClient& setServer(IPAddress ip, uint16_t port);
|
|
||||||
PubSubClient& setServer(uint8_t * ip, uint16_t port);
|
|
||||||
PubSubClient& setServer(const char * domain, uint16_t port);
|
|
||||||
PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
|
|
||||||
PubSubClient& setClient(Client& client);
|
|
||||||
PubSubClient& setStream(Stream& stream);
|
|
||||||
|
|
||||||
boolean connect(const char* id);
|
|
||||||
boolean connect(const char* id, const char* user, const char* pass);
|
|
||||||
boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
|
||||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
|
|
||||||
boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
|
|
||||||
void disconnect();
|
|
||||||
boolean publish(const char* topic, const char* payload);
|
|
||||||
boolean publish(const char* topic, const char* payload, boolean retained);
|
|
||||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
|
|
||||||
boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
|
||||||
boolean publish_P(const char* topic, const char* payload, boolean retained);
|
|
||||||
boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
|
|
||||||
// Start to publish a message.
|
|
||||||
// This API:
|
|
||||||
// beginPublish(...)
|
|
||||||
// one or more calls to write(...)
|
|
||||||
// endPublish()
|
|
||||||
// Allows for arbitrarily large payloads to be sent without them having to be copied into
|
|
||||||
// a new buffer and held in memory at one time
|
|
||||||
// Returns 1 if the message was started successfully, 0 if there was an error
|
|
||||||
boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
|
|
||||||
// Finish off this publish message (started with beginPublish)
|
|
||||||
// Returns 1 if the packet was sent successfully, 0 if there was an error
|
|
||||||
int endPublish();
|
|
||||||
// Write a single byte of payload (only to be used with beginPublish/endPublish)
|
|
||||||
virtual size_t write(uint8_t);
|
|
||||||
// Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
|
|
||||||
// Returns the number of bytes written
|
|
||||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
|
||||||
boolean subscribe(const char* topic);
|
|
||||||
boolean subscribe(const char* topic, uint8_t qos);
|
|
||||||
boolean unsubscribe(const char* topic);
|
|
||||||
boolean loop();
|
|
||||||
boolean connected();
|
|
||||||
int state();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
4
dep/pubsubclient-2.7/tests/.gitignore
vendored
4
dep/pubsubclient-2.7/tests/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
||||||
.build
|
|
||||||
tmpbin
|
|
||||||
logs
|
|
||||||
*.pyc
|
|
|
@ -1,25 +0,0 @@
|
||||||
SRC_PATH=./src
|
|
||||||
OUT_PATH=./bin
|
|
||||||
TEST_SRC=$(wildcard ${SRC_PATH}/*_spec.cpp)
|
|
||||||
TEST_BIN= $(TEST_SRC:${SRC_PATH}/%.cpp=${OUT_PATH}/%)
|
|
||||||
VPATH=${SRC_PATH}
|
|
||||||
SHIM_FILES=${SRC_PATH}/lib/*.cpp
|
|
||||||
PSC_FILE=../src/PubSubClient.cpp
|
|
||||||
CC=g++
|
|
||||||
CFLAGS=-I${SRC_PATH}/lib -I../src
|
|
||||||
|
|
||||||
all: $(TEST_BIN)
|
|
||||||
|
|
||||||
${OUT_PATH}/%: ${SRC_PATH}/%.cpp ${PSC_FILE} ${SHIM_FILES}
|
|
||||||
mkdir -p ${OUT_PATH}
|
|
||||||
${CC} ${CFLAGS} $^ -o $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
@rm -rf ${OUT_PATH}
|
|
||||||
|
|
||||||
test:
|
|
||||||
@bin/connect_spec
|
|
||||||
@bin/publish_spec
|
|
||||||
@bin/receive_spec
|
|
||||||
@bin/subscribe_spec
|
|
||||||
@bin/keepalive_spec
|
|
|
@ -1,93 +0,0 @@
|
||||||
# Arduino Client for MQTT Test Suite
|
|
||||||
|
|
||||||
This is a regression test suite for the `PubSubClient` library.
|
|
||||||
|
|
||||||
There are two parts:
|
|
||||||
|
|
||||||
- Tests that can be compiled and run on any machine
|
|
||||||
- Tests that build the example sketches using the Arduino IDE
|
|
||||||
|
|
||||||
|
|
||||||
It is a work-in-progress and is subject to complete refactoring as the whim takes
|
|
||||||
me.
|
|
||||||
|
|
||||||
|
|
||||||
## Local tests
|
|
||||||
|
|
||||||
These are a set of executables that can be run to test specific areas of functionality.
|
|
||||||
They do not require a real Arduino to be attached, nor the use of the Arduino IDE.
|
|
||||||
|
|
||||||
The tests include a set of mock files to stub out the parts of the Arduino environment the library
|
|
||||||
depends on.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- g++
|
|
||||||
|
|
||||||
### Running
|
|
||||||
|
|
||||||
Build the tests using the provided `Makefile`:
|
|
||||||
|
|
||||||
$ make
|
|
||||||
|
|
||||||
This will create a set of executables in `./bin/`. Run each of these executables to test the corresponding functionality.
|
|
||||||
|
|
||||||
*Note:* the `connect_spec` and `keepalive_spec` tests involve testing keepalive timers so naturally take a few minutes to run through.
|
|
||||||
|
|
||||||
## Arduino tests
|
|
||||||
|
|
||||||
*Note:* INO Tool doesn't currently play nicely with Arduino 1.5. This has broken this test suite.
|
|
||||||
|
|
||||||
Without a suitable arduino plugged in, the test suite will only check the
|
|
||||||
example sketches compile cleanly against the library.
|
|
||||||
|
|
||||||
With an arduino plugged in, each sketch that has a corresponding python
|
|
||||||
test case is built, uploaded and then the tests run.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- Python 2.7+
|
|
||||||
- [INO Tool](http://inotool.org/) - this provides command-line build/upload of Arduino sketches
|
|
||||||
|
|
||||||
### Running
|
|
||||||
|
|
||||||
The test suite _does not_ run an MQTT server - it is assumed to be running already.
|
|
||||||
|
|
||||||
$ python testsuite.py
|
|
||||||
|
|
||||||
A summary of activity is printed to the console. More comprehensive logs are written
|
|
||||||
to the `logs` directory.
|
|
||||||
|
|
||||||
### What it does
|
|
||||||
|
|
||||||
For each sketch in the library's `examples` directory, e.g. `mqtt_basic.ino`, the suite looks for a matching test case
|
|
||||||
`testcases/mqtt_basic.py`.
|
|
||||||
|
|
||||||
The test case must follow these conventions:
|
|
||||||
- sub-class `unittest.TestCase`
|
|
||||||
- provide the class methods `setUpClass` and `tearDownClass` (TODO: make this optional)
|
|
||||||
- all test method names begin with `test_`
|
|
||||||
|
|
||||||
The suite will call the `setUpClass` method _before_ uploading the sketch. This
|
|
||||||
allows any test setup to be performed before the sketch runs - such as connecting
|
|
||||||
a client and subscribing to topics.
|
|
||||||
|
|
||||||
|
|
||||||
### Settings
|
|
||||||
|
|
||||||
The file `testcases/settings.py` is used to config the test environment.
|
|
||||||
|
|
||||||
- `server_ip` - the IP address of the broker the client should connect to (the broker port is assumed to be 1883).
|
|
||||||
- `arduino_ip` - the IP address the arduino should use (when not testing DHCP).
|
|
||||||
|
|
||||||
Before each sketch is compiled, these values are automatically substituted in. To
|
|
||||||
do this, the suite looks for lines that _start_ with the following:
|
|
||||||
|
|
||||||
byte server[] = {
|
|
||||||
byte ip[] = {
|
|
||||||
|
|
||||||
and replaces them with the appropriate values.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,302 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_connect_fails_no_network() {
|
|
||||||
IT("fails to connect if underlying client doesn't connect");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(false);
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECT_FAILED);
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_fails_on_no_response() {
|
|
||||||
IT("fails to connect if no response received after 15 seconds");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_properly_formatted() {
|
|
||||||
IT("sends a properly formatted connect packet and succeeds");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte expectServer[] = { 172, 16, 0, 2 };
|
|
||||||
shimClient.expectConnect(expectServer,1883);
|
|
||||||
byte connect[] = {0x10,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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
|
|
||||||
shimClient.expect(connect,26);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
|
||||||
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECTED);
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_properly_formatted_hostname() {
|
|
||||||
IT("accepts a hostname");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
shimClient.expectConnect((char* const)"localhost",1883);
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client((char* const)"localhost", 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_connect_fails_on_bad_rc() {
|
|
||||||
IT("fails to connect if a bad return code is received");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x01 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == 0x01);
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_non_clean_session() {
|
|
||||||
IT("sends a properly formatted non-clean session connect packet and succeeds");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte expectServer[] = { 172, 16, 0, 2 };
|
|
||||||
shimClient.expectConnect(expectServer,1883);
|
|
||||||
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
|
|
||||||
shimClient.expect(connect,26);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
|
||||||
|
|
||||||
int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECTED);
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_accepts_username_password() {
|
|
||||||
IT("accepts a username and password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x26);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_accepts_username_no_password() {
|
|
||||||
IT("accepts a username but no password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x20);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",0);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
int test_connect_accepts_username_blank_password() {
|
|
||||||
IT("accepts a username and blank password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = { 0x10,0x20,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x0};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x26);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_ignores_password_no_username() {
|
|
||||||
IT("ignores a password but no username");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,26);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",0,(char*)"pass");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_with_will() {
|
|
||||||
IT("accepts a will");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = {0x10,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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x32);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"willTopic",1,0,(char*)"willMessage");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_with_will_username_password() {
|
|
||||||
IT("accepts a will, username and password");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connect[] = {0x10,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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.expect(connect,0x42);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"password",(char*)"willTopic",1,0,(char*)"willMessage");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_connect_disconnect_connect() {
|
|
||||||
IT("connects, disconnects and connects again");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
byte expectServer[] = { 172, 16, 0, 2 };
|
|
||||||
shimClient.expectConnect(expectServer,1883);
|
|
||||||
byte connect[] = {0x10,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};
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
|
|
||||||
shimClient.expect(connect,26);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
|
||||||
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECTED);
|
|
||||||
|
|
||||||
byte disconnect[] = {0xE0,0x00};
|
|
||||||
shimClient.expect(disconnect,2);
|
|
||||||
|
|
||||||
client.disconnect();
|
|
||||||
|
|
||||||
IS_FALSE(client.connected());
|
|
||||||
IS_FALSE(shimClient.connected());
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
|
||||||
|
|
||||||
shimClient.expect(connect,28);
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECTED);
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
SUITE("Connect");
|
|
||||||
|
|
||||||
test_connect_fails_no_network();
|
|
||||||
test_connect_fails_on_no_response();
|
|
||||||
|
|
||||||
test_connect_properly_formatted();
|
|
||||||
test_connect_non_clean_session();
|
|
||||||
test_connect_accepts_username_password();
|
|
||||||
test_connect_fails_on_bad_rc();
|
|
||||||
test_connect_properly_formatted_hostname();
|
|
||||||
|
|
||||||
test_connect_accepts_username_no_password();
|
|
||||||
test_connect_ignores_password_no_username();
|
|
||||||
test_connect_with_will();
|
|
||||||
test_connect_with_will_username_password();
|
|
||||||
test_connect_disconnect_connect();
|
|
||||||
FINISH
|
|
||||||
}
|
|
|
@ -1,185 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_keepalive_pings_idle() {
|
|
||||||
IT("keeps an idle connection alive (takes 1 minute)");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
byte pingresp[] = { 0xD0,0x0 };
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
sleep(1);
|
|
||||||
if ( i == 15 || i == 31 || i == 47) {
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
}
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_pings_with_outbound_qos0() {
|
|
||||||
IT("keeps a connection alive that only sends qos0 (takes 1 minute)");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
TRACE(i<<":");
|
|
||||||
shimClient.expect(publish,16);
|
|
||||||
rc = client.publish((char*)"topic",(char*)"payload");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
sleep(1);
|
|
||||||
if ( i == 15 || i == 31 || i == 47) {
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
byte pingresp[] = { 0xD0,0x0 };
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
}
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_pings_with_inbound_qos0() {
|
|
||||||
IT("keeps a connection alive that only receives qos0 (takes 1 minute)");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
TRACE(i<<":");
|
|
||||||
sleep(1);
|
|
||||||
if ( i == 15 || i == 31 || i == 47) {
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
byte pingresp[] = { 0xD0,0x0 };
|
|
||||||
shimClient.respond(pingresp,2);
|
|
||||||
}
|
|
||||||
shimClient.respond(publish,16);
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_no_pings_inbound_qos1() {
|
|
||||||
IT("does not send pings for connections with inbound qos1 (takes 1 minute)");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
byte puback[] = {0x40,0x2,0x12,0x34};
|
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++) {
|
|
||||||
shimClient.respond(publish,18);
|
|
||||||
shimClient.expect(puback,4);
|
|
||||||
sleep(1);
|
|
||||||
rc = client.loop();
|
|
||||||
IS_TRUE(rc);
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
}
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_keepalive_disconnects_hung() {
|
|
||||||
IT("disconnects a hung connection (takes 30 seconds)");
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte pingreq[] = { 0xC0,0x0 };
|
|
||||||
shimClient.expect(pingreq,2);
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
sleep(1);
|
|
||||||
rc = client.loop();
|
|
||||||
}
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
int state = client.state();
|
|
||||||
IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
SUITE("Keep-alive");
|
|
||||||
test_keepalive_pings_idle();
|
|
||||||
test_keepalive_pings_with_outbound_qos0();
|
|
||||||
test_keepalive_pings_with_inbound_qos0();
|
|
||||||
test_keepalive_no_pings_inbound_qos1();
|
|
||||||
test_keepalive_disconnects_hung();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#ifndef Arduino_h
|
|
||||||
#define Arduino_h
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include "Print.h"
|
|
||||||
|
|
||||||
|
|
||||||
extern "C"{
|
|
||||||
typedef uint8_t byte ;
|
|
||||||
typedef uint8_t boolean ;
|
|
||||||
|
|
||||||
/* sketch */
|
|
||||||
extern void setup( void ) ;
|
|
||||||
extern void loop( void ) ;
|
|
||||||
uint32_t millis( void );
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PROGMEM
|
|
||||||
#define pgm_read_byte_near(x) *(x)
|
|
||||||
|
|
||||||
#define yield(x) {}
|
|
||||||
|
|
||||||
#endif // Arduino_h
|
|
|
@ -1,50 +0,0 @@
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <sstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <list>
|
|
||||||
|
|
||||||
int testCount = 0;
|
|
||||||
int testPasses = 0;
|
|
||||||
const char* testDescription;
|
|
||||||
|
|
||||||
std::list<std::string> failureList;
|
|
||||||
|
|
||||||
void bddtest_suite(const char* name) {
|
|
||||||
LOG(name << "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int bddtest_test(const char* file, int line, const char* assertion, int result) {
|
|
||||||
if (!result) {
|
|
||||||
LOG("✗\n");
|
|
||||||
std::ostringstream os;
|
|
||||||
os << " ! "<<testDescription<<"\n " <<file << ":" <<line<<" : "<<assertion<<" ["<<result<<"]";
|
|
||||||
failureList.push_back(os.str());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bddtest_start(const char* description) {
|
|
||||||
LOG(" - "<<description<<" ");
|
|
||||||
testDescription = description;
|
|
||||||
testCount ++;
|
|
||||||
}
|
|
||||||
void bddtest_end() {
|
|
||||||
LOG("✓\n");
|
|
||||||
testPasses ++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bddtest_summary() {
|
|
||||||
for (std::list<std::string>::iterator it = failureList.begin(); it != failureList.end(); it++) {
|
|
||||||
LOG("\n");
|
|
||||||
LOG(*it);
|
|
||||||
LOG("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(std::dec << testPasses << "/" << testCount << " tests passed\n\n");
|
|
||||||
if (testPasses == testCount) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef bddtest_h
|
|
||||||
#define bddtest_h
|
|
||||||
|
|
||||||
void bddtest_suite(const char* name);
|
|
||||||
int bddtest_test(const char*, int, const char*, int);
|
|
||||||
void bddtest_start(const char*);
|
|
||||||
void bddtest_end();
|
|
||||||
int bddtest_summary();
|
|
||||||
|
|
||||||
#define SUITE(x) { bddtest_suite(x); }
|
|
||||||
#define TEST(x) { if (!bddtest_test(__FILE__, __LINE__, #x, (x))) return false; }
|
|
||||||
|
|
||||||
#define IT(x) { bddtest_start(x); }
|
|
||||||
#define END_IT { bddtest_end();return true;}
|
|
||||||
|
|
||||||
#define FINISH { return bddtest_summary(); }
|
|
||||||
|
|
||||||
#define IS_TRUE(x) TEST(x)
|
|
||||||
#define IS_FALSE(x) TEST(!(x))
|
|
||||||
#define IS_EQUAL(x,y) TEST(x==y)
|
|
||||||
#define IS_NOT_EQUAL(x,y) TEST(x!=y)
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,34 +0,0 @@
|
||||||
#include "Buffer.h"
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
Buffer::Buffer() {
|
|
||||||
this->pos = 0;
|
|
||||||
this->length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffer::Buffer(uint8_t* buf, size_t size) {
|
|
||||||
this->pos = 0;
|
|
||||||
this->length = 0;
|
|
||||||
this->add(buf,size);
|
|
||||||
}
|
|
||||||
bool Buffer::available() {
|
|
||||||
return this->pos < this->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Buffer::next() {
|
|
||||||
if (this->available()) {
|
|
||||||
return this->buffer[this->pos++];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer::reset() {
|
|
||||||
this->pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Buffer::add(uint8_t* buf, size_t size) {
|
|
||||||
uint16_t i = 0;
|
|
||||||
for (;i<size;i++) {
|
|
||||||
this->buffer[this->length++] = buf[i];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef buffer_h
|
|
||||||
#define buffer_h
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
class Buffer {
|
|
||||||
private:
|
|
||||||
uint8_t buffer[1024];
|
|
||||||
uint16_t pos;
|
|
||||||
uint16_t length;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Buffer();
|
|
||||||
Buffer(uint8_t* buf, size_t size);
|
|
||||||
|
|
||||||
virtual bool available();
|
|
||||||
virtual uint8_t next();
|
|
||||||
virtual void reset();
|
|
||||||
|
|
||||||
virtual void add(uint8_t* buf, size_t size);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,21 +0,0 @@
|
||||||
#ifndef client_h
|
|
||||||
#define client_h
|
|
||||||
#include "IPAddress.h"
|
|
||||||
|
|
||||||
class Client {
|
|
||||||
public:
|
|
||||||
virtual int connect(IPAddress ip, uint16_t port) =0;
|
|
||||||
virtual int connect(const char *host, uint16_t port) =0;
|
|
||||||
virtual size_t write(uint8_t) =0;
|
|
||||||
virtual size_t write(const uint8_t *buf, size_t size) =0;
|
|
||||||
virtual int available() = 0;
|
|
||||||
virtual int read() = 0;
|
|
||||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
|
||||||
virtual int peek() = 0;
|
|
||||||
virtual void flush() = 0;
|
|
||||||
virtual void stop() = 0;
|
|
||||||
virtual uint8_t connected() = 0;
|
|
||||||
virtual operator bool() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,44 +0,0 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <IPAddress.h>
|
|
||||||
|
|
||||||
IPAddress::IPAddress()
|
|
||||||
{
|
|
||||||
memset(_address, 0, sizeof(_address));
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
|
|
||||||
{
|
|
||||||
_address[0] = first_octet;
|
|
||||||
_address[1] = second_octet;
|
|
||||||
_address[2] = third_octet;
|
|
||||||
_address[3] = fourth_octet;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress::IPAddress(uint32_t address)
|
|
||||||
{
|
|
||||||
memcpy(_address, &address, sizeof(_address));
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress::IPAddress(const uint8_t *address)
|
|
||||||
{
|
|
||||||
memcpy(_address, address, sizeof(_address));
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress& IPAddress::operator=(const uint8_t *address)
|
|
||||||
{
|
|
||||||
memcpy(_address, address, sizeof(_address));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress& IPAddress::operator=(uint32_t address)
|
|
||||||
{
|
|
||||||
memcpy(_address, (const uint8_t *)&address, sizeof(_address));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IPAddress::operator==(const uint8_t* addr)
|
|
||||||
{
|
|
||||||
return memcmp(addr, _address, sizeof(_address)) == 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* MIT License:
|
|
||||||
* Copyright (c) 2011 Adrian McEwen
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* adrianm@mcqn.com 1/1/2011
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef IPAddress_h
|
|
||||||
#define IPAddress_h
|
|
||||||
|
|
||||||
|
|
||||||
// A class to make it easier to handle and pass around IP addresses
|
|
||||||
|
|
||||||
class IPAddress {
|
|
||||||
private:
|
|
||||||
uint8_t _address[4]; // IPv4 address
|
|
||||||
// Access the raw byte array containing the address. Because this returns a pointer
|
|
||||||
// to the internal structure rather than a copy of the address this function should only
|
|
||||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
|
||||||
// stored.
|
|
||||||
uint8_t* raw_address() { return _address; };
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructors
|
|
||||||
IPAddress();
|
|
||||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
|
||||||
IPAddress(uint32_t address);
|
|
||||||
IPAddress(const uint8_t *address);
|
|
||||||
|
|
||||||
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
|
||||||
// to a four-byte uint8_t array is expected
|
|
||||||
operator uint32_t() { return *((uint32_t*)_address); };
|
|
||||||
bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
|
|
||||||
bool operator==(const uint8_t* addr);
|
|
||||||
|
|
||||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
|
||||||
uint8_t operator[](int index) const { return _address[index]; };
|
|
||||||
uint8_t& operator[](int index) { return _address[index]; };
|
|
||||||
|
|
||||||
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
|
||||||
IPAddress& operator=(const uint8_t *address);
|
|
||||||
IPAddress& operator=(uint32_t address);
|
|
||||||
|
|
||||||
|
|
||||||
friend class EthernetClass;
|
|
||||||
friend class UDP;
|
|
||||||
friend class Client;
|
|
||||||
friend class Server;
|
|
||||||
friend class DhcpClass;
|
|
||||||
friend class DNSClient;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
Print.h - Base class that provides print() and println()
|
|
||||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef Print_h
|
|
||||||
#define Print_h
|
|
||||||
|
|
||||||
class Print {
|
|
||||||
public:
|
|
||||||
virtual size_t write(uint8_t) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,153 +0,0 @@
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
uint32_t millis(void) {
|
|
||||||
return time(0)*1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShimClient::ShimClient() {
|
|
||||||
this->responseBuffer = new Buffer();
|
|
||||||
this->expectBuffer = new Buffer();
|
|
||||||
this->_allowConnect = true;
|
|
||||||
this->_connected = false;
|
|
||||||
this->_error = false;
|
|
||||||
this->expectAnything = true;
|
|
||||||
this->_received = 0;
|
|
||||||
this->_expectedPort = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ShimClient::connect(IPAddress ip, uint16_t port) {
|
|
||||||
if (this->_allowConnect) {
|
|
||||||
this->_connected = true;
|
|
||||||
}
|
|
||||||
if (this->_expectedPort !=0) {
|
|
||||||
// if (memcmp(ip,this->_expectedIP,4) != 0) {
|
|
||||||
// TRACE( "ip mismatch\n");
|
|
||||||
// this->_error = true;
|
|
||||||
// }
|
|
||||||
if (port != this->_expectedPort) {
|
|
||||||
TRACE( "port mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this->_connected;
|
|
||||||
}
|
|
||||||
int ShimClient::connect(const char *host, uint16_t port) {
|
|
||||||
if (this->_allowConnect) {
|
|
||||||
this->_connected = true;
|
|
||||||
}
|
|
||||||
if (this->_expectedPort !=0) {
|
|
||||||
if (strcmp(host,this->_expectedHost) != 0) {
|
|
||||||
TRACE( "host mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
if (port != this->_expectedPort) {
|
|
||||||
TRACE( "port mismatch\n");
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return this->_connected;
|
|
||||||
}
|
|
||||||
size_t ShimClient::write(uint8_t b) {
|
|
||||||
this->_received += 1;
|
|
||||||
TRACE(std::hex << (unsigned int)b);
|
|
||||||
if (!this->expectAnything) {
|
|
||||||
if (this->expectBuffer->available()) {
|
|
||||||
uint8_t expected = this->expectBuffer->next();
|
|
||||||
if (expected != b) {
|
|
||||||
this->_error = true;
|
|
||||||
TRACE("!=" << (unsigned int)expected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TRACE("\n"<< std::dec);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
size_t ShimClient::write(const uint8_t *buf, size_t size) {
|
|
||||||
this->_received += size;
|
|
||||||
TRACE( "[" << std::dec << (unsigned int)(size) << "] ");
|
|
||||||
uint16_t i=0;
|
|
||||||
for (;i<size;i++) {
|
|
||||||
if (i>0) {
|
|
||||||
TRACE(":");
|
|
||||||
}
|
|
||||||
TRACE(std::hex << (unsigned int)(buf[i]));
|
|
||||||
|
|
||||||
if (!this->expectAnything) {
|
|
||||||
if (this->expectBuffer->available()) {
|
|
||||||
uint8_t expected = this->expectBuffer->next();
|
|
||||||
if (expected != buf[i]) {
|
|
||||||
this->_error = true;
|
|
||||||
TRACE("!=" << (unsigned int)expected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TRACE("\n"<<std::dec);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
int ShimClient::available() {
|
|
||||||
return this->responseBuffer->available();
|
|
||||||
}
|
|
||||||
int ShimClient::read() { return this->responseBuffer->next(); }
|
|
||||||
int ShimClient::read(uint8_t *buf, size_t size) {
|
|
||||||
uint16_t i = 0;
|
|
||||||
for (;i<size;i++) {
|
|
||||||
buf[i] = this->read();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
int ShimClient::peek() { return 0; }
|
|
||||||
void ShimClient::flush() {}
|
|
||||||
void ShimClient::stop() {
|
|
||||||
this->setConnected(false);
|
|
||||||
}
|
|
||||||
uint8_t ShimClient::connected() { return this->_connected; }
|
|
||||||
ShimClient::operator bool() { return true; }
|
|
||||||
|
|
||||||
|
|
||||||
ShimClient* ShimClient::respond(uint8_t *buf, size_t size) {
|
|
||||||
this->responseBuffer->add(buf,size);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShimClient* ShimClient::expect(uint8_t *buf, size_t size) {
|
|
||||||
this->expectAnything = false;
|
|
||||||
this->expectBuffer->add(buf,size);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShimClient::setConnected(bool b) {
|
|
||||||
this->_connected = b;
|
|
||||||
}
|
|
||||||
void ShimClient::setAllowConnect(bool b) {
|
|
||||||
this->_allowConnect = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ShimClient::error() {
|
|
||||||
return this->_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t ShimClient::received() {
|
|
||||||
return this->_received;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShimClient::expectConnect(IPAddress ip, uint16_t port) {
|
|
||||||
this->_expectedIP = ip;
|
|
||||||
this->_expectedPort = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShimClient::expectConnect(const char *host, uint16_t port) {
|
|
||||||
this->_expectedHost = host;
|
|
||||||
this->_expectedPort = port;
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
#ifndef shimclient_h
|
|
||||||
#define shimclient_h
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "Client.h"
|
|
||||||
#include "IPAddress.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
|
|
||||||
|
|
||||||
class ShimClient : public Client {
|
|
||||||
private:
|
|
||||||
Buffer* responseBuffer;
|
|
||||||
Buffer* expectBuffer;
|
|
||||||
bool _allowConnect;
|
|
||||||
bool _connected;
|
|
||||||
bool expectAnything;
|
|
||||||
bool _error;
|
|
||||||
uint16_t _received;
|
|
||||||
IPAddress _expectedIP;
|
|
||||||
uint16_t _expectedPort;
|
|
||||||
const char* _expectedHost;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ShimClient();
|
|
||||||
virtual int connect(IPAddress ip, uint16_t port);
|
|
||||||
virtual int connect(const char *host, uint16_t port);
|
|
||||||
virtual size_t write(uint8_t);
|
|
||||||
virtual size_t write(const uint8_t *buf, size_t size);
|
|
||||||
virtual int available();
|
|
||||||
virtual int read();
|
|
||||||
virtual int read(uint8_t *buf, size_t size);
|
|
||||||
virtual int peek();
|
|
||||||
virtual void flush();
|
|
||||||
virtual void stop();
|
|
||||||
virtual uint8_t connected();
|
|
||||||
virtual operator bool();
|
|
||||||
|
|
||||||
virtual ShimClient* respond(uint8_t *buf, size_t size);
|
|
||||||
virtual ShimClient* expect(uint8_t *buf, size_t size);
|
|
||||||
|
|
||||||
virtual void expectConnect(IPAddress ip, uint16_t port);
|
|
||||||
virtual void expectConnect(const char *host, uint16_t port);
|
|
||||||
|
|
||||||
virtual uint16_t received();
|
|
||||||
virtual bool error();
|
|
||||||
|
|
||||||
virtual void setAllowConnect(bool b);
|
|
||||||
virtual void setConnected(bool b);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,39 +0,0 @@
|
||||||
#include "Stream.h"
|
|
||||||
#include "trace.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
Stream::Stream() {
|
|
||||||
this->expectBuffer = new Buffer();
|
|
||||||
this->_error = false;
|
|
||||||
this->_written = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Stream::write(uint8_t b) {
|
|
||||||
this->_written++;
|
|
||||||
TRACE(std::hex << (unsigned int)b);
|
|
||||||
if (this->expectBuffer->available()) {
|
|
||||||
uint8_t expected = this->expectBuffer->next();
|
|
||||||
if (expected != b) {
|
|
||||||
this->_error = true;
|
|
||||||
TRACE("!=" << (unsigned int)expected);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this->_error = true;
|
|
||||||
}
|
|
||||||
TRACE("\n"<< std::dec);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Stream::error() {
|
|
||||||
return this->_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::expect(uint8_t *buf, size_t size) {
|
|
||||||
this->expectBuffer->add(buf,size);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t Stream::length() {
|
|
||||||
return this->_written;
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
#ifndef Stream_h
|
|
||||||
#define Stream_h
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
|
|
||||||
class Stream {
|
|
||||||
private:
|
|
||||||
Buffer* expectBuffer;
|
|
||||||
bool _error;
|
|
||||||
uint16_t _written;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Stream();
|
|
||||||
virtual size_t write(uint8_t);
|
|
||||||
|
|
||||||
virtual bool error();
|
|
||||||
virtual void expect(uint8_t *buf, size_t size);
|
|
||||||
virtual uint16_t length();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,10 +0,0 @@
|
||||||
#ifndef trace_h
|
|
||||||
#define trace_h
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#define LOG(x) {std::cout << x << std::flush; }
|
|
||||||
#define TRACE(x) {if (getenv("TRACE")) { std::cout << x << std::flush; }}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,190 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish() {
|
|
||||||
IT("publishes a null-terminated string");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.expect(publish,16);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",(char*)"payload");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_publish_bytes() {
|
|
||||||
IT("publishes a byte array");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
|
||||||
int length = 5;
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",payload,length);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_publish_retained() {
|
|
||||||
IT("publishes retained - 1");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
|
||||||
int length = 5;
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",payload,length,true);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish_retained_2() {
|
|
||||||
IT("publishes retained - 2");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,'A','B','C','D','E'};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish((char*)"topic",(char*)"ABCDE",true);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish_not_connected() {
|
|
||||||
IT("publish fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int rc = client.publish((char*)"topic",(char*)"payload");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish_too_long() {
|
|
||||||
IT("publish fails when topic/payload are too long");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2
|
|
||||||
rc = client.publish((char*)"topic",(char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_publish_P() {
|
|
||||||
IT("publishes using PROGMEM");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
|
||||||
int length = 5;
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
|
||||||
shimClient.expect(publish,14);
|
|
||||||
|
|
||||||
rc = client.publish_P((char*)"topic",payload,length,true);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
SUITE("Publish");
|
|
||||||
test_publish();
|
|
||||||
test_publish_bytes();
|
|
||||||
test_publish_retained();
|
|
||||||
test_publish_retained_2();
|
|
||||||
test_publish_not_connected();
|
|
||||||
test_publish_too_long();
|
|
||||||
test_publish_P();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
|
@ -1,279 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
bool callback_called = false;
|
|
||||||
char lastTopic[1024];
|
|
||||||
char lastPayload[1024];
|
|
||||||
unsigned int lastLength;
|
|
||||||
|
|
||||||
void reset_callback() {
|
|
||||||
callback_called = false;
|
|
||||||
lastTopic[0] = '\0';
|
|
||||||
lastPayload[0] = '\0';
|
|
||||||
lastLength = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
callback_called = true;
|
|
||||||
strcpy(lastTopic,topic);
|
|
||||||
memcpy(lastPayload,payload,length);
|
|
||||||
lastLength = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_callback() {
|
|
||||||
IT("receives a callback message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,16);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(memcmp(lastPayload,"payload",7)==0);
|
|
||||||
IS_TRUE(lastLength == 7);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_stream() {
|
|
||||||
IT("receives a streamed callback message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
Stream stream;
|
|
||||||
stream.expect((uint8_t*)"payload",7);
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient, stream);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,16);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(lastLength == 7);
|
|
||||||
|
|
||||||
IS_FALSE(stream.error());
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_max_sized_message() {
|
|
||||||
IT("receives an max-sized message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
int length = MQTT_MAX_PACKET_SIZE;
|
|
||||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
byte bigPublish[length];
|
|
||||||
memset(bigPublish,'A',length);
|
|
||||||
bigPublish[length] = 'B';
|
|
||||||
memcpy(bigPublish,publish,16);
|
|
||||||
shimClient.respond(bigPublish,length);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(lastLength == length-9);
|
|
||||||
IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_oversized_message() {
|
|
||||||
IT("drops an oversized message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
int length = MQTT_MAX_PACKET_SIZE+1;
|
|
||||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
byte bigPublish[length];
|
|
||||||
memset(bigPublish,'A',length);
|
|
||||||
bigPublish[length] = 'B';
|
|
||||||
memcpy(bigPublish,publish,16);
|
|
||||||
shimClient.respond(bigPublish,length);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(callback_called);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_drop_invalid_remaining_length_message() {
|
|
||||||
IT("drops invalid remaining length message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,20);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(callback_called);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_receive_oversized_stream_message() {
|
|
||||||
IT("drops an oversized message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
Stream stream;
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient, stream);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
int length = MQTT_MAX_PACKET_SIZE+1;
|
|
||||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
|
|
||||||
byte bigPublish[length];
|
|
||||||
memset(bigPublish,'A',length);
|
|
||||||
bigPublish[length] = 'B';
|
|
||||||
memcpy(bigPublish,publish,16);
|
|
||||||
|
|
||||||
shimClient.respond(bigPublish,length);
|
|
||||||
stream.expect(bigPublish+9,length-9);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(lastLength == length-9);
|
|
||||||
|
|
||||||
IS_FALSE(stream.error());
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_receive_qos1() {
|
|
||||||
IT("receives a qos1 message");
|
|
||||||
reset_callback();
|
|
||||||
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
|
||||||
shimClient.respond(publish,18);
|
|
||||||
|
|
||||||
byte puback[] = {0x40,0x2,0x12,0x34};
|
|
||||||
shimClient.expect(puback,4);
|
|
||||||
|
|
||||||
rc = client.loop();
|
|
||||||
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_TRUE(callback_called);
|
|
||||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
|
||||||
IS_TRUE(memcmp(lastPayload,"payload",7)==0);
|
|
||||||
IS_TRUE(lastLength == 7);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
SUITE("Receive");
|
|
||||||
test_receive_callback();
|
|
||||||
test_receive_stream();
|
|
||||||
test_receive_max_sized_message();
|
|
||||||
test_drop_invalid_remaining_length_message();
|
|
||||||
test_receive_oversized_message();
|
|
||||||
test_receive_oversized_stream_message();
|
|
||||||
test_receive_qos1();
|
|
||||||
|
|
||||||
FINISH
|
|
||||||
}
|
|
|
@ -1,177 +0,0 @@
|
||||||
#include "PubSubClient.h"
|
|
||||||
#include "ShimClient.h"
|
|
||||||
#include "Buffer.h"
|
|
||||||
#include "BDDTest.h"
|
|
||||||
#include "trace.h"
|
|
||||||
|
|
||||||
|
|
||||||
byte server[] = { 172, 16, 0, 2 };
|
|
||||||
|
|
||||||
void callback(char* topic, byte* payload, unsigned int length) {
|
|
||||||
// handle message arrived
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_no_qos() {
|
|
||||||
IT("subscribe without qos defaults to 0");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x0 };
|
|
||||||
shimClient.expect(subscribe,12);
|
|
||||||
byte suback[] = { 0x90,0x3,0x0,0x2,0x0 };
|
|
||||||
shimClient.respond(suback,5);
|
|
||||||
|
|
||||||
rc = client.subscribe((char*)"topic");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_qos_1() {
|
|
||||||
IT("subscribes qos 1");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1 };
|
|
||||||
shimClient.expect(subscribe,12);
|
|
||||||
byte suback[] = { 0x90,0x3,0x0,0x2,0x1 };
|
|
||||||
shimClient.respond(suback,5);
|
|
||||||
|
|
||||||
rc = client.subscribe((char*)"topic",1);
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_not_connected() {
|
|
||||||
IT("subscribe fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int rc = client.subscribe((char*)"topic");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_invalid_qos() {
|
|
||||||
IT("subscribe fails with invalid qos values");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
rc = client.subscribe((char*)"topic",2);
|
|
||||||
IS_FALSE(rc);
|
|
||||||
rc = client.subscribe((char*)"topic",254);
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_subscribe_too_long() {
|
|
||||||
IT("subscribe fails with too long topic");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
// max length should be allowed
|
|
||||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2
|
|
||||||
rc = client.subscribe((char*)"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2
|
|
||||||
rc = client.subscribe((char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int test_unsubscribe() {
|
|
||||||
IT("unsubscribes");
|
|
||||||
ShimClient shimClient;
|
|
||||||
shimClient.setAllowConnect(true);
|
|
||||||
|
|
||||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
|
||||||
shimClient.respond(connack,4);
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
int rc = client.connect((char*)"client_test1");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
byte unsubscribe[] = { 0xA2,0x9,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63 };
|
|
||||||
shimClient.expect(unsubscribe,12);
|
|
||||||
byte unsuback[] = { 0xB0,0x2,0x0,0x2 };
|
|
||||||
shimClient.respond(unsuback,4);
|
|
||||||
|
|
||||||
rc = client.unsubscribe((char*)"topic");
|
|
||||||
IS_TRUE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int test_unsubscribe_not_connected() {
|
|
||||||
IT("unsubscribe fails when not connected");
|
|
||||||
ShimClient shimClient;
|
|
||||||
|
|
||||||
PubSubClient client(server, 1883, callback, shimClient);
|
|
||||||
|
|
||||||
int rc = client.unsubscribe((char*)"topic");
|
|
||||||
IS_FALSE(rc);
|
|
||||||
|
|
||||||
IS_FALSE(shimClient.error());
|
|
||||||
|
|
||||||
END_IT
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
SUITE("Subscribe");
|
|
||||||
test_subscribe_no_qos();
|
|
||||||
test_subscribe_qos_1();
|
|
||||||
test_subscribe_not_connected();
|
|
||||||
test_subscribe_invalid_qos();
|
|
||||||
test_subscribe_too_long();
|
|
||||||
test_unsubscribe();
|
|
||||||
test_unsubscribe_not_connected();
|
|
||||||
FINISH
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
import unittest
|
|
||||||
import settings
|
|
||||||
import time
|
|
||||||
import mosquitto
|
|
||||||
|
|
||||||
|
|
||||||
def on_message(mosq, obj, msg):
|
|
||||||
obj.message_queue.append(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class mqtt_basic(unittest.TestCase):
|
|
||||||
|
|
||||||
message_queue = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(self):
|
|
||||||
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
|
|
||||||
self.client.connect(settings.server_ip)
|
|
||||||
self.client.on_message = on_message
|
|
||||||
self.client.subscribe("outTopic", 0)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(self):
|
|
||||||
self.client.disconnect()
|
|
||||||
|
|
||||||
def test_one(self):
|
|
||||||
i = 30
|
|
||||||
while len(self.message_queue) == 0 and i > 0:
|
|
||||||
self.client.loop()
|
|
||||||
time.sleep(0.5)
|
|
||||||
i -= 1
|
|
||||||
self.assertTrue(i > 0, "message receive timed-out")
|
|
||||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
|
||||||
msg = self.message_queue[0]
|
|
||||||
self.assertEqual(msg.mid, 0, "message id not 0")
|
|
||||||
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
|
||||||
self.assertEqual(msg.payload, "hello world")
|
|
||||||
self.assertEqual(msg.qos, 0, "message qos not 0")
|
|
||||||
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
|
|
@ -1,59 +0,0 @@
|
||||||
import unittest
|
|
||||||
import settings
|
|
||||||
import time
|
|
||||||
import mosquitto
|
|
||||||
|
|
||||||
|
|
||||||
def on_message(mosq, obj, msg):
|
|
||||||
obj.message_queue.append(msg)
|
|
||||||
|
|
||||||
|
|
||||||
class mqtt_publish_in_callback(unittest.TestCase):
|
|
||||||
|
|
||||||
message_queue = []
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(self):
|
|
||||||
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
|
|
||||||
self.client.connect(settings.server_ip)
|
|
||||||
self.client.on_message = on_message
|
|
||||||
self.client.subscribe("outTopic", 0)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(self):
|
|
||||||
self.client.disconnect()
|
|
||||||
|
|
||||||
def test_connect(self):
|
|
||||||
i = 30
|
|
||||||
while len(self.message_queue) == 0 and i > 0:
|
|
||||||
self.client.loop()
|
|
||||||
time.sleep(0.5)
|
|
||||||
i -= 1
|
|
||||||
self.assertTrue(i > 0, "message receive timed-out")
|
|
||||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
|
||||||
msg = self.message_queue.pop(0)
|
|
||||||
self.assertEqual(msg.mid, 0, "message id not 0")
|
|
||||||
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
|
||||||
self.assertEqual(msg.payload, "hello world")
|
|
||||||
self.assertEqual(msg.qos, 0, "message qos not 0")
|
|
||||||
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
|
||||||
|
|
||||||
def test_publish(self):
|
|
||||||
self.assertEqual(len(self.message_queue), 0, "message queue not empty")
|
|
||||||
payload = "abcdefghij"
|
|
||||||
self.client.publish("inTopic", payload)
|
|
||||||
|
|
||||||
i = 30
|
|
||||||
while len(self.message_queue) == 0 and i > 0:
|
|
||||||
self.client.loop()
|
|
||||||
time.sleep(0.5)
|
|
||||||
i -= 1
|
|
||||||
|
|
||||||
self.assertTrue(i > 0, "message receive timed-out")
|
|
||||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
|
||||||
msg = self.message_queue.pop(0)
|
|
||||||
self.assertEqual(msg.mid, 0, "message id not 0")
|
|
||||||
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
|
||||||
self.assertEqual(msg.payload, payload)
|
|
||||||
self.assertEqual(msg.qos, 0, "message qos not 0")
|
|
||||||
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
|
|
@ -1,2 +0,0 @@
|
||||||
server_ip = "172.16.0.2"
|
|
||||||
arduino_ip = "172.16.0.100"
|
|
|
@ -1,181 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os
|
|
||||||
import os.path
|
|
||||||
import sys
|
|
||||||
import shutil
|
|
||||||
from subprocess import call
|
|
||||||
import importlib
|
|
||||||
import unittest
|
|
||||||
import re
|
|
||||||
|
|
||||||
from testcases import settings
|
|
||||||
|
|
||||||
|
|
||||||
class Workspace(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.root_dir = os.getcwd()
|
|
||||||
self.build_dir = os.path.join(self.root_dir, "tmpbin")
|
|
||||||
self.log_dir = os.path.join(self.root_dir, "logs")
|
|
||||||
self.tests_dir = os.path.join(self.root_dir, "testcases")
|
|
||||||
self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples")
|
|
||||||
self.examples = []
|
|
||||||
self.tests = []
|
|
||||||
if not os.path.isdir("../PubSubClient"):
|
|
||||||
raise Exception("Cannot find PubSubClient library")
|
|
||||||
try:
|
|
||||||
return __import__('ino')
|
|
||||||
except ImportError:
|
|
||||||
raise Exception("ino tool not installed")
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
if os.path.isdir(self.build_dir):
|
|
||||||
shutil.rmtree(self.build_dir)
|
|
||||||
os.mkdir(self.build_dir)
|
|
||||||
if os.path.isdir(self.log_dir):
|
|
||||||
shutil.rmtree(self.log_dir)
|
|
||||||
os.mkdir(self.log_dir)
|
|
||||||
|
|
||||||
os.chdir(self.build_dir)
|
|
||||||
call(["ino", "init"])
|
|
||||||
|
|
||||||
shutil.copytree("../../PubSubClient", "lib/PubSubClient")
|
|
||||||
|
|
||||||
filenames = []
|
|
||||||
for root, dirs, files in os.walk(self.examples_dir):
|
|
||||||
filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
|
|
||||||
filenames.sort()
|
|
||||||
for e in filenames:
|
|
||||||
self.examples.append(Sketch(self, e))
|
|
||||||
|
|
||||||
filenames = []
|
|
||||||
for root, dirs, files in os.walk(self.tests_dir):
|
|
||||||
filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
|
|
||||||
filenames.sort()
|
|
||||||
for e in filenames:
|
|
||||||
self.tests.append(Sketch(self, e))
|
|
||||||
|
|
||||||
def clean(self):
|
|
||||||
shutil.rmtree(self.build_dir)
|
|
||||||
|
|
||||||
|
|
||||||
class Sketch(object):
|
|
||||||
def __init__(self, wksp, fn):
|
|
||||||
self.w = wksp
|
|
||||||
self.filename = fn
|
|
||||||
self.basename = os.path.basename(self.filename)
|
|
||||||
self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),))
|
|
||||||
self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),))
|
|
||||||
self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),))
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
sys.stdout.write(" Build: ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
# Copy sketch over, replacing IP addresses as necessary
|
|
||||||
fin = open(self.filename, "r")
|
|
||||||
lines = fin.readlines()
|
|
||||||
fin.close()
|
|
||||||
fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w")
|
|
||||||
for l in lines:
|
|
||||||
if re.match(r"^byte server\[\] = {", l):
|
|
||||||
fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),))
|
|
||||||
elif re.match(r"^byte ip\[\] = {", l):
|
|
||||||
fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),))
|
|
||||||
else:
|
|
||||||
fout.write(l)
|
|
||||||
fout.flush()
|
|
||||||
fout.close()
|
|
||||||
|
|
||||||
# Run build
|
|
||||||
fout = open(self.build_log, "w")
|
|
||||||
ferr = open(self.build_err_log, "w")
|
|
||||||
rc = call(["ino", "build"], stdout=fout, stderr=ferr)
|
|
||||||
fout.close()
|
|
||||||
ferr.close()
|
|
||||||
if rc == 0:
|
|
||||||
sys.stdout.write("pass")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
sys.stdout.write("fail")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
with open(self.build_err_log) as f:
|
|
||||||
for line in f:
|
|
||||||
print(" " + line)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def upload(self):
|
|
||||||
sys.stdout.write(" Upload: ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
fout = open(self.build_upload_log, "w")
|
|
||||||
rc = call(["ino", "upload"], stdout=fout, stderr=fout)
|
|
||||||
fout.close()
|
|
||||||
if rc == 0:
|
|
||||||
sys.stdout.write("pass")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
sys.stdout.write("fail")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
with open(self.build_upload_log) as f:
|
|
||||||
for line in f:
|
|
||||||
print(" " + line)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test(self):
|
|
||||||
# import the matching test case, if it exists
|
|
||||||
try:
|
|
||||||
basename = os.path.basename(self.filename)[:-4]
|
|
||||||
i = importlib.import_module("testcases." + basename)
|
|
||||||
except:
|
|
||||||
sys.stdout.write(" Test: no tests found")
|
|
||||||
sys.stdout.write("\n")
|
|
||||||
return
|
|
||||||
c = getattr(i, basename)
|
|
||||||
|
|
||||||
testmethods = [m for m in dir(c) if m.startswith("test_")]
|
|
||||||
testmethods.sort()
|
|
||||||
tests = []
|
|
||||||
for m in testmethods:
|
|
||||||
tests.append(c(m))
|
|
||||||
|
|
||||||
result = unittest.TestResult()
|
|
||||||
c.setUpClass()
|
|
||||||
if self.upload():
|
|
||||||
sys.stdout.write(" Test: ")
|
|
||||||
sys.stdout.flush()
|
|
||||||
for t in tests:
|
|
||||||
t.run(result)
|
|
||||||
print(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun))
|
|
||||||
if not result.wasSuccessful():
|
|
||||||
if len(result.failures) > 0:
|
|
||||||
for f in result.failures:
|
|
||||||
print("-- " + str(f[0]))
|
|
||||||
print(f[1])
|
|
||||||
if len(result.errors) > 0:
|
|
||||||
print(" Errors:")
|
|
||||||
for f in result.errors:
|
|
||||||
print("-- " + str(f[0]))
|
|
||||||
print(f[1])
|
|
||||||
c.tearDownClass()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
run_tests = True
|
|
||||||
|
|
||||||
w = Workspace()
|
|
||||||
w.init()
|
|
||||||
|
|
||||||
for e in w.examples:
|
|
||||||
print("--------------------------------------")
|
|
||||||
print("[" + e.basename + "]")
|
|
||||||
if e.build() and run_tests:
|
|
||||||
e.test()
|
|
||||||
for e in w.tests:
|
|
||||||
print("--------------------------------------")
|
|
||||||
print("[" + e.basename + "]")
|
|
||||||
if e.build() and run_tests:
|
|
||||||
e.test()
|
|
||||||
|
|
||||||
w.clean()
|
|
|
@ -1,40 +1,55 @@
|
||||||
|
#include "wifi.h"
|
||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <ArduinoOTA.h>
|
|
||||||
#include <SoftwareSerial.h>
|
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
#include "dep/pubsubclient-2.7/src/PubSubClient.cpp"
|
#include <WiFiManager.h>
|
||||||
#include "dep/WiFiManager-0.15.0/WiFiManager.cpp"
|
#include <PubSubClient.h>
|
||||||
#include "dep/SimpleTimer-schinken/SimpleTimer.cpp"
|
#include <SoftwareSerial.h>
|
||||||
|
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#ifdef USE_HA_AUTODISCOVERY
|
uint8_t mqttRetryCounter = 0;
|
||||||
#define FIRMWARE_PREFIX "esp8266-geigercounter"
|
|
||||||
char MQTT_TOPIC_LAST_WILL[128];
|
|
||||||
char MQTT_TOPIC_CPM_MEASUREMENT[128];
|
|
||||||
char MQTT_TOPIC_USV_MEASUREMENT[128];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
WiFiManager wifiManager;
|
||||||
WiFiClient wifiClient;
|
WiFiClient wifiClient;
|
||||||
PubSubClient mqttClient;
|
PubSubClient mqttClient;
|
||||||
|
|
||||||
|
WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, sizeof(mqtt_server));
|
||||||
|
WiFiManagerParameter custom_mqtt_user("user", "MQTT username", username, sizeof(username));
|
||||||
|
WiFiManagerParameter custom_mqtt_pass("pass", "MQTT password", password, sizeof(password));
|
||||||
|
|
||||||
|
unsigned long lastMqttConnectionAttempt = millis();
|
||||||
|
const long mqttConnectionInterval = 60000;
|
||||||
|
|
||||||
|
unsigned long statusPublishPreviousMillis = millis();
|
||||||
|
const long statusPublishInterval = 30000;
|
||||||
|
|
||||||
|
char identifier[24];
|
||||||
|
#define FIRMWARE_PREFIX "esp8266-geigercounter"
|
||||||
|
#define AVAILABILITY_ONLINE "online"
|
||||||
|
#define AVAILABILITY_OFFLINE "offline"
|
||||||
|
char MQTT_TOPIC_AVAILABILITY[128];
|
||||||
|
char MQTT_TOPIC_CPM[128];
|
||||||
|
char MQTT_TOPIC_USV[128];
|
||||||
|
|
||||||
|
char MQTT_TOPIC_AUTOCONF_CPM[128];
|
||||||
|
char MQTT_TOPIC_AUTOCONF_USV[128];
|
||||||
|
|
||||||
SoftwareSerial geigerCounterSerial(PIN_UART_RX, PIN_UART_TX);
|
SoftwareSerial geigerCounterSerial(PIN_UART_RX, PIN_UART_TX);
|
||||||
SimpleTimer timer;
|
|
||||||
|
|
||||||
uint8_t mqttRetryCounter = 0;
|
|
||||||
String serialInput = "";
|
|
||||||
char serialInputHelper[RECV_LINE_SIZE];
|
|
||||||
|
|
||||||
int lastCPM = 0, currentCPM = 0;
|
bool shouldSaveConfig = false;
|
||||||
float lastuSv = 0, currentuSv = 0;
|
|
||||||
|
|
||||||
char hostname[24];
|
void saveConfigCallback () {
|
||||||
|
shouldSaveConfig = true;
|
||||||
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
delay(3000);
|
delay(3000);
|
||||||
Serial.begin(115200);
|
Serial.begin(9600);
|
||||||
delay(2000);
|
delay(2000);
|
||||||
Serial.println("\n");
|
Serial.println("\n");
|
||||||
Serial.println("Hello from esp8266-geigercounter");
|
Serial.println("Hello from esp8266-geigercounter");
|
||||||
|
@ -43,290 +58,150 @@ void setup() {
|
||||||
Serial.printf("Boot Mode: %u\n", ESP.getBootMode());
|
Serial.printf("Boot Mode: %u\n", ESP.getBootMode());
|
||||||
Serial.printf("CPU Frequency: %u MHz\n", ESP.getCpuFreqMHz());
|
Serial.printf("CPU Frequency: %u MHz\n", ESP.getCpuFreqMHz());
|
||||||
Serial.printf("Reset reason: %s\n", ESP.getResetReason().c_str());
|
Serial.printf("Reset reason: %s\n", ESP.getResetReason().c_str());
|
||||||
delay(3000);
|
|
||||||
|
|
||||||
geigerCounterSerial.begin(BAUD_GEIGERCOUNTER);
|
geigerCounterSerial.begin(9600);
|
||||||
|
|
||||||
|
snprintf(identifier, sizeof(identifier), "GEIGERCTR-%X", ESP.getChipId());
|
||||||
int32_t chipid = ESP.getChipId();
|
snprintf(MQTT_TOPIC_AVAILABILITY, 127, "%s/%s/status", FIRMWARE_PREFIX, identifier);
|
||||||
|
snprintf(MQTT_TOPIC_CPM, 127, "%s/%s_cpm/state", FIRMWARE_PREFIX, identifier);
|
||||||
|
snprintf(MQTT_TOPIC_USV, 127, "%s/%s_usv/state", FIRMWARE_PREFIX, identifier);
|
||||||
|
|
||||||
Serial.print("MQTT_MAX_PACKET_SIZE: ");
|
snprintf(MQTT_TOPIC_AUTOCONF_CPM, 127, "homeassistant/sensor/%s/%s_cpm/config", FIRMWARE_PREFIX, identifier);
|
||||||
Serial.println(MQTT_MAX_PACKET_SIZE);
|
snprintf(MQTT_TOPIC_AUTOCONF_USV, 127, "homeassistant/sensor/%s/%s_usv/config", FIRMWARE_PREFIX, identifier);
|
||||||
|
|
||||||
|
|
||||||
#ifdef HOSTNAME
|
|
||||||
strncpy(hostname, HOSTNAME, sizeof(hostname));
|
|
||||||
#else
|
|
||||||
snprintf(hostname, sizeof(hostname), "GEIGERCTR-%X", chipid);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_HA_AUTODISCOVERY
|
WiFi.hostname(identifier);
|
||||||
snprintf(MQTT_TOPIC_LAST_WILL, 127, "%s/%s/presence", FIRMWARE_PREFIX, hostname);
|
|
||||||
snprintf(MQTT_TOPIC_CPM_MEASUREMENT, 127, "%s/%s/%s_%s/state", FIRMWARE_PREFIX, hostname, hostname, "cpm");
|
|
||||||
snprintf(MQTT_TOPIC_USV_MEASUREMENT, 127, "%s/%s/%s_%s/state", FIRMWARE_PREFIX, hostname, hostname, "uSv");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Serial.print("Connecting to WiFi");
|
loadConfig();
|
||||||
|
setupWifi();
|
||||||
WiFi.mode(WIFI_STA);
|
mqttClient.setServer(mqtt_server, 1883);
|
||||||
unsigned long wifiConnectStart = millis();
|
mqttClient.setKeepAlive(10);
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
mqttClient.setBufferSize(2048);
|
||||||
if (WiFi.status() == WL_CONNECT_FAILED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (millis() - wifiConnectStart > 10000) {
|
|
||||||
WiFiManager wifiManager;
|
|
||||||
#ifdef CONF_WIFI_PASSWORD
|
|
||||||
wifiManager.autoConnect(hostname, CONF_WIFI_PASSWORD);
|
|
||||||
#else
|
|
||||||
wifiManager.autoConnect(hostname);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
delay(100);
|
|
||||||
Serial.print(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.print("\nIP: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
|
|
||||||
WiFi.hostname(hostname);
|
|
||||||
mqttClient.setClient(wifiClient);
|
|
||||||
mqttClient.setServer(MQTT_HOST, 1883);
|
|
||||||
|
|
||||||
ArduinoOTA.setHostname(hostname);
|
|
||||||
ArduinoOTA.setPassword(OTA_PASSWORD);
|
|
||||||
ArduinoOTA.begin();
|
|
||||||
|
|
||||||
Serial.print("Hostname: ");
|
Serial.print("Hostname: ");
|
||||||
Serial.println(hostname);
|
Serial.println(identifier);
|
||||||
|
Serial.print("\nIP: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
Serial.println("-- Current GPIO Configuration --");
|
Serial.println("-- Current GPIO Configuration --");
|
||||||
Serial.print("PIN_UART_RX: ");
|
Serial.print("PIN_UART_RX: ");
|
||||||
Serial.println(PIN_UART_RX);
|
Serial.println(PIN_UART_RX);
|
||||||
|
|
||||||
mqttConnect();
|
//Disable blue LED
|
||||||
timer.setInterval(UPDATE_INTERVAL_SECONDS * 1000L, updateRadiationValues);
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
|
||||||
|
mqttReconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mqttConnect() {
|
|
||||||
while (!mqttClient.connected()) {
|
|
||||||
|
|
||||||
bool mqttConnected = false;
|
|
||||||
if (MQTT_USERNAME && MQTT_PASSWORD) {
|
|
||||||
mqttConnected = mqttClient.connect(hostname, MQTT_USERNAME, MQTT_PASSWORD, MQTT_TOPIC_LAST_WILL, 1, true, MQTT_LAST_WILL_PAYLOAD_DISCONNECTED);
|
|
||||||
} else {
|
|
||||||
mqttConnected = mqttClient.connect(hostname, MQTT_TOPIC_LAST_WILL, 1, true, MQTT_LAST_WILL_PAYLOAD_DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mqttConnected) {
|
|
||||||
Serial.println("Connected to MQTT Broker");
|
|
||||||
mqttClient.publish(MQTT_TOPIC_LAST_WILL, MQTT_LAST_WILL_PAYLOAD_CONNECTED, true);
|
|
||||||
mqttRetryCounter = 0;
|
|
||||||
|
|
||||||
#ifdef USE_HA_AUTODISCOVERY
|
|
||||||
setupHAAutodiscovery();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Serial.println("Failed to connect to MQTT Broker");
|
|
||||||
|
|
||||||
if (mqttRetryCounter++ > MQTT_MAX_CONNECT_RETRY) {
|
|
||||||
Serial.println("Restarting uC");
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_HA_AUTODISCOVERY
|
|
||||||
#define AUTOCONFIG_PAYLOAD_TPL_USV "{\
|
|
||||||
\"stat_t\":\"%s/%s/%s_uSv/state\",\
|
|
||||||
\"avty_t\":\"%s/%s/presence\",\
|
|
||||||
\"unique_id\":\"%s_uSv\",\
|
|
||||||
\"name\":\"%s uSv\",\
|
|
||||||
\"unit_of_meas\":\"µSv/h\",\
|
|
||||||
\"dev\": {\
|
|
||||||
\"identifiers\":\"%s\",\
|
|
||||||
\"name\":\"%s\",\
|
|
||||||
\"manufacturer\":\"MightyOhm LLC\",\
|
|
||||||
\"model\":\"Geiger Counter\"\
|
|
||||||
}\
|
|
||||||
}"
|
|
||||||
#define AUTOCONFIG_PAYLOAD_TPL_CPM "{\
|
|
||||||
\"stat_t\":\"%s/%s/%s_cpm/state\",\
|
|
||||||
\"avty_t\":\"%s/%s/presence\",\
|
|
||||||
\"unique_id\":\"%s_cpm\",\
|
|
||||||
\"name\":\"%s CPM\",\
|
|
||||||
\"unit_of_meas\":\"CPM\",\
|
|
||||||
\"dev\": {\
|
|
||||||
\"identifiers\":\"%s\",\
|
|
||||||
\"name\":\"%s\",\
|
|
||||||
\"manufacturer\":\"MightyOhm LLC\",\
|
|
||||||
\"model\":\"Geiger Counter\"\
|
|
||||||
}\
|
|
||||||
}"
|
|
||||||
|
|
||||||
void setupHAAutodiscovery() {
|
|
||||||
char autoconfig_topic_cpm[128];
|
|
||||||
char autoconfig_payload_cpm[1024];
|
|
||||||
char autoconfig_topic_usv[128];
|
|
||||||
char autoconfig_payload_usv[1024];
|
|
||||||
|
|
||||||
snprintf(
|
|
||||||
autoconfig_topic_cpm,
|
|
||||||
127,
|
|
||||||
"%s/sensor/%s/%s_cpm/config",
|
|
||||||
HA_DISCOVERY_PREFIX,
|
|
||||||
hostname,
|
|
||||||
hostname
|
|
||||||
);
|
|
||||||
|
|
||||||
snprintf(
|
|
||||||
autoconfig_topic_usv,
|
|
||||||
127,
|
|
||||||
"%s/sensor/%s/%s_usv/config",
|
|
||||||
HA_DISCOVERY_PREFIX,
|
|
||||||
hostname,
|
|
||||||
hostname
|
|
||||||
);
|
|
||||||
|
|
||||||
snprintf(
|
|
||||||
autoconfig_payload_cpm,
|
|
||||||
1023,
|
|
||||||
|
|
||||||
AUTOCONFIG_PAYLOAD_TPL_CPM,
|
|
||||||
|
|
||||||
FIRMWARE_PREFIX,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
FIRMWARE_PREFIX,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
hostname
|
|
||||||
);
|
|
||||||
|
|
||||||
snprintf(
|
|
||||||
autoconfig_payload_usv,
|
|
||||||
1023,
|
|
||||||
|
|
||||||
AUTOCONFIG_PAYLOAD_TPL_USV,
|
|
||||||
|
|
||||||
FIRMWARE_PREFIX,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
FIRMWARE_PREFIX,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
hostname,
|
|
||||||
hostname
|
|
||||||
);
|
|
||||||
|
|
||||||
if(
|
|
||||||
mqttClient.publish(autoconfig_topic_cpm, autoconfig_payload_cpm, true) &&
|
|
||||||
mqttClient.publish(autoconfig_topic_usv, autoconfig_payload_usv, true)
|
|
||||||
){
|
|
||||||
Serial.println("Autoconf publish successful");
|
|
||||||
} else {
|
|
||||||
Serial.println("Autoconf publish failed. Is MQTT_MAX_PACKET_SIZE large enough?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
timer.run();
|
|
||||||
mqttConnect();
|
|
||||||
mqttClient.loop();
|
mqttClient.loop();
|
||||||
ESP.wdtFeed();
|
|
||||||
|
|
||||||
if (geigerCounterSerial.available()) {
|
handleUart();
|
||||||
char in = (char) geigerCounterSerial.read();
|
|
||||||
|
|
||||||
serialInput += in;
|
|
||||||
|
|
||||||
if (in == '\n') {
|
|
||||||
serialInput.toCharArray(serialInputHelper, RECV_LINE_SIZE);
|
|
||||||
parseReceivedLine(serialInputHelper);
|
|
||||||
|
|
||||||
serialInput = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just in case the buffer gets to big, start from scratch
|
|
||||||
if (serialInput.length() > RECV_LINE_SIZE + 10) {
|
|
||||||
serialInput = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.write(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArduinoOTA.handle();
|
if (statusPublishInterval <= (millis() - statusPublishPreviousMillis)) {
|
||||||
}
|
statusPublishPreviousMillis = millis();
|
||||||
|
updateRadiationValues();
|
||||||
void updateRadiationValues() {
|
|
||||||
char tmp[8];
|
|
||||||
|
|
||||||
if (currentCPM != lastCPM) {
|
|
||||||
String(currentCPM).toCharArray(tmp, 8);
|
|
||||||
Serial.print("Sending CPM: ");
|
|
||||||
Serial.println(tmp);
|
|
||||||
mqttClient.publish(MQTT_TOPIC_CPM_MEASUREMENT, tmp, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentuSv != lastuSv) {
|
if (!mqttClient.connected() && (mqttConnectionInterval <= (millis() - lastMqttConnectionAttempt)) ) {
|
||||||
String(currentuSv).toCharArray(tmp, 8);
|
lastMqttConnectionAttempt = millis();
|
||||||
Serial.print("Sending uSv: ");
|
mqttReconnect();
|
||||||
Serial.println(tmp);
|
|
||||||
mqttClient.publish(MQTT_TOPIC_USV_MEASUREMENT, tmp, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastCPM = currentCPM;
|
|
||||||
lastuSv = currentuSv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void parseReceivedLine(char* input) {
|
void setupWifi() {
|
||||||
|
wifiManager.setDebugOutput(false);
|
||||||
|
wifiManager.setSaveConfigCallback(saveConfigCallback);
|
||||||
|
|
||||||
char segment = 0;
|
wifiManager.addParameter(&custom_mqtt_server);
|
||||||
char *token;
|
wifiManager.addParameter(&custom_mqtt_user);
|
||||||
|
wifiManager.addParameter(&custom_mqtt_pass);
|
||||||
|
|
||||||
float uSv = 0;
|
WiFi.hostname(identifier);
|
||||||
float cpm = 0;
|
wifiManager.autoConnect(identifier);
|
||||||
|
mqttClient.setClient(wifiClient);
|
||||||
|
|
||||||
token = strtok(input, delimiter);
|
strcpy(mqtt_server, custom_mqtt_server.getValue());
|
||||||
|
strcpy(username, custom_mqtt_user.getValue());
|
||||||
|
strcpy(password, custom_mqtt_pass.getValue());
|
||||||
|
|
||||||
while (token != NULL) {
|
if (shouldSaveConfig) {
|
||||||
|
saveConfig();
|
||||||
|
} else {
|
||||||
|
//For some reason, the read values get overwritten in this function
|
||||||
|
//To combat this, we just reload the config
|
||||||
|
//This is most likely a logic error which could be fixed otherwise
|
||||||
|
loadConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (segment) {
|
void resetWifiSettingsAndReboot() {
|
||||||
|
wifiManager.resetSettings();
|
||||||
|
delay(3000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
// This is just for validation
|
void mqttReconnect() {
|
||||||
case IDX_CPS_KEY: if (strcmp(token, "CPS") != 0) return; break;
|
for (int attempt = 0; attempt < 3; ++attempt) {
|
||||||
case IDX_CPM_KEY: if (strcmp(token, "CPM") != 0) return; break;
|
if (mqttClient.connect(identifier, username, password, MQTT_TOPIC_AVAILABILITY, 1, true, AVAILABILITY_OFFLINE)) {
|
||||||
case IDX_uSv_KEY: if (strcmp(token, "uSv/hr") != 0) return; break;
|
mqttClient.publish(MQTT_TOPIC_AVAILABILITY, AVAILABILITY_ONLINE, true);
|
||||||
|
|
||||||
case IDX_CPM:
|
Serial.println("Connected to MQTT Server");
|
||||||
Serial.printf("\nCurrent CPM: %s\n", token);
|
|
||||||
cpm = String(token).toInt();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IDX_uSv:
|
publishAutoConfig();
|
||||||
Serial.printf("Current uSv/hr: %s\n", token);
|
break;
|
||||||
uSv = String(token).toFloat();
|
} else {
|
||||||
break;
|
Serial.println("Failed to connect to MQTT Server :(");
|
||||||
|
delay(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment > 7) {
|
|
||||||
// Invalid! There should be no more than 7 segments
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
token = strtok(NULL, delimiter);
|
|
||||||
segment++;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
currentuSv = uSv;
|
|
||||||
currentCPM = cpm;
|
boolean isMqttConnected() {
|
||||||
|
return mqttClient.connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
void publishAutoConfig() {
|
||||||
|
char mqttPayload[2048];
|
||||||
|
DynamicJsonDocument device(256);
|
||||||
|
StaticJsonDocument<64> identifiersDoc;
|
||||||
|
JsonArray identifiers = identifiersDoc.to<JsonArray>();
|
||||||
|
|
||||||
|
identifiers.add(identifier);
|
||||||
|
|
||||||
|
device["identifiers"] = identifiers;
|
||||||
|
device["manufacturer"] = "MightyOhm LLC";
|
||||||
|
device["model"] = "Geiger Counter";
|
||||||
|
device["name"] = identifier;
|
||||||
|
device["sw_version"] = "0.0.1";
|
||||||
|
|
||||||
|
|
||||||
|
DynamicJsonDocument cpmSensorPayload(512);
|
||||||
|
|
||||||
|
cpmSensorPayload["device"] = device.as<JsonObject>();
|
||||||
|
cpmSensorPayload["availability_topic"] = MQTT_TOPIC_AVAILABILITY;
|
||||||
|
cpmSensorPayload["state_topic"] = MQTT_TOPIC_CPM;
|
||||||
|
cpmSensorPayload["name"] = identifier + String(" CPM");
|
||||||
|
cpmSensorPayload["unit_of_measurement"] = "CPM";
|
||||||
|
cpmSensorPayload["unique_id"] = identifier + String("_cpm");
|
||||||
|
|
||||||
|
serializeJson(cpmSensorPayload, mqttPayload);
|
||||||
|
mqttClient.publish(MQTT_TOPIC_AUTOCONF_CPM, mqttPayload, true);
|
||||||
|
|
||||||
|
DynamicJsonDocument usvSensorPayload(512);
|
||||||
|
|
||||||
|
usvSensorPayload["device"] = device.as<JsonObject>();
|
||||||
|
usvSensorPayload["availability_topic"] = MQTT_TOPIC_AVAILABILITY;
|
||||||
|
usvSensorPayload["state_topic"] = MQTT_TOPIC_USV;
|
||||||
|
usvSensorPayload["name"] = identifier + String(" uSv");
|
||||||
|
usvSensorPayload["unit_of_measurement"] = "µSv/h";
|
||||||
|
usvSensorPayload["unique_id"] = identifier + String("_uSv");
|
||||||
|
|
||||||
|
serializeJson(usvSensorPayload, mqttPayload);
|
||||||
|
mqttClient.publish(MQTT_TOPIC_AUTOCONF_USV, mqttPayload, true);
|
||||||
|
|
||||||
|
Serial.println("Published MQTT Autoconf");
|
||||||
}
|
}
|
||||||
|
|
81
serialCommunication.ino
Normal file
81
serialCommunication.ino
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
const char* delimiter = ", ";
|
||||||
|
int lastCPM = 0, currentCPM = 0;
|
||||||
|
float lastuSv = 0, currentuSv = 0;
|
||||||
|
|
||||||
|
byte serialRxBuf[255];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void handleUart() {
|
||||||
|
if (geigerCounterSerial.available()) {
|
||||||
|
geigerCounterSerial.readBytesUntil('\n', serialRxBuf, 250);
|
||||||
|
|
||||||
|
parseReceivedLine((char*)serialRxBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseReceivedLine(char* input) {
|
||||||
|
Serial.println(input);
|
||||||
|
|
||||||
|
char segment = 0;
|
||||||
|
char *token;
|
||||||
|
|
||||||
|
float uSv = 0;
|
||||||
|
float cpm = 0;
|
||||||
|
|
||||||
|
token = strtok(input, delimiter);
|
||||||
|
|
||||||
|
while (token != NULL) {
|
||||||
|
|
||||||
|
switch (segment) {
|
||||||
|
|
||||||
|
// This is just for validation
|
||||||
|
case IDX_CPS_KEY: if (strcmp(token, "CPS") != 0) return; break;
|
||||||
|
case IDX_CPM_KEY: if (strcmp(token, "CPM") != 0) return; break;
|
||||||
|
case IDX_uSv_KEY: if (strcmp(token, "uSv/hr") != 0) return; break;
|
||||||
|
|
||||||
|
case IDX_CPM:
|
||||||
|
//Serial.printf("\nCurrent CPM: %s\n", token);
|
||||||
|
cpm = String(token).toInt();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IDX_uSv:
|
||||||
|
//Serial.printf("Current uSv/hr: %s\n", token);
|
||||||
|
uSv = String(token).toFloat();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment > 7) {
|
||||||
|
// Invalid! There should be no more than 7 segments
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, delimiter);
|
||||||
|
segment++;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentuSv = uSv;
|
||||||
|
currentCPM = cpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateRadiationValues() {
|
||||||
|
char tmp[8];
|
||||||
|
|
||||||
|
if (currentCPM != lastCPM) {
|
||||||
|
String(currentCPM).toCharArray(tmp, 8);
|
||||||
|
Serial.print("Sending CPM: ");
|
||||||
|
Serial.println(tmp);
|
||||||
|
mqttClient.publish(MQTT_TOPIC_CPM, tmp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentuSv != lastuSv) {
|
||||||
|
String(currentuSv).toCharArray(tmp, 8);
|
||||||
|
Serial.print("Sending uSv: ");
|
||||||
|
Serial.println(tmp);
|
||||||
|
mqttClient.publish(MQTT_TOPIC_USV, tmp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCPM = currentCPM;
|
||||||
|
lastuSv = currentuSv;
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
#define IDX_CPS_KEY 0
|
|
||||||
#define IDX_CPM_KEY 2
|
|
||||||
#define IDX_uSv_KEY 4
|
|
||||||
|
|
||||||
#define IDX_CPM 3
|
|
||||||
#define IDX_uSv 5
|
|
||||||
#define IDX_MODE 6
|
|
||||||
|
|
||||||
#define RECV_LINE_SIZE 37
|
|
||||||
|
|
||||||
#define PIN_UART_RX 0 // 4
|
|
||||||
#define PIN_UART_TX 13 // UNUSED
|
|
||||||
|
|
||||||
#define UPDATE_INTERVAL_SECONDS 60L
|
|
||||||
|
|
||||||
#define BAUD_GEIGERCOUNTER 9600
|
|
||||||
|
|
||||||
//#define HOSTNAME "ESP-GeigerCounter" //Optional
|
|
||||||
|
|
||||||
const char* OTA_PASSWORD = "foobar";
|
|
||||||
|
|
||||||
#define MQTT_HOST "mqtt.core.bckspc.de"
|
|
||||||
|
|
||||||
//If you don't want to use home-assistant autodiscovery comment this out
|
|
||||||
#define USE_HA_AUTODISCOVERY
|
|
||||||
|
|
||||||
#ifdef USE_HA_AUTODISCOVERY
|
|
||||||
#define HA_DISCOVERY_PREFIX "homeassistant"
|
|
||||||
const char* MQTT_LAST_WILL_PAYLOAD_CONNECTED = "online";
|
|
||||||
const char* MQTT_LAST_WILL_PAYLOAD_DISCONNECTED = "offline";
|
|
||||||
#else
|
|
||||||
//If you're not using HA Autodiscovery, you can specify your topics here
|
|
||||||
|
|
||||||
const char* MQTT_TOPIC_CPM_MEASUREMENT = "sensor/radiation/cpm";
|
|
||||||
const char* MQTT_TOPIC_USV_MEASUREMENT = "sensor/radiation/uSv";
|
|
||||||
const char* MQTT_TOPIC_LAST_WILL = "sensor/radiation/will";
|
|
||||||
const char* MQTT_LAST_WILL_PAYLOAD_CONNECTED = "connected";
|
|
||||||
const char* MQTT_LAST_WILL_PAYLOAD_DISCONNECTED = "disconnected";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
const char* MQTT_USERNAME = NULL;
|
|
||||||
const char* MQTT_PASSWORD = NULL;
|
|
||||||
const uint8_t MQTT_MAX_CONNECT_RETRY = 42;
|
|
||||||
|
|
||||||
|
|
||||||
const char* delimiter = ", ";
|
|
4
wifi.h
Normal file
4
wifi.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
char mqtt_server[80] = "example.tld";
|
||||||
|
|
||||||
|
char username[24] = "";
|
||||||
|
char password[24] = "";
|
Loading…
Reference in a new issue