Homie 2.0 with OpenHAB on ESP8266 using MQTT

11. Juli 2017 at 14:31
Print Friendly, PDF & Email

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

/*
* 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();
}

 

homie/config.json

{
"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
}
}

 

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.

// 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((.*))]"}

 

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.

// 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

 

homie.rules

This works, but I failed using „case“ or „if“ to comine both cases within one rule.

// 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

 

trueonfalseoff.map

// homie.rules
false=OFF
OFF=false
true=ON
ON=true