Mqtt with HA configuration
This commit is contained in:
parent
156d92b7f8
commit
fc3416f12c
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
|||||||
[submodule "lib/EasyButton"]
|
[submodule "lib/EasyButton"]
|
||||||
path = lib/EasyButton
|
path = lib/EasyButton
|
||||||
url = https://github.com/evert-arias/EasyButton.git
|
url = https://github.com/evert-arias/EasyButton.git
|
||||||
|
[submodule "lib/pubsubclient"]
|
||||||
|
path = lib/pubsubclient
|
||||||
|
url = https://github.com/knolleary/pubsubclient.git
|
||||||
|
34
README.md
34
README.md
@ -10,9 +10,39 @@ Before sending press the on board button "flash" or connect a button to D0. You
|
|||||||
{
|
{
|
||||||
"hostname": "your_hostname",
|
"hostname": "your_hostname",
|
||||||
"ssid": "your_ssid",
|
"ssid": "your_ssid",
|
||||||
"pass": "[48..."
|
"wifipass": "[48...",
|
||||||
|
"mqttserver": "mqtt.mydomain.com",
|
||||||
|
"mqttuser": "mymqtt_user",
|
||||||
|
"mqttpass": "7j%!fs#",
|
||||||
|
"mqttport": "1883",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## OTA Updates
|
## OTA Updates
|
||||||
Configured in vscode, inside of *platformio.ini* change the board name to what you've set in the configuration. Then upload sketch updates via WiFi by clicking the *Upload* button in *platformIO* under the *evn:esp8266 OTA* configuration
|
Configured in vscode, inside of *platformio.ini* change the board name to what you've set in the configuration. Then upload sketch updates via WiFi by clicking the *Upload* button in *platformIO* under the *evn:esp8266 OTA* configuration
|
||||||
|
|
||||||
|
## MQTT
|
||||||
|
Topics....
|
||||||
|
Example payload:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"brightness": 255,
|
||||||
|
"color": {
|
||||||
|
"r": 255,
|
||||||
|
"g": 180,
|
||||||
|
"b": 200
|
||||||
|
},
|
||||||
|
"state": "ON"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Home Assistant
|
||||||
|
Adjust the name and topics to suite your configuration
|
||||||
|
```
|
||||||
|
- name: Bedside Lamp
|
||||||
|
platform: mqtt
|
||||||
|
schema: json
|
||||||
|
state_topic: light/bedlamp/relay/0
|
||||||
|
command_topic: light/bedlamp/relay/0/set
|
||||||
|
brightness: true
|
||||||
|
rgb: true
|
||||||
|
```
|
@ -9,6 +9,10 @@ public:
|
|||||||
char hostname[20];
|
char hostname[20];
|
||||||
char ssid[20];
|
char ssid[20];
|
||||||
char pass[20];
|
char pass[20];
|
||||||
|
char mqttServer[20];
|
||||||
|
char mqttUser[10];
|
||||||
|
char mqttPass[20];
|
||||||
|
int mqttPort;
|
||||||
uint8_t brightness;
|
uint8_t brightness;
|
||||||
std::array<uint8_t, 3> color;
|
std::array<uint8_t, 3> color;
|
||||||
} Data;
|
} Data;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <Adafruit_NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
|
|
||||||
class MyLed {
|
class MyLed {
|
||||||
@ -12,18 +14,32 @@ public:
|
|||||||
|
|
||||||
void toggle();
|
void toggle();
|
||||||
|
|
||||||
|
//! @param state Json package
|
||||||
|
//! {
|
||||||
|
//! "brightness": 255,
|
||||||
|
//! "color": {
|
||||||
|
//! "r": 255,
|
||||||
|
//! "g": 180,
|
||||||
|
//! "b": 200,
|
||||||
|
//! },
|
||||||
|
//! "state": "ON"
|
||||||
|
//! }
|
||||||
|
void set(String jsonState);
|
||||||
|
|
||||||
|
String get();
|
||||||
|
|
||||||
//! @param brightness in [0, 1]
|
//! @param brightness in [0, 1]
|
||||||
void setBrightness(float brightness);
|
void setBrightness(float brightness);
|
||||||
|
|
||||||
//! @param diff in [-1, 1]
|
//! @param diff in [-1, 1]
|
||||||
void adjustBrightness(float diff);
|
void adjustBrightness(float diff);
|
||||||
|
|
||||||
|
void setColor(uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! Called by public setBrightness. This function
|
void _apply();
|
||||||
//! will not store the brightness
|
|
||||||
void _setBrightness(float brightness);
|
|
||||||
|
|
||||||
bool _useEffects;
|
bool _useEffects;
|
||||||
|
|
||||||
@ -35,5 +51,5 @@ private:
|
|||||||
|
|
||||||
const std::array<float, 3> _defColor;
|
const std::array<float, 3> _defColor;
|
||||||
|
|
||||||
const std::array<float, 3> _color;
|
std::array<float, 3> _color;
|
||||||
};
|
};
|
1
lib/pubsubclient
Submodule
1
lib/pubsubclient
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 2d228f2f862a95846c65a8518c79f48dfc8f188c
|
@ -9,7 +9,11 @@ void printConfig(Config::Data data)
|
|||||||
{
|
{
|
||||||
Serial.printf("hostname: %s\n", data.hostname);
|
Serial.printf("hostname: %s\n", data.hostname);
|
||||||
Serial.printf("ssid: %s\n", data.ssid);
|
Serial.printf("ssid: %s\n", data.ssid);
|
||||||
Serial.printf("pass: %s\n", data.pass);
|
Serial.printf("wifi pass: %s\n", data.pass);
|
||||||
|
Serial.printf("mqtt server: %s\n", data.mqttServer);
|
||||||
|
Serial.printf("mqtt user: %s\n", data.mqttUser);
|
||||||
|
Serial.printf("mqtt pass: %s\n", data.mqttPass);
|
||||||
|
Serial.printf("mqtt port: %d\n", data.mqttPort);
|
||||||
Serial.printf("brightness: %d\n", data.brightness);
|
Serial.printf("brightness: %d\n", data.brightness);
|
||||||
Serial.printf("color: %d, %d, %d\n",
|
Serial.printf("color: %d, %d, %d\n",
|
||||||
data.color.at(0),
|
data.color.at(0),
|
||||||
|
103
src/main.cpp
103
src/main.cpp
@ -3,6 +3,8 @@
|
|||||||
#include <ArduinoOTA.h>
|
#include <ArduinoOTA.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
#include <EasyButton.h>
|
#include <EasyButton.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <Ticker.h>
|
||||||
|
|
||||||
// My own includes
|
// My own includes
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -25,6 +27,11 @@ Config& config = Config::Instance();
|
|||||||
|
|
||||||
MyLed myLed(ledPin, ledCount);
|
MyLed myLed(ledPin, ledCount);
|
||||||
|
|
||||||
|
WiFiClient wifiClient;
|
||||||
|
PubSubClient mqttClient(wifiClient);
|
||||||
|
|
||||||
|
Ticker tickerLedStatus, tickerMqttCyclic, tickerMqttReconnect;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float distance; // Distance in mm
|
float distance; // Distance in mm
|
||||||
float distanceNormalized;
|
float distanceNormalized;
|
||||||
@ -37,23 +44,34 @@ void checkSerial()
|
|||||||
if (!s.isEmpty())
|
if (!s.isEmpty())
|
||||||
{
|
{
|
||||||
Serial.println(s.c_str());
|
Serial.println(s.c_str());
|
||||||
DynamicJsonDocument doc(200);
|
DynamicJsonDocument doc(512);
|
||||||
deserializeJson(doc, s);
|
deserializeJson(doc, s);
|
||||||
JsonObject obj = doc.as<JsonObject>();
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
String hostname = obj["hostname"];
|
String hostname = obj["hostname"];
|
||||||
String ssid = obj["ssid"];
|
String ssid = obj["ssid"];
|
||||||
String pass = obj["pass"];
|
String wifiPass = obj["pass"];
|
||||||
|
String mqttServer = obj["mqttserver"];
|
||||||
|
String mqttUser = obj["mqttuser"];
|
||||||
|
String mqttPass = obj["mqttpass"];
|
||||||
|
int mqttPort = obj["mqttport"];
|
||||||
Serial.printf("Parsed hostname: %s, ssid: %s, pass: %s\n",
|
Serial.printf("Parsed hostname: %s, ssid: %s, pass: %s\n",
|
||||||
hostname.c_str(),
|
hostname.c_str(),
|
||||||
ssid.c_str(),
|
ssid.c_str(),
|
||||||
String(pass).c_str());
|
String(wifiPass).c_str());
|
||||||
|
|
||||||
if (hostname != "null" && ssid != "null" && pass != "null")
|
if (hostname != "null" && ssid != "null" && wifiPass != "null")
|
||||||
{
|
{
|
||||||
hostname.toCharArray(config.data.hostname,
|
hostname.toCharArray(config.data.hostname,
|
||||||
sizeof(config.data.hostname));
|
sizeof(config.data.hostname));
|
||||||
ssid.toCharArray(config.data.ssid, sizeof(config.data.ssid));
|
ssid.toCharArray(config.data.ssid, sizeof(config.data.ssid));
|
||||||
pass.toCharArray(config.data.pass, sizeof(config.data.pass));
|
wifiPass.toCharArray(config.data.pass, sizeof(config.data.pass));
|
||||||
|
mqttServer.toCharArray(config.data.mqttServer,
|
||||||
|
sizeof(config.data.mqttServer));
|
||||||
|
mqttUser.toCharArray(config.data.mqttUser,
|
||||||
|
sizeof(config.data.mqttUser));
|
||||||
|
mqttPass.toCharArray(config.data.mqttPass,
|
||||||
|
sizeof(config.data.mqttPass));
|
||||||
|
config.data.mqttPort = mqttPort;
|
||||||
|
|
||||||
config.write();
|
config.write();
|
||||||
}
|
}
|
||||||
@ -63,6 +81,7 @@ void checkSerial()
|
|||||||
void onPressed()
|
void onPressed()
|
||||||
{
|
{
|
||||||
digitalWrite(boardLedPin, LOW);
|
digitalWrite(boardLedPin, LOW);
|
||||||
|
Serial.println("Button pressed. Waiting 10s for config...");
|
||||||
|
|
||||||
unsigned long t = millis();
|
unsigned long t = millis();
|
||||||
static unsigned long lastT = t;
|
static unsigned long lastT = t;
|
||||||
@ -71,12 +90,60 @@ void onPressed()
|
|||||||
{
|
{
|
||||||
lastT = t;
|
lastT = t;
|
||||||
checkSerial();
|
checkSerial();
|
||||||
delay(1000);
|
delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
digitalWrite(boardLedPin, HIGH);
|
digitalWrite(boardLedPin, HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mqttPublishState()
|
||||||
|
{
|
||||||
|
const String outTopic =
|
||||||
|
"light/" + String(config.data.hostname) + "/relay/0";
|
||||||
|
if (mqttClient.connected())
|
||||||
|
mqttClient.publish(outTopic.c_str(), myLed.get().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqttReconnect()
|
||||||
|
{
|
||||||
|
if (!mqttClient.connected())
|
||||||
|
{
|
||||||
|
Serial.print("Attempting MQTT connection...");
|
||||||
|
if (mqttClient.connect(config.data.hostname,
|
||||||
|
config.data.mqttUser,
|
||||||
|
config.data.mqttPass))
|
||||||
|
{
|
||||||
|
Serial.println("connected");
|
||||||
|
mqttPublishState();
|
||||||
|
const String inTopic =
|
||||||
|
"light/" + String(config.data.hostname) + "/relay/0/set";
|
||||||
|
mqttClient.subscribe(inTopic.c_str());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Serial.print("failed, rc=");
|
||||||
|
Serial.print(mqttClient.state());
|
||||||
|
Serial.println(" try again in 5 seconds");
|
||||||
|
|
||||||
|
tickerMqttReconnect.once(5, mqttReconnect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mqttCallback(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();
|
||||||
|
|
||||||
|
myLed.set(String(reinterpret_cast<char const*>(payload)));
|
||||||
|
mqttPublishState();
|
||||||
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
pinMode(boardLedPin, OUTPUT);
|
pinMode(boardLedPin, OUTPUT);
|
||||||
@ -100,8 +167,21 @@ void setup()
|
|||||||
Serial.println();
|
Serial.println();
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
config.load();
|
||||||
|
|
||||||
|
mqttClient.setServer(config.data.mqttServer, config.data.mqttPort);
|
||||||
|
mqttClient.setCallback(mqttCallback);
|
||||||
|
|
||||||
ArduinoOTA.setHostname(config.data.hostname);
|
ArduinoOTA.setHostname(config.data.hostname);
|
||||||
ArduinoOTA.begin();
|
ArduinoOTA.begin();
|
||||||
|
|
||||||
|
tickerLedStatus.attach(120, mqttPublishState);
|
||||||
|
tickerMqttCyclic.attach(60 * 5, mqttReconnect);
|
||||||
|
mqttReconnect();
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
// myLed.set("{\"brightness\": 233, \"color\": { \"r\": 211, \"g\": 200, "
|
||||||
|
// "\"b\": 199 }, \"state\": \"OFF\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
float filter(float input)
|
float filter(float input)
|
||||||
@ -172,6 +252,7 @@ void evalDist(SensorData data)
|
|||||||
{
|
{
|
||||||
lastToggleTime = t;
|
lastToggleTime = t;
|
||||||
myLed.toggle();
|
myLed.toggle();
|
||||||
|
mqttPublishState();
|
||||||
stillCounter = 0;
|
stillCounter = 0;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -192,8 +273,11 @@ void evalDist(SensorData data)
|
|||||||
|
|
||||||
if (distDeltaAbs < releaseThreshold &&
|
if (distDeltaAbs < releaseThreshold &&
|
||||||
distDeltaAbs > stillThreshold * 2)
|
distDeltaAbs > stillThreshold * 2)
|
||||||
|
{
|
||||||
myLed.adjustBrightness((data.distance - lastDist) *
|
myLed.adjustBrightness((data.distance - lastDist) *
|
||||||
dimFactor);
|
dimFactor);
|
||||||
|
mqttPublishState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
@ -207,7 +291,7 @@ void evalDist(SensorData data)
|
|||||||
lastDist = data.distance;
|
lastDist = data.distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run()
|
void slowLoop()
|
||||||
{
|
{
|
||||||
SensorData data = getDist();
|
SensorData data = getDist();
|
||||||
|
|
||||||
@ -215,7 +299,7 @@ void run()
|
|||||||
|
|
||||||
myLed.run();
|
myLed.run();
|
||||||
|
|
||||||
button.read();
|
mqttClient.loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
@ -223,10 +307,11 @@ void loop()
|
|||||||
unsigned long t = millis();
|
unsigned long t = millis();
|
||||||
static unsigned long lastT = t;
|
static unsigned long lastT = t;
|
||||||
|
|
||||||
|
button.read();
|
||||||
if (t - lastT > 50)
|
if (t - lastT > 50)
|
||||||
{
|
{
|
||||||
lastT = t;
|
lastT = t;
|
||||||
run();
|
slowLoop();
|
||||||
}
|
}
|
||||||
ArduinoOTA.handle();
|
ArduinoOTA.handle();
|
||||||
}
|
}
|
@ -1,5 +1,7 @@
|
|||||||
#include "myled.h"
|
#include "myled.h"
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const float minBrightness = 0.2;
|
const float minBrightness = 0.2;
|
||||||
}
|
}
|
||||||
@ -25,9 +27,16 @@ void MyLed::initialize()
|
|||||||
|
|
||||||
void MyLed::setOnState(bool on)
|
void MyLed::setOnState(bool on)
|
||||||
{
|
{
|
||||||
_setBrightness(on ? _brightness : 0.0f);
|
|
||||||
_isOn = on;
|
_isOn = on;
|
||||||
Serial.println(on ? "Turning on" : "Turning off");
|
Serial.println(on ? "Turning on" : "Turning off");
|
||||||
|
if (on)
|
||||||
|
{
|
||||||
|
_apply();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
_strip.fill({});
|
||||||
|
_strip.show();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MyLed::getOnState()
|
bool MyLed::getOnState()
|
||||||
@ -41,6 +50,45 @@ void MyLed::toggle()
|
|||||||
setOnState(!_isOn);
|
setOnState(!_isOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyLed::set(String jsonState)
|
||||||
|
{
|
||||||
|
DynamicJsonDocument doc(1024);
|
||||||
|
deserializeJson(doc, jsonState);
|
||||||
|
JsonObject obj = doc.as<JsonObject>();
|
||||||
|
Serial.println(jsonState.c_str());
|
||||||
|
|
||||||
|
if (obj.containsKey("brightness"))
|
||||||
|
{
|
||||||
|
uint8_t brightness = obj["brightness"];
|
||||||
|
setBrightness(static_cast<float>(brightness) / 255.0f);
|
||||||
|
Serial.printf("Brightness: %d\n", brightness);
|
||||||
|
}
|
||||||
|
if (obj.containsKey("color"))
|
||||||
|
{
|
||||||
|
uint8_t r = obj["color"]["r"];
|
||||||
|
uint8_t g = obj["color"]["g"];
|
||||||
|
uint8_t b = obj["color"]["b"];
|
||||||
|
setColor(r, g, b);
|
||||||
|
Serial.printf("Color: r: %d, g: %d, b: %d\n", r, g, b);
|
||||||
|
}
|
||||||
|
if (obj.containsKey("state"))
|
||||||
|
{
|
||||||
|
bool state = obj["state"] == "ON";
|
||||||
|
if (state != getOnState())
|
||||||
|
setOnState(state);
|
||||||
|
Serial.printf("State: %s\n", state ? "ON" : "OFF");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String MyLed::get()
|
||||||
|
{
|
||||||
|
return "{\"brightness\": " + String(_brightness * 255.0f) +
|
||||||
|
", \"color\": { \"r\": " + String(_color.at(0)) +
|
||||||
|
", \"g\": " + String(_color.at(1)) +
|
||||||
|
", \"b\": " + String(_color.at(2)) + " }, \"state\": \"" +
|
||||||
|
String(_isOn ? "ON" : "OFF") + "\"}";
|
||||||
|
}
|
||||||
|
|
||||||
void MyLed::setBrightness(float brightness)
|
void MyLed::setBrightness(float brightness)
|
||||||
{
|
{
|
||||||
brightness = max(minBrightness, brightness);
|
brightness = max(minBrightness, brightness);
|
||||||
@ -50,8 +98,8 @@ void MyLed::setBrightness(float brightness)
|
|||||||
_targetBrightness = brightness;
|
_targetBrightness = brightness;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
_setBrightness(brightness);
|
|
||||||
_brightness = brightness;
|
_brightness = brightness;
|
||||||
|
_apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +109,12 @@ void MyLed::adjustBrightness(float diff)
|
|||||||
setBrightness(brightness);
|
setBrightness(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MyLed::setColor(uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
_color = {r, g, b};
|
||||||
|
_apply();
|
||||||
|
}
|
||||||
|
|
||||||
void MyLed::run()
|
void MyLed::run()
|
||||||
{
|
{
|
||||||
if (!_useEffects)
|
if (!_useEffects)
|
||||||
@ -70,15 +124,14 @@ void MyLed::run()
|
|||||||
const float mixFactor = 0.2;
|
const float mixFactor = 0.2;
|
||||||
_brightness =
|
_brightness =
|
||||||
_brightness * (1 - mixFactor) + _targetBrightness * mixFactor;
|
_brightness * (1 - mixFactor) + _targetBrightness * mixFactor;
|
||||||
_setBrightness(_brightness);
|
_apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyLed::_setBrightness(float brightness)
|
void MyLed::_apply()
|
||||||
{
|
{
|
||||||
_strip.fill(
|
_strip.fill(_strip.Color(static_cast<uint8_t>(_color.at(0) * _brightness),
|
||||||
_strip.Color(static_cast<uint8_t>(_defColor.at(0) * brightness),
|
static_cast<uint8_t>(_color.at(1) * _brightness),
|
||||||
static_cast<uint8_t>(_defColor.at(1) * brightness),
|
static_cast<uint8_t>(_color.at(2) * _brightness)));
|
||||||
static_cast<uint8_t>(_defColor.at(2) * brightness)));
|
|
||||||
_strip.show();
|
_strip.show();
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user