Decoding Smart Waste Bin Detector payload

This stuff is still a mystery to me - another decoder looks fairly straight forward once I see a working decoder, but the terminology used in this description is beyond my limited knowledge.

Here is what is in the documentation - it’s an ultrasonic bin sensor, CNDingtek Smart Waste Bin Detector DF702 LoRaWAN:

4 Bytes in hex: 0 0 0 0 1 0 3 4 (TTN Payload looks like this)
Ascii: 00 00 10 34
Hex: 30 30 30 30 31 30 33 34
Byte1 4bit (bit 7-4): full: 1, not full: 0
Byte1 4bit (bit 3-0): fire: 1, not fire: 0
Byte2 4bit (bit 7-4): fall: 1, not fall: 0
Byte2 4bit (bit 3-0): battery: 1 not: 0
Byte3 and 4: the air level(from sensor to garbage), unit: mm, 1034 means 1034mm; range 0-2000mm;

Example:
Application server get data in payload_hex section 10000230, 4 bytes, in hex 0x
31 30 30 30 30 32 33 30.
That mean it is full, no flame, no fall, air height is 230mm.
Notes: Some application server parsing the payload to the original 4 Bytes.
Please use the half byte as one status to parse accordingly.

Again any help would be appreciated

thanks in advance
Paul

Hi @Paul_Stewart, the data makes sense and can understand how its derived from the sensor.

How do you want this split up by the Decoder, ie how do you want this sent?

For example do you want the Decoder to create some variables something like
BinFull (Boolean) 0 or 1
Fire (Boolean) 0 or 1
Fall (Boolean) 0 or 1
Battery (is this a variable for battery voltage or something similar?)
AirLevel (Integer) from 0-2000

If you can define the variable we can then explain how to create the Decoder

1 Like

To help future readers find the answer: what sensor is that, and can you share the manual? Also, someone recently asked for Rubbish/Trash Can monitoring, so please share there?

And where did you find the hexadecimal encoded version? What you’re calling ASCII is not ASCII. The 4 bytes 00 00 10 34 are already shown in a hexadecimal representation. These are all the same:

  • base 1, binary: 00000000 00000000 00010000 00110100
  • base 8, octal: 0 0 22 64
  • base 10, decimal: 0 0 16 52
  • base 16, hexadecimal: 00 00 10 34
  • Base64: AAAQNA==

Indeed, 30 30 30 30 31 30 33 34 is the hexadecimal representation of the ASCII characters 0, 0, 0, 0, 1, 0, 3 and 4, but it makes no sense to do that.

See Wikipedia’s Computer number format and TTN’s Working with Bytes.

Also, the manual might explain why 4 bits are used for (apparently) boolean (on/off) values, where a single bit would suffice.

Can you use that device to get some actual readings?

To me, it would be really unexpected that a hexadecimal value of 0x0230 would decode to a decimal result of 230 instead of 560 (MSB). That could mean they used something that’s not very storage efficient (though with a range of 0…2000 regular bit shifting needs 2 bytes too), like:

data[3] = height / 100;
data[4] = height % 100;

Or it could mean that they explicitly did this to make the hexadecimal representation easier to read by humans (and a tiny bit harder to process by computers). But then: using 4 bits for a single on/off value might indicate that such is exactly what the manufacturer is trying to do. Weird; unless they explicitly wanted to achieve this, I wouldn’t expect much from, say, low power behaviour then…

Done :+1:

Not sure if I am allowed, the Protocol Manual was obtained under an NDA - but I will check if it is OK.

That was a small section interpreted from the manual - I dont understand any of it sorry.

Ill see if I can get some from the farmer with corresponding actual measurements to double check.

cheers
Paul

We are having troubles with the battery that was shipped with it - so he might have to wait till a new one turns up - we can power it off the TTL cable serial cable but they may mess up the readings, not sure

thanks again for your help so far

cheers
Paul

ok he managed to get some payloads.

at a distance of approximately 30cm from the floor
00 01 03 03

at 10cm (which by specs is below the minimum read height which is 15cm)
10 01 01 40

and pointing at the ceiling height which is about 3 metres (again outside specified range of 0-200cm)
10 01 00 00

No fires I assume :slight_smile:

cheers
Paul

These look like what we would need I reckon - battery is again just a boolean - its an alarm to say the level has dropped below 20%

cheers
Paul

Hi Paul,

See if this works for you. There are a number of assumptions here. @arjanvanb i’m a novice with JS so any feedback would be appreciated.

As pointed out earlier, its odd the 4 binary variables (Binfull, Fire, Fall and BatteryLow) are not sent with one bit each but using 4 bits each (half a byte each). So I have assumed 0000 is False and any of the possible non zero values (0001 or 0010 or 0011 or … up to 1111) would mean true.

We could put a Boolean function in the decoder (see below) but since i don’t know how your processing the data downstream i haven’t added this in the code. If its needed then an example would be something like var BinFull = Boolean( (bytes[0] >> 4) != 0 )

In the code (below) I have assumed AirLevel is packed efficiently into the two bytes but looking at your decode it may need something like
AirLevel = ((bytes[2] >> 4) * 1000) + ((bytes[2] & 00001111) * 100) + ((bytes[3] >> 4) * 10) + ((bytes[3] & 00001111))
With this one, give it a try and see what you get. We can help you adjust the code from there.

function Decoder(bytes, port) {
  var BinFull = (bytes[0] >> 4);
  var Fire = (bytes[0] & 00001111);
  var Fall = (bytes[1] >>4);
  var BatteryLow = (bytes[1] & 00001111);
  var AirLevel = (bytes[2] << 8) | bytes[3])  ;
  return {
    BinFull:BinFull,
    Fire:Fire,
    Fall:Fall,
    BatteryLow:BatteryLow,
    AirLevel:AirLevel,
  }
}
3 Likes

It seems the device uses an old standard known as “packed binary-coded decimal” (BCD), which encodes each digit into its own nibble (4 bits). So, Tony is right about this:

Fixed for the octal problem (see further below), that would then read:

AirLevel = (bytes[2] >> 4) * 1000 
  + (bytes[2] & 0x0F) * 100 
  + (bytes[3] >> 4) * 10
  + (bytes[3] & 0x0F)

Just as another fun fact, one could abuse JavaScript’s type coercion and use intermediate string values:

// Convert to hexadecimal strings without any "0x" prefix, hence
// treated as decimal values when coercing back to a number (due
// to the multiplication and the explicit unary plus operator).
AirLevel = bytes[2].toString(16) * 100 + +bytes[3].toString(16)

Yes, that needs two plus-characters for that second part, one being addition and the second being the unary plus. (One could also multiply the second part by 1, to get a number.)

So:

function Decoder(bytes, port) {
  return {
    // Decode groups of 4 bits into single booleans
    BinFull: (bytes[0] & 0xF0) > 0,
    Fire: (bytes[0] & 0x0F) > 0,
    Fall: (bytes[1] & 0xF0) > 0,
    BatteryLow: (bytes[1] & 0x0F) > 0,

    // BCD: interpret hexadecimal 0x0230 as decimal 230, not decimal 560
    AirLevel: (bytes[2] >> 4) * 1000 
      + (bytes[2] & 0x0F) * 100 
      + (bytes[3] >> 4) * 10 
      + (bytes[3] & 0x0F)
  };
}

Which seems to pass all tests for the data we know.

Click to see the decoded examples
  • …giving for 00 00 10 34:

    {
      "AirLevel": 1034,
      "BatteryLow": false,
      "BinFull": false,
      "Fall": false,
      "Fire": false
    }
    
  • …yields for 10 00 02 30:

    {
      "AirLevel": 230,
      "BatteryLow": false,
      "BinFull": true,
      "Fall": false,
      "Fire": false
    }
    
  • …would yield:

    {
      "AirLevel": 303,
      "BatteryLow": true,
      "BinFull": false,
      "Fall": false,
      "Fire": false
    }
    
  • …gives:

    {
      "AirLevel": 140,
      "BatteryLow": true,
      "BinFull": true,
      "Fall": false,
      "Fire": false
    }
    

Some more asides:

So, it’s not encoded the way I figured above, as this would still show a value of 230 as hexadecimal 0x021E, not as 0x0230 like documented.

Welcome to the amazing world of octal numbers! :partying_face:

Above, 00001111 does not denote a binary number, but due to it starting with a zero, it’s interpreted as an octal number, where 10 is decimal 8. So rather than this being hexadecimal 0x0F or decimal 15, using 00001111 yields decimal 585. (This has changed in ECMAScript 6.)

The version of JavaScript that is used by the Payload Formats (being: ECMAScript 5) does not have any prefix to explicitly denote binary, like one can use 0x to prefix hexadecimal numbers, where 0x10 is decimal 16. (ECMAScript 6 allows for writing 0b00001111.)

One could use parseInt("00001111", 2), and as JavaScript is doing all kinds of type coercions for you, even parseInt(1111, 2) would work, where 1111 is first changed into the string “1111” and then interpreted as a binary number. But leaving out the quotes would not work with leading zeroes, as then it would first be interpreted as octal yielding 585 in decimal, after which the string “585” cannot be parsed as a binary number.

No need for explicit casting; (bytes[0] >> 4) != 0 is already a boolean, so suffices.

That’s indeed what I’d expect (though the parentheses are not needed when using a bitwise OR). But like you already wrote: given the examples I guess it’s not.

1 Like

Wow…thanks guys, would never have got close to that by myself.

I won’t have an opportunity to test for a couple of days but as you did, I put it into the application and tested those few readings we had and it looked good.

Will keep you up to date with how it goes

cheers
Paul

JS does WHAT!! :laughing:

Thank goodness someone has come to their senses. I started programming in Fortran, learnt Cobol and Pascal (showing my age). Coded in assembler on PDP11/04 (and many other machines and chips since) but never heard of a language that assumed something that started with a zero to be Octal. (Said i was a novice in JS and I think I might just keep it that way :grin: )

I referred to JS documentation but did not realise there are significant differences in the versions. So thanks for the feedback, it’s most appreciated.

@Paul_Stewart, let us know how you get on.

It’s the same in C/C++ and Java. And removing support is a breaking change for existing code, though I yet need to run into someone who uses octal.

Removing it surely takes the fun out of coaching juniors, where some like to align values in code, like:

var a = 200;
var b = 080; // decimal 80 in JavaScript, as 8 is out of range for octal
var c = 070; // decimal 56

That’ll teach them. :wink:

1 Like

A post was merged into an existing topic: Rubbish/Trash Can monitoring

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.