Agoraopinion Feedback Button Payload decoder

hi all, i have some problems in getting a payload decoder in js for "agoraopinion feedback buttons"working proper. This device has 3 action keys(red,yellow,green). i took an already existing script and modify it. my solution shows only 2 of the 3 buttons that got triggered. i am totaly new to js. would be fine if you could help me. thanks

Here my solution:

var TYPE_PTYPE		= bytes[-2]; //Payload type
var TYPE_YELLOW		= bytes[2]; //Leftbutton
var TYPE_GREEN		= bytes[4]; //middle button
var TYPE_RED        = bytes[0]; //right button 
var TYPE_BL			= bytes[6]; //Battery Level
var TYPE_DI			= bytes[8]; //Device ID
function bin16dec(bin) {
    var num=bin&0xFFFF;
    if (0x8000 & num)
        num = - (0x010000 - num);
    return num;
}
function bin8dec(bin) {
    var num=bin&0xFF;
    if (0x80 & num) 
        num = - (0x0100 - num);
    return num;
}

function hexToBytes(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 3)
		bytes.push(parseInt(hex.substr(c, 2), 16));      
    return bytes;
}
function DecodeSmileybox(bytes){
    var obj = new Object();
    for(i=0;i<bytes.length;i++){
        //console.log(data[i]);
        switch(bytes[i]){
            case TYPE_PTYPE: //Payloadtype
                obj.ptype=(bytes[i+1]);
                i+=1;
            break
            case TYPE_YELLOW: //left button
                obj.yellow=(bytes[i+1]);
                i+=1;
            break
            case TYPE_GREEN: //middle button
                obj.green=(bytes[i+1]>>4); //obj.green=(bytes[i+1]);
                i+=1;
            break
            case TYPE_RED: //right button
                obj.red=(bytes[i+1]);
                i+=1;
            break
            case TYPE_BL: //Battery Level
                obj.bl=(bytes[i+1]);
                i+=1;
            break
            case TYPE_DI: //Device ID
                //obj.motion=(bytes[i+1]);
            	obj.di=(bytes[i+1]);
                i+=1;
            break
            default: //somthing is wrong with data
                i=bytes.length;
            break
        }
    }
    return obj;
}
function Decode(fPort, bytes) {
  return DecodeSmileybox(bytes);
}

here its documentation: [EN] Payload information V5.pdf (1014.7 KB)

i really don’t know how to solve it.
fine if you could help me thanks

regards
Michael

Okay, this is indeed a literal repost of your Slack post :slight_smile:

Let me repeat my remarks/questions too then:

The PDF document shows a text-based message, assuming you indeed get 16 bytes (or 14), not 8 (or 6), for the first message type that’s described in the PDF. That is not only quite bad for LoRaWAN (a big waste of airtime), but also complicates things a lot. Also, it does not at all match the script you’ve got so far: the script seems to expect a specific message whenever a button is pressed, rather than some totals as per the PDF.

So: it would help to see actual payloads as received in your application (TTN Console) along with the values that you think this should decode into.

mqtt shows me this here:
“adr” : true,
“fCnt” : 9,
“fPort” : 5,
“data” : “AgDADQDF”

the decoded data in hex will be look like this:
0200c00d00c5

02 - is the payload type (here voting)
00c - red button
00d - yellow button
00c - green button
5 - should be the batterylevel


hope this is helpful. thanks

Good, it’s not text! Even more, it only uses 1.5 bytes for a single count, not wasting anything at all. Many would simply have used 2 bytes per counter. :slight_smile:

Then the following might work? Note that it uses a fixed-format decoder; not some loop that iterates over the payload and tries to determine how to interpret the next part by comparing to some known values, like in your attempt:

/**
 * Decoder for Agora Opinion feedback button.
 */
function Decoder(bytes, port) {
  var result = {};
  result.payloadType = bytes[0];

  // 1.5 bytes (3 nibbles) per count, plus 0.5 byte for battery,
  // encoded in 5 bytes; rr ry yy gg gb
  result.redCount = bytes[1]<<4 | bytes[2]>>4;
  result.yellowCount = (bytes[2] & 0x0F)<<8 | bytes[3];
  result.greenCount = bytes[4]<<4 | bytes[5]>>4;

  // Unary-plus operator to cast string result of toFixed to number
  result.batteryVoltage = 
      +((3 * 1.224) / (((bytes[5] & 0x0F) + 20) / 21)).toFixed(1);

  return result;
}

To explain the bitwise operators, for the 5 bytes rr ry yy gg gb, all in hexadecimal notation using two 4-bits nibbles for each 8-bits byte:

  • rr << 4 appends a nibble to the right, so yields rr0 (left shift of 4 bits; actually the result is 32 bits, so 00000rr0)
  • ry >> 4 shifts the rightmost nibble out of memory, so discards y and yields r (right shift; actually 0000000r)
  • rr0 | r yields rrr (bitwise OR)
  • ry & 0x0F yields y (bitwise AND)
  • y << 8 yields y00
  • y00 | yy yields yyy

Easy! :nerd_face:

1 Like

Thanks for your quick help. i will test it, when i will be back in office and come back to you with the result.