[SOLVED] Problem writing floats to Modbus with a JSON writetransform

Just starting with OpenHab on a Raspberry 4 and I’m having an issue trying to write a floating point value to a ModBus RTU device.
I use a write transform as I need to set a number of registers at once to be able to change the parameters of the device.

So in my sitemap I have a setpoint:

Setpoint item=itm_vva_tstat_01_sp_heating label="Set point heating [%.1f °C]" minValue=10 maxValue=28 step=0.5

The thing:

Thing data vva_tstat_01_sp_heating "VVA TSTAT 01 - Set point heating" [readStart="196",   readValueType="float32_swap", writeTransform="JS(vva_ts_rpc_sp_heating.js)"]

The writetransform:

(function(inputData) {
  var obj             = new Object();
  obj.functionCode    = "16";
  obj.address         = "70";

  /* RPC_APP_ID, RPC_BSP_SAVE_CMD, PARAM_NUM              , RPC_MAGIC_NR, PARAM_VALUE             */
  /*          4,               14, PN_TS_SP_HEATING =  618,        43605, inputData               */
  obj.value           = [ 4, 14, 618, 43605, inputData];
  obj.maxTries        = "2";
  /* Convert the object to a JSON string */
  return "[ "  + JSON.stringify(obj) + " ]";
})(input)

However, if fractional values are present (e.g. setpoint 20.5°C) nothing is sent as the binding does runs into problem with the floating point in the JSON.

2020-02-23 20:10:11.720 [WARN ] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:vva_tstat_01:vva_tstat_01_rtinfo:vva_tstat_01_sp_heating 'VVA TSTAT 01 - Set point heating' could handle transformation result '[ {"functionCode":"16","address":"70","value":[4,14,618,43605,"16.5"],"maxTries":"2"} ]'. Original command 16.5. Error details follow

java.lang.NumberFormatException: For input string: "16.5"
snip...

I am an absolute JS beginner so I am a bit lost on how to solve this, ideally I would like to convert the setpoint to a 32 bit floating point and then copy the 2 16 bit words to the JSON (this also allows for a word swap that would be handy) but I have no idea how to do that. I tried with some bit manipulation but

If someone can point me to an example or similar I’d be thankfull.

Steven

I don’t think it’s an “ideally”, if your target device require floating point then you must convert your decimal input to an IEEE-754 binary form when you use the binding’s “direct write control” JSON feature.

I can’t say how to do that, but it’s “just javascript” and I’m sure you’d find examples on the web.
I’m pretty sure javascript natively uses 64-bit IEEE floats. Should be some simple binary masking method to extract a 32-bit version.

Alternatively,

What exactly? Might be another method.

Thanks for your help. I’ve managed to solve it (a minute ago) by using a DataView object.
If other people are struggling with a similar issue, maybe the code below can be of assistance.

(function(inputData) {
  var obj             = new Object();
  var bfr             = new ArrayBuffer(8);
  var view            = new DataView(bfr);
  var lb;
  var hb;
  
  view.setFloat32(0, parseFloat(inputData));
  lb = view.getInt16(0);
  hb = view.getInt16(2);

  obj.functionCode    = "16";
  obj.address         = "70";

  /* RPC_APP_ID, RPC_BSP_SAVE_CMD, PARAM_NUM              , RPC_MAGIC_NR, PARAM_VALUE             */
  /* TSTAT  = 4, BSPSC_TSTAT = 14, PN_TS_SP_HEATING =  618,        43605, inputData (f32_swap)    */
  obj.value           = [ 4, 14, 618, 43605, hb, lb];
  obj.maxTries        = "2";
  /* Convert the object to a JSON string */
  return "[ "  + JSON.stringify(obj) + " ]";
})(input)
2 Likes

That’s very neat :smiley:
I had a search and only came with messy methods.