forked from github/esp8266-geigercounter
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
|
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 <ArduinoOTA.h>
|
||||
#include <SoftwareSerial.h>
|
||||
#include <DNSServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
|
||||
#include "dep/pubsubclient-2.7/src/PubSubClient.cpp"
|
||||
#include "dep/WiFiManager-0.15.0/WiFiManager.cpp"
|
||||
#include "dep/SimpleTimer-schinken/SimpleTimer.cpp"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <PubSubClient.h>
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
#ifdef USE_HA_AUTODISCOVERY
|
||||
#define FIRMWARE_PREFIX "esp8266-geigercounter"
|
||||
char MQTT_TOPIC_LAST_WILL[128];
|
||||
char MQTT_TOPIC_CPM_MEASUREMENT[128];
|
||||
char MQTT_TOPIC_USV_MEASUREMENT[128];
|
||||
#endif
|
||||
uint8_t mqttRetryCounter = 0;
|
||||
|
||||
|
||||
WiFiManager wifiManager;
|
||||
WiFiClient wifiClient;
|
||||
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);
|
||||
SimpleTimer timer;
|
||||
|
||||
uint8_t mqttRetryCounter = 0;
|
||||
String serialInput = "";
|
||||
char serialInputHelper[RECV_LINE_SIZE];
|
||||
|
||||
int lastCPM = 0, currentCPM = 0;
|
||||
float lastuSv = 0, currentuSv = 0;
|
||||
bool shouldSaveConfig = false;
|
||||
|
||||
char hostname[24];
|
||||
void saveConfigCallback () {
|
||||
shouldSaveConfig = true;
|
||||
}
|
||||
|
||||
void setup() {
|
||||
delay(3000);
|
||||
Serial.begin(115200);
|
||||
Serial.begin(9600);
|
||||
delay(2000);
|
||||
Serial.println("\n");
|
||||
Serial.println("Hello from esp8266-geigercounter");
|
||||
|
@ -43,290 +58,150 @@ void setup() {
|
|||
Serial.printf("Boot Mode: %u\n", ESP.getBootMode());
|
||||
Serial.printf("CPU Frequency: %u MHz\n", ESP.getCpuFreqMHz());
|
||||
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());
|
||||
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);
|
||||
|
||||
snprintf(MQTT_TOPIC_AUTOCONF_CPM, 127, "homeassistant/sensor/%s/%s_cpm/config", FIRMWARE_PREFIX, identifier);
|
||||
snprintf(MQTT_TOPIC_AUTOCONF_USV, 127, "homeassistant/sensor/%s/%s_usv/config", FIRMWARE_PREFIX, identifier);
|
||||
|
||||
|
||||
int32_t chipid = ESP.getChipId();
|
||||
|
||||
Serial.print("MQTT_MAX_PACKET_SIZE: ");
|
||||
Serial.println(MQTT_MAX_PACKET_SIZE);
|
||||
WiFi.hostname(identifier);
|
||||
|
||||
|
||||
#ifdef HOSTNAME
|
||||
strncpy(hostname, HOSTNAME, sizeof(hostname));
|
||||
#else
|
||||
snprintf(hostname, sizeof(hostname), "GEIGERCTR-%X", chipid);
|
||||
#endif
|
||||
|
||||
#ifdef USE_HA_AUTODISCOVERY
|
||||
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");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
unsigned long wifiConnectStart = millis();
|
||||
while (WiFi.status() != WL_CONNECTED) {
|
||||
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();
|
||||
loadConfig();
|
||||
setupWifi();
|
||||
mqttClient.setServer(mqtt_server, 1883);
|
||||
mqttClient.setKeepAlive(10);
|
||||
mqttClient.setBufferSize(2048);
|
||||
|
||||
Serial.print("Hostname: ");
|
||||
Serial.println(hostname);
|
||||
Serial.println(identifier);
|
||||
Serial.print("\nIP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
Serial.println("-- Current GPIO Configuration --");
|
||||
Serial.print("PIN_UART_RX: ");
|
||||
Serial.println(PIN_UART_RX);
|
||||
|
||||
mqttConnect();
|
||||
timer.setInterval(UPDATE_INTERVAL_SECONDS * 1000L, updateRadiationValues);
|
||||
//Disable blue LED
|
||||
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);
|
||||
void loop() {
|
||||
mqttClient.loop();
|
||||
|
||||
handleUart();
|
||||
|
||||
if (statusPublishInterval <= (millis() - statusPublishPreviousMillis)) {
|
||||
statusPublishPreviousMillis = millis();
|
||||
updateRadiationValues();
|
||||
}
|
||||
|
||||
if (mqttConnected) {
|
||||
Serial.println("Connected to MQTT Broker");
|
||||
mqttClient.publish(MQTT_TOPIC_LAST_WILL, MQTT_LAST_WILL_PAYLOAD_CONNECTED, true);
|
||||
mqttRetryCounter = 0;
|
||||
if (!mqttClient.connected() && (mqttConnectionInterval <= (millis() - lastMqttConnectionAttempt)) ) {
|
||||
lastMqttConnectionAttempt = millis();
|
||||
mqttReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_HA_AUTODISCOVERY
|
||||
setupHAAutodiscovery();
|
||||
#endif
|
||||
void setupWifi() {
|
||||
wifiManager.setDebugOutput(false);
|
||||
wifiManager.setSaveConfigCallback(saveConfigCallback);
|
||||
|
||||
wifiManager.addParameter(&custom_mqtt_server);
|
||||
wifiManager.addParameter(&custom_mqtt_user);
|
||||
wifiManager.addParameter(&custom_mqtt_pass);
|
||||
|
||||
WiFi.hostname(identifier);
|
||||
wifiManager.autoConnect(identifier);
|
||||
mqttClient.setClient(wifiClient);
|
||||
|
||||
strcpy(mqtt_server, custom_mqtt_server.getValue());
|
||||
strcpy(username, custom_mqtt_user.getValue());
|
||||
strcpy(password, custom_mqtt_pass.getValue());
|
||||
|
||||
if (shouldSaveConfig) {
|
||||
saveConfig();
|
||||
} else {
|
||||
Serial.println("Failed to connect to MQTT Broker");
|
||||
//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();
|
||||
}
|
||||
}
|
||||
|
||||
if (mqttRetryCounter++ > MQTT_MAX_CONNECT_RETRY) {
|
||||
Serial.println("Restarting uC");
|
||||
void resetWifiSettingsAndReboot() {
|
||||
wifiManager.resetSettings();
|
||||
delay(3000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
delay(2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
void mqttReconnect() {
|
||||
for (int attempt = 0; attempt < 3; ++attempt) {
|
||||
if (mqttClient.connect(identifier, username, password, MQTT_TOPIC_AVAILABILITY, 1, true, AVAILABILITY_OFFLINE)) {
|
||||
mqttClient.publish(MQTT_TOPIC_AVAILABILITY, AVAILABILITY_ONLINE, true);
|
||||
|
||||
Serial.println("Connected to MQTT Server");
|
||||
|
||||
#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");
|
||||
publishAutoConfig();
|
||||
break;
|
||||
} else {
|
||||
Serial.println("Autoconf publish failed. Is MQTT_MAX_PACKET_SIZE large enough?");
|
||||
Serial.println("Failed to connect to MQTT Server :(");
|
||||
delay(5000);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void loop() {
|
||||
timer.run();
|
||||
mqttConnect();
|
||||
mqttClient.loop();
|
||||
ESP.wdtFeed();
|
||||
|
||||
if (geigerCounterSerial.available()) {
|
||||
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 = "";
|
||||
boolean isMqttConnected() {
|
||||
return mqttClient.connected();
|
||||
}
|
||||
|
||||
Serial.write(in);
|
||||
}
|
||||
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
|
||||
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) {
|
||||
String(currentuSv).toCharArray(tmp, 8);
|
||||
Serial.print("Sending uSv: ");
|
||||
Serial.println(tmp);
|
||||
mqttClient.publish(MQTT_TOPIC_USV_MEASUREMENT, tmp, true);
|
||||
}
|
||||
|
||||
lastCPM = currentCPM;
|
||||
lastuSv = currentuSv;
|
||||
}
|
||||
|
||||
void parseReceivedLine(char* 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 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