Sending data to Espruinohub


#1

Hello everyone,

I am trying to modify the Ruuvitag+ so that I can get all the reading of the weather station plus the status of a door using a door contact connected to the Ruuvitag. The intention is to use one set of those per room to monitor the room environment and window status throughout the house. I want this data to be forwarded as MQTT-messages so I can use this information in OpenHAB.

As I see it now I would like to send the following information:

  • Temperature
  • Humidity
  • Pressure
  • Battery
  • Door/window status

I installed Espruino on the tag and I am able to read all the data except the door status (not looked into this yet) and print them to console.log. The following code works to some extent:

var Ruuvitag = require("Ruuvitag");
Ruuvitag.setEnvOn(true); 

setInterval(function(fn){

var temperature = [Math.round(Ruuvitag.getEnvData().temp)];
var pressure = [Math.round(Ruuvitag.getEnvData().pressure)];
var humid = [Math.round(Ruuvitag.getEnvData().humidity)];

console.log("Temperature: " + temperature);
console.log("Pressure: " + pressure);
console.log("Humidity: " + humid);
console.log("Battery [V]: " + NRF.getBattery());

  NRF.setAdvertising({
0x1809 : temperature,
0x2A6F : humid,
0x2A6D : pressure
  });
        },10000);

From Espruinohub on my Raspberry Pi 3 I can see that it receives data:

e4:12:a1:06:c9:01 - RuuviTag c9 (RSSI -84)
1809 => {“temp”:24}
2a6f => {“type”:“Buffer”,“data”:[55]}
2a6d => {“type”:“Buffer”,“data”:[2]}

For the temperature it creates a MQTT-message with the topic “/ble/temp/
e4:12:a1:06:c9:01” and the payload “24”. For the other data nothing is sent. So I know the transmission is working and the problem is with the advertising of the data.

Can anyone advise me on how to do the.setAdvertising correct for those data?

I tried to read on BLE UUID etc. and found some information but was not able to send the data in the correct format. One thing I tried was this (testing with the temperature since this was working with the above code):

NRF.setAdvertising({
0x1809 : { // Health Thermometer
0x2A6E: {value : [E.getTemperature().toFixed(2)]}
},
});
},10000);

But this produces already on the console the following warning:

Uncaught TypeError: If specifying an object, it must be of the form {data : ..., count : N} - got {
  "10862": [ 24 ]
 }
 at line 38 col 3
 });

Any help is appreciated.


#2

Hello,

The first code you had looks good, however:

  • Characteristic 0x2a6f has resolution of 0.01 digits and type of UINT16. you’ll probably want to send the data multiplied by 100, i.e. 52.67% => 5267
  • Same thing for 0x2a6d, resolution is 0.1 Pa and type is UINT32

However, I think that Espruinohub itself lacks the code for interpreting these 2 characteristics. We can find export for 0x1809, however search for 0x2a6e and 2a6f return no results.

I’m sure Espruinohub project would be grateful if you’d implement these missing features, the way temperature is implemented is probably a good starting point.

Thanks for pointing out the way data is advertised in Espruino / Espruinohub, I’ll consider adding that format to RuuviTag too.


#3

Thanks for the response. Unfortunately, I don’t think I will be able to implement any missing features. My hobbyist coding skills are just enough for some simple home automation scripts. I have not even figured out how git works :frowning:

I am not sure that the Characteristics 0x2a6f and 0x2a6d can be done the same way as the service 0x1809. If I understand the concept right, the characteristics belong to a service so that there should be a hierarchy service-characteristic. But maybe I got it all wrong.

in config.json of the Espruinohub is a possibility to define custom UUIDs. Maybe this is solution to my problem. I will try to find out more on this.


#4

I managed to get it to work. I needed to do two things:

  • Update the JS code of the Espruino hub to digest the additional UUIDs – I am certain that this is not standard. I simply copied the code for 0x1809 for the additionla paramters that I wanted to transmit.

  • The data is sent as an array of bytes. So larger values need to be split into bytes and the value to be sent as an array of corresponding bytes. There is a check in the Espruinohub, depending on the parameter, if a value consists of an array with length one or two and reassembles the value again. So it requires some change on the Ruuvitag code to transmit the data as an array if more than one byte is needed.

Now the data shows up in MQTT topics.


#5

Here are the Espruino code and the changes I made to Espruinohub to get the advertising work for me. I needed to disable some data as it seemed to be getting too long. I guess sending it separately will work but I have not tried it. Maybe this helps someone. It took me a long time to figure out…

Ruuvitag:

var Ruuvitag = require(“Ruuvitag”);
Ruuvitag.setEnvOn(true);

setInterval(function(fn){
var temperature = [Math.round(Ruuvitag.getEnvData().temp*100)/100];
var pressure = [Math.round(Ruuvitag.getEnvData().pressure*10)/10];
var humid = [Math.round(Ruuvitag.getEnvData().humidity*100)/100];
var battery = [Math.round(NRF.getBattery()*100)/100];
var door = 0;
console.log("Pressure fixed to '1234.56' for testing");
pressure = 1234.56;
console.log(Ruuvitag.getEnvData());
console.log("E.getTemperature: " + E.getTemperature());
console.log("Temperature: " + temperature);
console.log("Pressure: " + pressure);
console.log("Humidity: " + humid);
console.log("Battery [V]: " + battery);
console.log("Door status: " + door);
  
  NRF.setAdvertising({
0x1809 : [encodeFloat100(temperature)],
0x2A6D : [encodeFloat10(pressure)],

// 0xfffe : [battery],
// 0x2A6F : [humid],
// 0x2a3f : [door]
});
},10000);

function encodeFloat10(num) {
  var d = Math.round(num*10);
  return [ d&255, d >> 8 ];
}
function encodeFloat100(num) {
  var d = Math.round(num*100);
  return [ d&255, d >> 8 ];
}

Espruinohub (attributes.js): (I only made changes in the exports.names and exports.handlers)

exports.names = {
 "1809" : "Temperature",
 "180a" : "Device Information",
 "180f" : "Battery Percentage",
 "181c" : "User Data",
 "2a6f" : "Humidity",
 "2a6d" : "Pressure",
 "2aef" : "Door",
 "fffe" : "Battery Voltage",
 "feaa" : "Eddystone",
 "6e400001b5a3f393e0a9e50e24dcca9e" : "nus",
 "6e400002b5a3f393e0a9e50e24dcca9e" : "nus_tx",
 "6e400003b5a3f393e0a9e50e24dcca9e" : "nus_rx",
};
exports.handlers = {
 "2a3f" : function(a) { // Door/Window status
   return {
     door : a[0]
   }
  },
 "fffe" : function(a) { // battery
   return {
     batt : (a.length==2) ? (((a[1]<<8)+a[0])/100) : a[0]
   }
  },
 "2a6d" : function(a) { // Pressure
   return {
     pressure : (a.length==2) ? (((a[1]<<8)+a[0])/10) : a[0]
   }
  },
 "2a6f" : function(a) { // Humidity
   return {
     humid : (a.length==2) ? (((a[1]<<8)+a[0])/100) : a[0]
   }
  },
 "1809" : function(a) { // Temperature
   return { 
     temp : (a.length==2) ? (((a[1]<<8)+a[0])/100) : a[0]
   }
  },
 "180f" : function(a) { // Battery percent
   return { 
     battery : a[0] 
   }
  },
 "feaa" : function(d) { // Eddystone
   if (d[0]==0x10) { // URL
     var rssi = d[1];
     if (rssi&128) rssi-=256; // signed number
     var urlType = d[2];
     var URL_TYPES = [
        "http://www.",
        "https://www.",
        "http://",
        "https://"];
     var url = URL_TYPES[urlType] || "";
     for (var i=3;i<d.length;i++)
       url += String.fromCharCode(d[i]);
     return { url : url, "rssi@1m":rssi };
   }
  }
};

#6

One thing that I forgot: all values transmitted are integers. So for those values with decimals you need to multiply it with 10 or 100 hundred first and then divide it on the Espruinohub by the same amount.

And I just noticed a problem: apparently the code has a problem with negative values. I just set the temperature to be minus and it shows completely wrong. The temperture is the parameter that I did not change but used as a template. Since the other values will not be negative it is not a problem for them but I need to look into the temperature more.

42


#7

I finally got around to continue on my project.

I managed to solder a reed relay to monitor a window to pin 18. For the window monitoring I use the setWatch()-function as recommended on the Espruino pages. Temperature, air pressure and humidity I am currently sending once per minute and the battery temperature as well as the window state (in addition to the setWatch(), just to make sure the status gets updated in case a status change message gets lost). This is the code I use on the Ruuvitag:

var Ruuvitag = require("Ruuvitag");
Ruuvitag.setEnvOn(true); 
pinMode(18, 'input_pulldown');

    setWatch(function(e) {
      console.log("Window closed");
      var door = 1;
      NRF.setAdvertising({
        0x2a3f : [door]
          });
    }, 18, { repeat: true,debounce:50, edge: 'rising' });

    setWatch(function(e) {
      console.log("Window open");
      var door = 0;
      NRF.setAdvertising({
        0x2a3f : [door]
          });
    }, 18, { repeat: true,debounce:50, edge: 'falling' });

    setInterval(function(fn){

    var temperature = [Math.round(Ruuvitag.getEnvData().temp*100)/100];
    var pressure = [Math.round(Ruuvitag.getEnvData().pressure*10)/10];
    var humid = [Math.round(Ruuvitag.getEnvData().humidity*10)/10];
      NRF.setAdvertising({
    0x1809 : [encodeFloat100(temperature)],
    0x2A6D : [encodeFloat10(pressure)],
    0x2A6F : [encodeFloat10(humid)],
      });
            },60000);

    setInterval(function(fn){
    var battery = [Math.round(NRF.getBattery()*100)/100];
    var door = digitalRead(18);
     NRF.setAdvertising({
    0xfffe : [encodeFloat100(battery)],
    0x2a3f : [door]
      });
            },600000);

    function encodeFloat10(num) {
      var d = Math.round(num*10);
      return [ d&255, d >> 8 ];
    }

    function encodeFloat100(num) {
      var d = Math.round(num*100);
      return [ d&255, d >> 8 ];
}

The code on EspruinoHub that I posted above works. The problem with my number is that it is signed. So I changed the part for the temperature a little. Here is how the changed part in attribute.js looks now:

exports.names = {
 "1809" : "Temperature",
 "180a" : "Device Information",
 "180f" : "Battery Percentage",
 "181c" : "User Data",
 "2a6f" : "Humidity",
 "2a6d" : "Pressure",
 "2aef" : "Door",
 "fffe" : "Battery Voltage",
 "feaa" : "Eddystone",
 "6e400001b5a3f393e0a9e50e24dcca9e" : "nus",
 "6e400002b5a3f393e0a9e50e24dcca9e" : "nus_tx",
 "6e400003b5a3f393e0a9e50e24dcca9e" : "nus_rx",
};

exports.handlers = {
 "2a3f" : function(a) { // Door/Window status
   return {
     door : a[0]
   }
  },

 "fffe" : function(a) { // battery
   return {
     batt : (a.length==2) ? (((a[1]<<8)+a[0])/100) : a[0]/100
   }
  },
 "2a6d" : function(a) { // Pressure
   return {
     pressure : (a.length==2) ? (((a[1]<<8)+a[0])/10) : a[0]/10
   }
  },

 "2a6f" : function(a) { // Humidity
   return {
     humid : (a.length==2) ? (((a[1]<<8)+a[0])/10) : a[0]/10
   }
  },
 "1809" : function(a) { // Temperature
   var temp;
   if (a.length==2) {
      temp = (((a[1]<<8)+a[0])/100);
      if (temp > 327.67){
        temp = (Math.round((655.35-temp)*100)/100);
      }
   }else{
      temp = a[0]/10;
      if (temp > 127) {
        temp = -(255 - temp);
      }
         }
  return { 
     //temp : (a.length==2) ? (((a[1]<<8)+a[0])/100) : a[0]
     temp : temp
   }
  },
 "180f" : function(a) { // Battery percent
   return { 
     battery : a[0] 
   }
  },
 "feaa" : function(d) { // Eddystone
   if (d[0]==0x10) { // URL
     var rssi = d[1];
     if (rssi&128) rssi-=256; // signed number
     var urlType = d[2];
     var URL_TYPES = [
        "http://www.",
        "https://www.",
        "http://",
        "https://"];
     var url = URL_TYPES[urlType] || "";
     for (var i=3;i<d.length;i++)
       url += String.fromCharCode(d[i]);
     return { url : url, "rssi@1m":rssi };
   }
  }
};

I just realized that the problem with signed number is the same as for the rssi - there it is solved much more elegantly.

I hope this helps someone. It took me a long time to figure it all out. I am not a developer so the code is a raw hack and everything is still running only as a prototype. But it seems to do what I want.

I am reading the data via mqtt into OpenHAB. There I am using InfluxDB and Grafana to visualize the data but also assign it to variables to trigger events. For example, I have a ventilator in the bathroom that gets started when the humidity is getting too high and also the temperature is controlled via OpenHAB (I have electric heating so this is very easy to do :slight_smile: )

Thanks to everyone that shared information, in particular to mrwhale (https://f.ruuvi.com/t/ruuvitag-basic-espruino-and-openhab/335) and the Ruuvi-team who got me looking into the right direction.

42