Mqtt with HA configuration

This commit is contained in:
Philip Johansson 2020-06-09 13:35:30 +02:00
parent 156d92b7f8
commit fc3416f12c
8 changed files with 220 additions and 24 deletions

3
.gitmodules vendored
View File

@ -10,3 +10,6 @@
[submodule "lib/EasyButton"]
path = lib/EasyButton
url = https://github.com/evert-arias/EasyButton.git
[submodule "lib/pubsubclient"]
path = lib/pubsubclient
url = https://github.com/knolleary/pubsubclient.git

View File

@ -10,9 +10,39 @@ Before sending press the on board button "flash" or connect a button to D0. You
{
"hostname": "your_hostname",
"ssid": "your_ssid",
"pass": "[48..."
"wifipass": "[48...",
"mqttserver": "mqtt.mydomain.com",
"mqttuser": "mymqtt_user",
"mqttpass": "7j%!fs#",
"mqttport": "1883",
}
```
## 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
```

View File

@ -9,6 +9,10 @@ public:
char hostname[20];
char ssid[20];
char pass[20];
char mqttServer[20];
char mqttUser[10];
char mqttPass[20];
int mqttPort;
uint8_t brightness;
std::array<uint8_t, 3> color;
} Data;

View File

@ -1,3 +1,5 @@
#pragma once
#include <Adafruit_NeoPixel.h>
class MyLed {
@ -12,18 +14,32 @@ public:
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]
void setBrightness(float brightness);
//! @param diff in [-1, 1]
void adjustBrightness(float diff);
void setColor(uint8_t r, uint8_t g, uint8_t b);
void run();
private:
//! Called by public setBrightness. This function
//! will not store the brightness
void _setBrightness(float brightness);
void _apply();
bool _useEffects;
@ -35,5 +51,5 @@ private:
const std::array<float, 3> _defColor;
const std::array<float, 3> _color;
std::array<float, 3> _color;
};

1
lib/pubsubclient Submodule

@ -0,0 +1 @@
Subproject commit 2d228f2f862a95846c65a8518c79f48dfc8f188c

View File

@ -9,7 +9,11 @@ void printConfig(Config::Data data)
{
Serial.printf("hostname: %s\n", data.hostname);
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("color: %d, %d, %d\n",
data.color.at(0),

View File

@ -3,6 +3,8 @@
#include <ArduinoOTA.h>
#include <ESP8266WiFi.h>
#include <EasyButton.h>
#include <PubSubClient.h>
#include <Ticker.h>
// My own includes
#include "config.h"
@ -25,6 +27,11 @@ Config& config = Config::Instance();
MyLed myLed(ledPin, ledCount);
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
Ticker tickerLedStatus, tickerMqttCyclic, tickerMqttReconnect;
typedef struct {
float distance; // Distance in mm
float distanceNormalized;
@ -37,23 +44,34 @@ void checkSerial()
if (!s.isEmpty())
{
Serial.println(s.c_str());
DynamicJsonDocument doc(200);
DynamicJsonDocument doc(512);
deserializeJson(doc, s);
JsonObject obj = doc.as<JsonObject>();
String hostname = obj["hostname"];
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",
hostname.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,
sizeof(config.data.hostname));
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();
}
@ -63,6 +81,7 @@ void checkSerial()
void onPressed()
{
digitalWrite(boardLedPin, LOW);
Serial.println("Button pressed. Waiting 10s for config...");
unsigned long t = millis();
static unsigned long lastT = t;
@ -71,12 +90,60 @@ void onPressed()
{
lastT = t;
checkSerial();
delay(1000);
delay(500);
}
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()
{
pinMode(boardLedPin, OUTPUT);
@ -100,8 +167,21 @@ void setup()
Serial.println();
Serial.println(WiFi.localIP());
config.load();
mqttClient.setServer(config.data.mqttServer, config.data.mqttPort);
mqttClient.setCallback(mqttCallback);
ArduinoOTA.setHostname(config.data.hostname);
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)
@ -172,6 +252,7 @@ void evalDist(SensorData data)
{
lastToggleTime = t;
myLed.toggle();
mqttPublishState();
stillCounter = 0;
}
} else
@ -192,8 +273,11 @@ void evalDist(SensorData data)
if (distDeltaAbs < releaseThreshold &&
distDeltaAbs > stillThreshold * 2)
{
myLed.adjustBrightness((data.distance - lastDist) *
dimFactor);
mqttPublishState();
}
}
} else
{
@ -207,7 +291,7 @@ void evalDist(SensorData data)
lastDist = data.distance;
}
void run()
void slowLoop()
{
SensorData data = getDist();
@ -215,7 +299,7 @@ void run()
myLed.run();
button.read();
mqttClient.loop();
}
void loop()
@ -223,10 +307,11 @@ void loop()
unsigned long t = millis();
static unsigned long lastT = t;
button.read();
if (t - lastT > 50)
{
lastT = t;
run();
slowLoop();
}
ArduinoOTA.handle();
}

View File

@ -1,5 +1,7 @@
#include "myled.h"
#include <ArduinoJson.h>
namespace {
const float minBrightness = 0.2;
}
@ -25,9 +27,16 @@ void MyLed::initialize()
void MyLed::setOnState(bool on)
{
_setBrightness(on ? _brightness : 0.0f);
_isOn = on;
Serial.println(on ? "Turning on" : "Turning off");
if (on)
{
_apply();
} else
{
_strip.fill({});
_strip.show();
};
}
bool MyLed::getOnState()
@ -41,6 +50,45 @@ void MyLed::toggle()
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)
{
brightness = max(minBrightness, brightness);
@ -50,8 +98,8 @@ void MyLed::setBrightness(float brightness)
_targetBrightness = brightness;
} else
{
_setBrightness(brightness);
_brightness = brightness;
_apply();
}
}
@ -61,6 +109,12 @@ void MyLed::adjustBrightness(float diff)
setBrightness(brightness);
}
void MyLed::setColor(uint8_t r, uint8_t g, uint8_t b)
{
_color = {r, g, b};
_apply();
}
void MyLed::run()
{
if (!_useEffects)
@ -70,15 +124,14 @@ void MyLed::run()
const float mixFactor = 0.2;
_brightness =
_brightness * (1 - mixFactor) + _targetBrightness * mixFactor;
_setBrightness(_brightness);
_apply();
}
}
void MyLed::_setBrightness(float brightness)
void MyLed::_apply()
{
_strip.fill(
_strip.Color(static_cast<uint8_t>(_defColor.at(0) * brightness),
static_cast<uint8_t>(_defColor.at(1) * brightness),
static_cast<uint8_t>(_defColor.at(2) * brightness)));
_strip.fill(_strip.Color(static_cast<uint8_t>(_color.at(0) * _brightness),
static_cast<uint8_t>(_color.at(1) * _brightness),
static_cast<uint8_t>(_color.at(2) * _brightness)));
_strip.show();
}