Updated boolean write to MQTT
This commit is contained in:
BIN
data/homie/ui_bundle.gz
Normal file
BIN
data/homie/ui_bundle.gz
Normal file
Binary file not shown.
@@ -1,132 +0,0 @@
|
||||
#include <PubSubClient.h>
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
class MQTTHelper {
|
||||
private:
|
||||
String _group = "homedevice";
|
||||
String _identifier = "garagesensor";
|
||||
const char* _user = "homedevice";
|
||||
const char* _password = "WNzAb4VazNFUPlpkn0ED";
|
||||
|
||||
WiFiClient _espClient;
|
||||
PubSubClient _mqttClient;
|
||||
String _will_topic;
|
||||
String _command_topic;
|
||||
String _announce_topic;
|
||||
|
||||
void reconnect(int delayMillis = 5000);
|
||||
bool connect();
|
||||
void announce();
|
||||
void callback(char* topic, byte* payload, unsigned int length);
|
||||
public:
|
||||
MQTTHelper() : _espClient(), _mqttClient(_espClient) {
|
||||
auto deviceMac = WiFi.macAddress();
|
||||
deviceMac.replace(":","");
|
||||
_identifier = "etxean-"+deviceMac.substring(6);
|
||||
_will_topic = getTopic("online");
|
||||
_command_topic = _group + "/command";
|
||||
_announce_topic = _group+"/announce";
|
||||
_mqttClient.setCallback([this] (char* topic, byte* payload, unsigned int length) { this->callback(topic, payload, length); });
|
||||
}
|
||||
|
||||
String getIdentifier() { return _identifier; }
|
||||
|
||||
String getTopic(String node) {
|
||||
return _group + "/" + _identifier + "/" + node;
|
||||
}
|
||||
|
||||
void Configure(IPAddress server, uint16 port) {
|
||||
Serial.println("MQTT: configuring connection to "+server.toString()+":"+String(port));
|
||||
_mqttClient.setServer(server, port);
|
||||
if (_mqttClient.connected()) {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static unsigned long lastWillTopicSent;
|
||||
if (!_mqttClient.connected()) {
|
||||
reconnect();
|
||||
}
|
||||
_mqttClient.loop();
|
||||
|
||||
auto now = millis();
|
||||
if ((now - lastWillTopicSent) > 1000)
|
||||
{
|
||||
lastWillTopicSent = now;
|
||||
_mqttClient.publish(_will_topic.c_str(), "true");
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void publish(String node, T payload, bool retained = false, bool debug = true) {
|
||||
auto topic = getTopic(node);
|
||||
auto payload_s = String(payload);
|
||||
if (debug)
|
||||
Serial.println("MQTT: publish: "+topic+" = "+payload_s);
|
||||
_mqttClient.publish(topic.c_str(), payload_s.c_str(), retained);
|
||||
}
|
||||
};
|
||||
|
||||
void MQTTHelper::callback(char* topic_chars, byte* payload_bytes, unsigned int length) {
|
||||
String topic(topic_chars);
|
||||
payload_bytes[length] = '\0';
|
||||
String payload = (char*)payload_bytes;
|
||||
Serial.print("MQTT: callback on: ");
|
||||
Serial.println(topic+" = "+payload);
|
||||
String commandTopic = getTopic("command");
|
||||
|
||||
if (topic == _command_topic && payload == "announce") {
|
||||
Serial.println("Announcing");
|
||||
announce();
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTHelper::reconnect(int delayMillis) {
|
||||
// Loop until we're reconnected
|
||||
while (!_mqttClient.connected()) {
|
||||
Serial.print("MQTT: attempting connection...");
|
||||
if (connect()) {
|
||||
Serial.println("connected");
|
||||
} else {
|
||||
auto reconnectSeconds = (int)round(delayMillis/1000.0);
|
||||
Serial.print("failed, rc=");
|
||||
Serial.print(_mqttClient.state());
|
||||
Serial.println(" try again in "+String(reconnectSeconds)+" seconds");
|
||||
// Wait 5 seconds before retrying
|
||||
delay(delayMillis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTHelper::announce() {
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["ip"] = WiFi.localIP().toString();
|
||||
doc["mac"] = WiFi.macAddress();
|
||||
String payloadSettings;
|
||||
serializeJson(doc,payloadSettings);
|
||||
// publish to settings topic
|
||||
publish("settings", payloadSettings);
|
||||
|
||||
doc["id"] = _identifier;
|
||||
String payloadAnnounce;
|
||||
serializeJson(doc,payloadAnnounce);
|
||||
// publish to genneral announce topic
|
||||
Serial.println("MQTT: "+_announce_topic+" = "+payloadAnnounce);
|
||||
_mqttClient.publish(_announce_topic.c_str(),payloadAnnounce.c_str());
|
||||
}
|
||||
|
||||
bool MQTTHelper::connect() {
|
||||
// If you do not want to use a username and password, change next line to
|
||||
// if (_mqttClient.connect(_identifier.c_str()) {
|
||||
if (_mqttClient.connect(_identifier.c_str(), _user, _password, _will_topic.c_str(), 2, false, "false")) {
|
||||
_mqttClient.subscribe((_command_topic).c_str());
|
||||
announce();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ framework = arduino
|
||||
upload_speed = 921600
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
PubSubClient
|
||||
ArduinoJson
|
||||
NewPing
|
||||
WiFiManager
|
||||
Homie
|
||||
|
||||
|
||||
200
src/main.cpp
200
src/main.cpp
@@ -1,128 +1,95 @@
|
||||
#include <FS.h> //this needs to be first, or it all crashes and burns...
|
||||
|
||||
#include "MQTTHelper.h"
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <DNSServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <NewPing.h>
|
||||
#include <Homie.h>
|
||||
|
||||
#define TURN_ON LOW
|
||||
#define TURN_OFF HIGH
|
||||
|
||||
const int trigPin = D1; //D6
|
||||
const int echoPin = D2; //D5
|
||||
const int ledPin = LED_BUILTIN; //D1
|
||||
const int trigPin = D1; //D1
|
||||
const int echoPin = D2; //D2
|
||||
const int ledPin = LED_BUILTIN;
|
||||
const float minDistance = 0.01;
|
||||
const float maxDistance = 4.0;
|
||||
const float minimumChange = 0.2;
|
||||
const int updateInterval_ms = 1; // s
|
||||
const int ledInterval_ms = 5; // s
|
||||
|
||||
struct Config {
|
||||
IPAddress mqtt_server = {192, 168, 249, 5};
|
||||
uint16 mqtt_port = 1883;
|
||||
} config;
|
||||
|
||||
MQTTHelper mqtt;
|
||||
#define DISTANCE_INTERVAL 5 // s
|
||||
#define LED_INTERVAL 5 // s
|
||||
|
||||
NewPing sonar(trigPin,echoPin,maxDistance*100.0);
|
||||
HomieNode obstacleNode("obstacle", "Obstacle", "object");
|
||||
|
||||
#define heartbeatMillis 5000
|
||||
#define messageMillis 5000
|
||||
|
||||
//flag for saving data
|
||||
bool shouldSaveConfig = false;
|
||||
//callback notifying us of the need to save config
|
||||
void saveConfigCallback () {
|
||||
Serial.println("Should save config");
|
||||
shouldSaveConfig = true;
|
||||
}
|
||||
|
||||
void signal_led(bool ons = true);
|
||||
void heartbeat_led(int times = 2);
|
||||
void longbeat_led();
|
||||
void setup_wifi(String ssid, String password);
|
||||
void reconnect();
|
||||
void readConfig();
|
||||
void saveConfig();
|
||||
void loopHandler();
|
||||
void setupHandler();
|
||||
float readDistance();
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial << endl << endl;
|
||||
randomSeed(analogRead(0));
|
||||
pinMode(ledPin, OUTPUT);
|
||||
digitalWrite(ledPin, TURN_OFF);
|
||||
|
||||
//clean FS, for testing
|
||||
//SPIFFS.format();
|
||||
|
||||
readConfig();
|
||||
//WiFiManager
|
||||
WiFiManager wifiManager;
|
||||
//wifiManager.resetSettings(); //reset saved settings
|
||||
|
||||
WiFiManagerParameter custom_mqtt_server("MQTT server", "mqtt server", config.mqtt_server.toString().c_str(), 40);
|
||||
WiFiManagerParameter custom_mqtt_port("MQTT port", "mqtt port", String(config.mqtt_port).c_str(), 6);
|
||||
wifiManager.setSaveConfigCallback(saveConfigCallback);
|
||||
wifiManager.addParameter(&custom_mqtt_server);
|
||||
wifiManager.addParameter(&custom_mqtt_port);
|
||||
wifiManager.autoConnect(mqtt.getIdentifier().c_str());
|
||||
|
||||
//if you get here you have connected to the WiFi
|
||||
Serial.println("WiFi connected to: "+WiFi.SSID()+", IP address: "+WiFi.localIP().toString());
|
||||
|
||||
//read updated parameters
|
||||
config.mqtt_server.fromString(custom_mqtt_server.getValue());
|
||||
config.mqtt_port = String(custom_mqtt_port.getValue()).toInt();
|
||||
saveConfig();
|
||||
mqtt.Configure(config.mqtt_server,config.mqtt_port);
|
||||
}
|
||||
|
||||
long lastMessage = 0;
|
||||
long lastHeartbeat = 0;
|
||||
float oldDistance = 0.0;
|
||||
float diff = 4.0;
|
||||
|
||||
bool checkBounds(float value, float min, float max) {
|
||||
return !isnan(value) &&
|
||||
(value >= min && value <= max);
|
||||
}
|
||||
|
||||
bool signalChange(float distance, float oldDistance) {
|
||||
return fabs(distance - oldDistance) > minimumChange;
|
||||
Homie_setBrand("EtxeanIoT");
|
||||
Homie_setFirmware("etxean-distancesensor", "1.0.0");
|
||||
Homie.setLoopFunction(loopHandler);
|
||||
obstacleNode
|
||||
.advertise("distance").setName("Distance").setDatatype("float").setUnit("m");
|
||||
obstacleNode
|
||||
.advertise("valid").setName("Valid measurement").setDatatype("boolean");
|
||||
obstacleNode
|
||||
.advertise("changed").setName("Obstacle changed").setDatatype("boolean");
|
||||
Homie.setup();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
mqtt.loop();
|
||||
|
||||
long now = millis();
|
||||
if (now - lastHeartbeat > heartbeatMillis) {
|
||||
lastHeartbeat = now;
|
||||
heartbeat_led();
|
||||
Homie.loop();
|
||||
}
|
||||
|
||||
if (now - lastMessage > messageMillis) {
|
||||
lastMessage = now;
|
||||
long lastHeartbeat = 0;
|
||||
long lastDistanceSent = 0;
|
||||
float lastDistance = 0.0;
|
||||
|
||||
bool checkBounds(float value, float min, float max) {
|
||||
return !isnan(value) && value >= min && value <= max;
|
||||
}
|
||||
|
||||
bool signalChange(float distance, float lastDistance) {
|
||||
return fabs(distance - lastDistance) > minimumChange;
|
||||
}
|
||||
|
||||
String toPayload(bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
void loopHandler() {
|
||||
if (millis() - lastDistanceSent >= DISTANCE_INTERVAL * 1000UL || lastDistanceSent == 0) {
|
||||
heartbeat_led();
|
||||
float distance = readDistance();
|
||||
//float distance = random(5,400)/100.0;
|
||||
// Serial.print("Distance read:");
|
||||
// Serial.print(int(distance*1000));
|
||||
// Serial.println(" mm");
|
||||
Homie.getLogger() << "Distance: " << distance << " m";
|
||||
obstacleNode.setProperty("distance").send(String(distance));
|
||||
|
||||
if (checkBounds(distance, 0, maxDistance)) {
|
||||
mqtt.publish("distance", distance);
|
||||
|
||||
bool signal = signalChange(distance, oldDistance);
|
||||
mqtt.publish("signal", signal);
|
||||
if (signal)
|
||||
bool valid = checkBounds(distance,minDistance,maxDistance);
|
||||
Serial << ", valid = " << toPayload(valid);
|
||||
obstacleNode.setProperty("valid").send(toPayload(valid));
|
||||
if (valid)
|
||||
{
|
||||
bool changed = signalChange(distance, lastDistance);
|
||||
obstacleNode.setProperty("changed").send(toPayload(changed));
|
||||
if (changed)
|
||||
{
|
||||
signal_led();
|
||||
Serial << ", changed";
|
||||
}
|
||||
|
||||
oldDistance = distance;
|
||||
lastDistance = distance;
|
||||
}
|
||||
Serial << endl;
|
||||
lastDistanceSent = millis();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,59 +115,6 @@ float readDistance() {
|
||||
return computedDistance;
|
||||
}
|
||||
|
||||
void readConfig() {
|
||||
//read configuration from FS json
|
||||
Serial.println("mounting FS...");
|
||||
if (SPIFFS.begin()) {
|
||||
Serial.println("mounted file system");
|
||||
if (SPIFFS.exists("/config.json")) {
|
||||
//file exists, reading and loading
|
||||
Serial.println("reading config.json");
|
||||
File configFile = SPIFFS.open("/config.json", "r");
|
||||
if (configFile) {
|
||||
Serial.println("opened config.json");
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc,configFile);
|
||||
serializeJsonPretty(doc,Serial);
|
||||
if (error) {
|
||||
Serial.print("failed to load config.json: ");
|
||||
Serial.println(error.c_str());
|
||||
return;
|
||||
}
|
||||
Serial.println("\nparsed config.json");
|
||||
IPAddress addr;
|
||||
if (addr.fromString(doc["mqtt_server"].as<char*>())) {
|
||||
config.mqtt_server = addr;
|
||||
}
|
||||
config.mqtt_port = doc["mqtt_port"];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.println("failed to mount FS");
|
||||
}
|
||||
//end read
|
||||
}
|
||||
|
||||
void saveConfig() {
|
||||
//save the custom parameters to FS
|
||||
if (shouldSaveConfig) {
|
||||
Serial.println("saving config.json");
|
||||
DynamicJsonDocument doc(1024);
|
||||
doc["mqtt_server"] = config.mqtt_server.toString();
|
||||
doc["mqtt_port"] = String(config.mqtt_port);
|
||||
|
||||
File configFile = SPIFFS.open("/config.json", "w");
|
||||
if (!configFile) {
|
||||
Serial.println("failed to open config.json for writing");
|
||||
}
|
||||
|
||||
serializeJsonPretty(doc,Serial);
|
||||
serializeJson(doc,configFile);
|
||||
configFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void signal_led(bool on)
|
||||
{
|
||||
if (on)
|
||||
|
||||
Reference in New Issue
Block a user