MQTT/OpenHab gw.request reply
-
How can I make OpenHab respod to gw.request(sensor, V_HEATER_SW,0);
I have a relay actuator sketch and in setup() I have this
for (int sensor=1 ; sensor<=NUMBER_OF_RELAYS;sensor++)
{
gw.present(sensor, S_HEATER);
gw.request(sensor, V_HEATER_SW,0);
}practically I would like OpenHab to respond to the gw.request with the last state of the relay that OpenHab knows.
My item definition is the follwing. I am able to ON and OFF the relay, but I need to find a way to get the values from OpenHab of the relays when the relay actuator arduino reboots.
Switch Incalzire_Releu_GF_Living2 "Incalzire Releu Living 2" <heating> (Incalzire) {mqtt=">[mysensor:MyMQTT/3/2/V_HEATER_SW:command:ON:1],>[mysensor:MyMQTT/3/2/V_HEATER_SW:command:OFF:0]"}
-
To work with actuators, suggests implementation of an RPC interface over the MQTT, such as here - mqtt-rpc for node.js. In other words - it requires special design of topic structure, as a very simple example, you could publish to MyMQTT/20/0/V_LIGHT/REQUEST and subscribe to MyMQTT/20/0/V_LIGHT/RESPONSE. Here your can read how it works in Gadgetkeeper - JSON-RPC over MQTT.
Even better would be to have implemented the protocol WAMP - WAMP Router diagram
But there is a trick for our situation - we can use V_VAR2 for requesting controller and V_VAR1 for getting response. For example:
MyMQTT/20/1/V_VAR1 - (incomingMessage)
MyMQTT/20/1/V_VAR2 - (send request).
Below you can see sketch of Natural Gas Meter from Majordomo forum (russian - you can use translate.google.com), which used this trick. This sketch derrived from MySensors Power Meter Pulse Sensor.But you have to adapt the settings of OpenHub like settings in Majordomo.
// Use this sensor to measure KWH and Watt of your house meeter // You need to set the correct pulsefactor of your meeter (blinks per KWH). // The sensor starts by fetching current KWH value from gateway. // Reports both KWH and Watt back to gateway. // // Unfortunately millis() won't increment when the Arduino is in // sleepmode. So we cannot make this sensor sleep if we also want // to calculate/report watt-number. #include <SPI.h> #include <MySensor.h> #define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your light sensor. (Only 2 and 3 generates interrupt!) #define PULSE_FACTOR 1000 // Nummber of blinks per KWH of your meeter #define SLEEP_MODE true // Watt-value can only be reported when sleep mode is false. #define MAX_WATT 10000 // Max watt value to report. This filetrs outliers. #define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway) // Id of the sensor child #define CHILD_ID_CNT 1 #define CHILD_ID_DEV 254 unsigned long SEND_FREQUENCY = 300000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway. MySensor gw; double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour boolean pcReceived = false; volatile unsigned long pulseCount = 0; volatile unsigned long lastBlink = 0; volatile unsigned long watt = 0; unsigned long oldPulseCount = 0; unsigned long oldWatt = 0; double oldKwh; double oldTemp = 0; float oldBatteryV = 0; unsigned long lastSend; MyMessage CntWattMsg(CHILD_ID_CNT, V_WATT); MyMessage CntKwhMsg(CHILD_ID_CNT, V_KWH); MyMessage pcMsg(CHILD_ID_CNT, V_VAR1); MyMessage pcReqMsg(CHILD_ID_CNT, V_VAR2); MyMessage BattMsg(CHILD_ID_DEV, V_VOLTAGE); long readVcc() { long result; // Read 1.1V reference against AVcc ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); delay(2); // Wait for Vref to settle ADCSRA |= _BV(ADSC); // Convert while (bit_is_set(ADCSRA,ADSC)); result = ADCL; result |= ADCH<<8; result = 1126400L / result; // Back-calculate AVcc in mV return result; } void SendDevInfo() { //========= Battery ============= float batteryV = readVcc() * 0.001; Serial.print("BatV:"); Serial.println(batteryV); gw.send(BattMsg.set(batteryV, 2)); } void setup() { wdt_disable(); // Disable the ADC by setting the ADEN bit (bit 7) to zero. ADCSRA = ADCSRA & B01111111; // Disable the analog comparator by setting the ACD bit // (bit 7) to one. ACSR = B10000000; gw.begin(incomingMessage); // Send the sketch version information to the gateway and Controller gw.sendSketchInfo("Natural GAS Meter", "1.0"); // Register this device as power sensor gw.present(CHILD_ID_CNT, S_POWER); gw.present(CHILD_ID_DEV, V_VOLTAGE); // Fetch last known pulse count value from gw //gw.request(CHILD_ID_CNT, V_VAR1); gw.send(pcReqMsg.set("REQ")); attachInterrupt(INTERRUPT, onPulse, RISING); wdt_enable(WDTO_8S); lastSend=millis(); } void loop() { wdt_reset(); // By calling process() you route messages in the background gw.process(); // Send feq // Only send values at a maximum frequency or woken up from sleep unsigned long now = millis(); bool sendTime = now - lastSend > SEND_FREQUENCY; //========= Cnt ============= if (pcReceived && (SLEEP_MODE || sendTime)) { SendDevInfo(); // New watt value has been calculated if (!SLEEP_MODE && watt != oldWatt) { // Check that we dont get unresonable large watt value. // could hapen when long wraps or false interrupt triggered if (watt<((unsigned long)MAX_WATT)) { gw.send(CntWattMsg.set(watt)); // Send watt value to gw } Serial.print("Watt:"); Serial.println(watt); oldWatt = watt; } // Pulse cout has changed if (pulseCount != oldPulseCount) { gw.send(pcMsg.set(pulseCount)); // Send pulse count value to gw double kwh = ((double)pulseCount/((double)PULSE_FACTOR)); oldPulseCount = pulseCount; if (kwh != oldKwh) { gw.send(CntKwhMsg.set(kwh, 4)); // Send kwh value to gw oldKwh = kwh; } } lastSend = now; } else if (sendTime && !pcReceived) { // No count received. Try requesting it again //gw.request(CHILD_ID_CNT, V_VAR1); gw.send(pcReqMsg.set("REQ")); lastSend=now; } //========= Sleep ================ if (SLEEP_MODE && pcReceived) { Serial.println("sleep"); gw.sleep(SEND_FREQUENCY); } } void incomingMessage(const MyMessage &message) { if (message.type==V_VAR1) { pulseCount = oldPulseCount = message.getLong(); Serial.print("Received last pulse count from gw:"); Serial.println(pulseCount); pcReceived = true; } } void onPulse() { if (!SLEEP_MODE) { unsigned long newBlink = micros(); unsigned long interval = newBlink-lastBlink; if (interval<10000L) { // Sometimes we get interrupt on RISING return; } watt = (3600000000.0 /interval) / ppwh; lastBlink = newBlink; } pulseCount++; }
-
Thanks for the suggestion. I found another solution.
I have the item like this:
Switch Incalzire_Releu_GF_Living1 "Incalzire Releu Living 1" <heating> (Incalzire) {mqtt=">[mysensor:MyMQTT/3/1/V_HEATER_SW:command:ON:1],>[mysensor:MyMQTT/3/1/V_HEATER_SW:command:OFF:0],<[mysensor:MyMQTT/3/1/V_HEATER_SW:command:MAP(1on0off.map)]"}
and in 1onoff.map i have:
**1=ON
0=OFF
1.4.1=REQUEST
**And then I have a rule:
rule "SW1 Persist"
when
Item Incalzire_Releu_GF_Living1 received command
then
if (receivedCommand == "REQUEST")
{
sendCommand(Incalzire_Releu_GF_Living1 , Incalzire_Releu_GF_Living1.state);
}
endIn the arduino code for the relay I have in the setu() this code.
**void setup()
{gw.begin(incomingMessage, AUTO, true);
gw.sendSketchInfo("Relay Control", "1.0");
for (int sensor=1 ; sensor<=NUMBER_OF_RELAYS;sensor++)
{
gw.present(sensor, S_HEATER);
gw.request(sensor, V_HEATER_SW,0);
}
}**So whenever gw.request is called the idem receives an update with 1.4.1 that is resolved by the rule to the actual state of the switch in openhab.
Like this, when arduino starts, the relays will be swiched ON as per the actual state of the switch.