Homie 2.0 with OpenHAB on ESP8266 using MQTT
Intergrating a Homie 2.0 in OpenHAB
ESP8266 is a great device whenever you need a small and cheap module to be connected to the WiFi network. It is cheaper than an arduino with an additional LAN or WLAN module attached to it.
My first attempt for a lightshow was using an Arduino nano + nRF24. The second approach was based on an ESP8266. Nevertheless implementing a fail safe WiFI connection is sort of a pain.
For the time being, I will move on using the homie framework, as it does handle all the background work and provide an MQTT interface to communicate with OpenHAB.
It took quite some time to get homie 2.0 up and running, as the framework won’t work when using Eclipse (Sloeber from bayens). It works when using platformIO which also does allow to upload the configuration in config.json to SPIFFS.
This project is in github as well: https://github.com/ThomasH-W/ESP8266-Homie-2.0
ESP8266
The module is providing two functions: one port will switch a door lock when receiving a message via MQTT or when the button is pressed. In addition the module will periodically send the temperature to the MQTT server.
homie2_main.cpp
[codesyntax lang=”cpp”]
/*
* The Relay could be toggeled with the physical pushbutton
*/
#include <Homie.h> // this is based on homie 2.0
const int PIN_RELAY = 12;
const int PIN_LED = 13;
const int PIN_BUTTON = 0;
unsigned long buttonDownTime = 0;
byte lastButtonState = 1;
byte buttonPressHandled = 0;
const int TEMPERATURE_INTERVAL = 20; // 300;
unsigned long lastTemperatureSent = 0;
int tempOffset = 0;
HomieNode switchNode (“switchDoor” , “switch”); // ID, type
HomieNode temperatureNode (“temperature”, “temperature”);
// HomieNode temperatureNode(“basementtemperature”, “temperature”);
bool switchOnHandler(HomieRange range, String value) {
if (value != “true” && value != “false”) return false;
bool on = (value == “true”);
digitalWrite(PIN_RELAY, on ? HIGH : LOW);
digitalWrite(BUILTIN_LED, on ? LOW : HIGH);
switchNode.setProperty(“on”).send(value);
Homie.getLogger() << “Switch is ” << (on ? “on” : “off”) << “(MQTT)” << endl;
return true;
}
void toggleRelay() {
bool on = digitalRead(PIN_RELAY) == HIGH;
digitalWrite(PIN_RELAY, on ? LOW : HIGH);
digitalWrite(BUILTIN_LED, on ? LOW : HIGH);
switchNode.setProperty(“on”).send(on ? “false” : “true”);
Homie.getLogger() << “Switch is ” << (on ? “off” : “on”) <<“(Button)” << endl;
}
void loopHandler() {
byte buttonState = digitalRead(PIN_BUTTON);
if ( buttonState != lastButtonState ) {
if (buttonState == LOW) {
buttonDownTime = millis();
buttonPressHandled = 0;
}
else {
unsigned long dt = millis() – buttonDownTime;
if ( dt >= 90 && dt <= 900 && buttonPressHandled == 0 ) {
toggleRelay();
buttonPressHandled = 1;
}
}
lastButtonState = buttonState;
}
if (millis() – lastTemperatureSent >= TEMPERATURE_INTERVAL * 1000UL || lastTemperatureSent == 0) {
float temperature = -5 + tempOffset; // Fake temperature here, for the example
if(tempOffset++>10) tempOffset=0;
// Homie.getLogger() << “Temperature: ” << temperature << ” C” << endl; //°
Homie.getLogger() << “Temperature: 33 C from loopHandler” << endl; //°
temperatureNode.setProperty(“degrees”).send(String(temperature));
lastTemperatureSent = millis();
}
}
void setupHandler() {
temperatureNode.setProperty(“unit”).send(“c”);
}
void setup() {
Serial.begin(115200);
Serial << endl << endl;
pinMode(PIN_RELAY, OUTPUT);
pinMode(PIN_BUTTON, INPUT);
pinMode(BUILTIN_LED, OUTPUT);
digitalWrite(PIN_RELAY, LOW);
Homie_setFirmware(“garage-doorlock”, “1.0.1”);
Homie.setLedPin(PIN_LED, LOW).setResetTrigger(PIN_BUTTON, LOW, 5000);
// Homie.setSetupFunction(setupHandlerTemp).setLoopFunction(loopHandlerTemp);
switchNode.advertise(“on”).settable(switchOnHandler);
temperatureNode.advertise(“unit”);
temperatureNode.advertise(“degrees”);
Homie.setSetupFunction(setupHandler);
Homie.setLoopFunction(loopHandler);
Homie.setup();
}
void loop() {
Homie.loop();
}
[/codesyntax]
homie/config.json
[codesyntax lang=”bash”]
{
“name”: “Doorlock of the garage”,
“device_id”: “Door_Garage_ESP1”,
“wifi”: {
“ssid” : “mywifinetwork”,
“password”: “dasgehtdichnichtsan”
},
“mqtt”: {
“host” : “192.168.1.2”,
“port”: 1883,
“base_topic”: “garage/”,
“auth”: false
},
“ota”: {
“enabled”: true
}
}
[/codesyntax]
OpenHAB
homie.items
This was the hardest piece as some requirements are not that obvious. I was able to see all the messages on the MQTT server via MQTT fx but nothing happened in openHAB. The key to success was to use the Log Viewer. You will see the error messages there and get a glimpse where to start hunting down the bugs.
The status of the switch “homie_switch” is managed by a rule rather than reading the inbound message from MQTT. This was the only way the switch would change the sate when the button is pressed on the device.
[codesyntax lang=”bash”]
// homie.items
/************************************************** Groups ********************************************/
Group ghomieGarage “Homie Garage”
Group gHomie1Setup “Homie 1 Setup”
/************************************************** Items ********************************************/
// no inbound message applied so we can set the state of the switch from outside via MQTT as defined in homie.rules
Switch homie_switch “MQTT Switch 5 (outbound only)” (gHomie1Setup) {
mqtt=”>[mosquitto:garage/Door_Garage_ESP1/switchDoor/on/set:state:ON:true],
>[mosquitto:garage/Door_Garage_ESP1/switchDoor/on/set:state:OFF:false]”
}
// listen to status conveyed via MQTT
String mqttMessage “MQTT switch status: [%s]” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/switchDoor/on:state:MAP(trueonfalseoff.map)” }
// homie2_main.cpp: setup() Homie_setFirmware(“garage-doorlock”, “1.0.1”);
String homie_name “Name: [%s]” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/$fw/name:state:REGEX((.*))]” }
String homie_fwversion “FW version: [%s]” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/$fw/version:state:REGEX((.*))]” }
// homie/config.json
String homie_fwname “FW name: [%s]” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/$name:state:REGEX((.*))]” }
Number homie_signal “Signal [%1.0f]%” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/$stats/signal:state:REGEX((.*))]” }
Number homie_uptime “uptime [%1.0f]s” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/$stats/uptime:state:REGEX((.*))]” }
String homie_online “online: [%s]” (gHomie1Setup) { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/$online:state:default]” }
String homie_tempS “Temperature string [%s]” <temperature> { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/temperature/degrees:state:default]”}
Number homie_temp “Temperature number [%.1f °C]” <temperature> { mqtt=”<[mosquitto:garage/Door_Garage_ESP1/temperature/degrees:state:REGEX((.*))]”}
[/codesyntax]
myHome.sitemap
This does work, but not as expected. My preferences would be to use sub-frames. The only way to display a sub-page was using a group. Unfortunately you have no control of the order the items are shown within the group.
[codesyntax lang=”bash”]
// myHome.sitemap
sitemap myHome label=”Welcome to my Home” {
Frame label=”Homie Garage” {
Switch item=homie_switch
// Text item=mqttMessage
// Text item=homie_tempS
Text item=homie_temp
Text item=homie_uptime
Group item=gHomie1Setup label=”Device Info” icon=”settings”
} // end of Frame
} // end of sitemap
[/codesyntax]
homie.rules
This works, but I failed using “case” or “if” to comine both cases within one rule.
[codesyntax lang=”bash”]
// homie.rules
rule MqttMessageChanged_On
when
Item mqttMessage changed to ON
then
postUpdate(homie_switch,ON)
// pushover(“MQTT rule> set homie_switch to ON “)
end
rule MqttMessageChanged_Off
when
Item mqttMessage changed to OFF
then
postUpdate(homie_switch,OFF)
// pushover(“MQTT rule> set homie_switch to OFF “)
end
[/codesyntax]
trueonfalseoff.map
[codesyntax lang=”bash”]
// homie.rules
false=OFF
OFF=false
true=ON
ON=true
[/codesyntax]