Nemeus NIS-UL – Ultrasonic sensor

The following can be used to decode the data. I don’t own this device, so it has only been tested with the provided examples, and some more examples I created myself:

function Decoder(bytes, port) {
  
  // See http://wiki.nemeus.fr/index.php?title=NIS-UL_UltraSonic_Sensor#Nemeus_uplink_protocol
  // Test with:
  //   8B 02 0097 0098 1f 19: 151, 152cm; 31, 25°C
  //   8B 02 0097 0098 fe 03: 151, 152cm; -2, 3°C
  //   AF 03 0097 0098 0090 0fb9 0e91 0fff fe 03 00 0a: 151, 152, 144cm; 4025, 3729, 4095mV, -2, 3, 0°C
  //   82 00a0: 160cm
  //   88 1e: 30°C
  var usonic_dist = [], voltage = [], internal_temp = [];
  var i = 0, m = 0;

  var mask = bytes[i++];

  // If bit 0 set: 1 byte indicating the number of measurements, else 1
  var nb_meas = mask & 1<<0 ? bytes[i++] : 1;

  // If bit 1 set: distances in centimeters, each 2 bytes unsigned MSB, 1 to 300cm
  if (mask & 1<<1) {
    for (m = 0; m < nb_meas; m++) {
      usonic_dist.push(bytes[i++]<<8 | bytes[i++]);
    }
  }

  // If bit 2 set: voltages in millivolts, each 2 bytes unsigned MSB
  if (mask & 1<<2) {
    for (m = 0; m < nb_meas; m++) {
      voltage.push((bytes[i++]<<8 | bytes[i++]) / 1000);
    }
  }

  // If bit 3 set: internal temperatures, each 1 byte signed integer [-128..+127]
  if (mask & 1<<3) {
    for (m = 0; m < nb_meas; m++) {
      // Sign-extend to 32 bits to support negative values, by shifting 24 bits
      // (too far) to the left, followed by a sign-propagating right shift:
      internal_temp.push(bytes[i++]<<24>>24);
    }
  }

  // Bit 4: reserved

  // If bit 5 set: cause for the measurement, else "periodic measurement"
  var cause = mask & 1<<5 ? bytes[i++] : 1;

  // We should have consumed all bytes, if not then just return the payload as hex
  if (i !== bytes.length) {
    return {
      'error': 'failed to parse payload',
      'payload': bytes.map(function(b) {
         return ('0' + b.toString(16).toUpperCase()).substr(-2);
       }).join(' ')
    }
  }

  // continued below! 

…which in the very same Decoder can then be formatted any way you like:

  // Format the results
  var result = {};
  // Show all 8 bits, ensuring leading zeroes
  result.mask = '0b' + ('00000000' + mask.toString(2)).substr(-8);

  result.numberOfMeasurements = nb_meas;
  result.distances = usonic_dist;
  result.voltages = voltage;
  result.temperatures = internal_temp;

  // Same data, grouped by measurement
  result.measurements = [];
  for (m = 0; m < nb_meas; m++) {
    result.measurements.push({
      distance: usonic_dist[m],
      voltage: voltage[m],
      temperature: internal_temp[m]
    });
  }

  result.cause = cause;
  result.causeMask = '0b' + ('00000000' + cause.toString(2)).substr(-8);
  result.causes = [];
  if (cause & 1<<0) {
    result.causes.push('periodic');
  }
  if (cause & 1<<1) {
    result.causes.push('usonic_dist > high threshold');
  }
  if (cause & 1<<2) {
    result.causes.push('usonic_dist < high threshold - high hysteresis');
  }
  if (cause & 1<<3) {
    result.causes.push('usonic_dist < low threshold');
  }
  if (cause & 1<<4) {
    result.causes.push('usonic_dist > low threshold + low hysteresis');
  }
  if (cause & 1<<5) {
    result.causes.push('forced manually');
  }

  return result;
}

…which for AF 03 0097 0098 0090 0fb9 0e91 0fff fe 03 00 0a would return something like:

{
  "mask": "0b10101111",
  "numberOfMeasurements": 3,
  "distances": [
    151,
    152,
    144
  ],
  "voltages": [
    4.025,
    3.729,
    4.095
  ],
  "temperatures": [
    -2,
    3,
    0
  ],
  "measurements": [
    {
      "distance": 151,
      "voltage": 4.025,
      "temperature": -2
    },
    {
      "distance": 152,
      "voltage": 3.729,
      "temperature": 3
    },
    {
      "distance": 144,
      "voltage": 4.095,
      "temperature": 0
    }
  ],
  "cause": 10,
  "causeMask": "0b00001010",
  "causes": [
    "usonic_dist > high threshold",
    "usonic_dist < low threshold"
  ]
}

(Above, I changed the order for readability, to match the order of the bytes and the handling in the Decoder; you’ll get the keys sorted by name instead. And of course, much of the data in the result is redundant.)

If you know of a way to add this the Nemeus wiki, then please do! If you have true data, then please let us know as well, for proper testing.

3 Likes