LT-33222-L LoRa I/O Controller - Payload decoder and downlink config

While working with the LT-33222 I found a wrong configuration in the preconfigured device to receive downlink data. Ensure you set the RX2 data rate to SF9/125 with the AT command “AT+RX2DR=3”.

And here is my payload decoder, DIx are inverted to represent the logical state, not the physical one.
Input is 1 when tied to GND (Device LED on).

function Decoder(bytes, port) {
  var decoded = {};
  var inputs = bytes[8];
  
  decoded.ACI1 = ((bytes[0] << 8) + bytes[1])/100;
  decoded.ACI2 = ((bytes[2] << 8) + bytes[3])/100;
  decoded.AVI1 = ((bytes[4] << 8) + bytes[5])/100;
  decoded.AVI2 = ((bytes[6] << 8) + bytes[7])/100;
  decoded.DO1 = inputs & (1 << 0) ? 1 : 0;
  decoded.DO2 = inputs & (1 << 1) ? 1 : 0;  
  decoded.DO3 = inputs & (1 << 2) ? 1 : 0;
  decoded.DI1 = inputs & (1 << 3) ? 0 : 1;
  decoded.DI2 = inputs & (1 << 4) ? 0 : 1;
  decoded.DI3 = inputs & (1 << 5) ? 0 : 1;
  decoded.RO1 = inputs & (1 << 6) ? 1 : 0;
  decoded.RO2 = inputs & (1 << 7) ? 1 : 0;
  
  return decoded;
}

Feel free to use and improve :wink:

  • Christian
1 Like

Am I right to assume you’re using ABP?

For ABP one indeed needs to manually configure the network settings, which for TTN implies configuring RX2, at least for EU868. For OTAA, the configuration should be automatic. (So, if you’re using OTAA, you might want to report a bug with Dragino.)

No, I’m using OTAA

Unfortunately the RX2 is not configured correctly in my case.
The controller reports RX2 configured as DR_0 (SF12/125) but my all my gateways are sending downlink with DR_3 (SF9/125).

I’m going to file a bug report.

I’m also using OTAA, all the uplinks are fine, but you have to configure (at present) the downlink manually via a serial connection to the device … AT+RX2DR=3 … once this is done, works just great !

Hey I have a question about the LT-33222. I want to buy one of these controllers and a 8 Ch Gateway. I. want to control one of the onboard relays. My Question is:
How often can i switch the relay on this Controller, because the downlink limitation from TTN. Can i switch every time the Controller sends data to the network and right after that it can receive the data? Or how does it works? I’m completly New to this topic but i think the Controller is the right thing for me.
Maybe you can help me :slight_smile:

  • for basic controlling lights and things in your house, better use one of these modules and not LoRaWAN / TTN
1 Like

Yeah i know. But i dont want to control lights. I want to control like 10 solenoidvalves for a irrigation System on fields. My Main question is how often i can downlink to the Controller.

see

Encoder:

function Encoder(object, port) {
  
  var bytes = [];
  
  if (object.Control == "DO")
  {
    //test with: {"Control": "DO", "DO1":1 , "DO2":1, "DO3":1} => 02 01 01 01 (where 1 = on, 0 = off)
    
    bytes[0] = 0x02;
    if (object.DO1 ==1)
    {
      bytes[1] = 1;
    }
    else
    {
      bytes[1] = 0;
    }

    if (object.DO2 ==1)
    {
      bytes[2] = 1;
    }
    else
    {
      bytes[2] = 0;
    }

    if (object.DO3 ==1)
    {
      bytes[3] = 1;
    }
    else
    {
      bytes[3] = 0;
    }
  }

  if (object.Control == "RELAY")
  {
    //test with: {"Control": "RELAY", "RO1":1 , "RO2":1} => 03 01 01 (where 1 = close, 0 = open)
    //test with: {"Control": "RELAY", "RO1":0 , "RO2":1} => 03 00 01 (where 1 = close, 0 = open)
    //test with: {"Control": "RELAY", "RO1":1 , "RO2":0} => 03 01 00 (where 1 = close, 0 = open)
    bytes[0] = 0x03;
    if (object.RO1 ==1)
    {
      bytes[1] = 1;
    }
    else
    {
      bytes[1] = 0;
    }
    
    if (object.RO2 ==1)
    {
      bytes[2] = 1;
    }
    else
    {
      bytes[2] = 0;
    }
  }

  if (object.Control == "RESET")
  {
    //test with: {"Control": "RESET"} => 04 FF
    bytes[0] = 0x04;
    bytes[1] = 0xFF;
  }

  if (object.Control == "PERIOD")
  {
    //test with: {"Control": "PERIOD", "Seconds":60} => 01 00 3C
    bytes[0] = 0x01;
    
    // in seconds byte[2]..byte[3]  
    
    bytes[1] = object.Seconds >> 8;
    bytes[2] = object.Seconds;
    
  }

  return bytes;
}
1 Like

Nice, thanks for sharing! But beware, the following won’t work for values larger than 255 seconds:

bytes[1] = object.Seconds >> 8;
bytes[2] = object.Seconds;

Unlike in, e.g., C++ where a byte can really only hold 8 bits and all other bits are simply discarded, JavaScript does not have such specific types. (Instead, everything is a floating point.) Above, bytes[2] will hold the full value, not just limited to its 8 least significant bits. You can try with {"Control": "PERIOD", "Seconds": 300}, which will not throw an error in the JavaScript, but TTN will still reject with Error("Encoder Output not valid: Numbers in Array should be between 0 and 255").

Also, it seems you need 3 bytes for the time?

So, I’d use:

// test with: {"Control": "PERIOD", "Seconds": 86400} => 01 01 51 80 (24 hours)
bytes[1] = object.Seconds >> 16;
bytes[2] = object.Seconds >> 8 & 0xFF;
bytes[3] = object.Seconds & 0xFF;

For bytes[1] you don’t need the & 0xFF to limit to 8 bits (as all bits in the leftmost bytes are zeroes), but it won’t harm either.

And just some notes in case you didn’t know:

You’re defaulting to zero. Probably by design, but just so you’re aware: {"Control": "DO", "DO1": 2, "DO2": "foo"} (where DO3 is missing and the others have invalid values) will silently set all outputs to zero. If you don’t expect errors in the input, then you could simply use:

bytes[1] = object.DO1;
bytes[2] = object.DO2;
bytes[3] = object.DO3;

You can define helper functions too, either above Encoder or within that function. Like:

// Decodes the value to either 1 or 0
function onoff(val) {
  // Non-strict equals: return 1 for the number 1 or the string "1"
  if (val == 1) {
    return 1;
  }
  // Default all missing or invalid values to 0
  return 0;
}

function Encoder(object, port) {
  var bytes = [];
  if (object.Control === "DO") {
    //test with: {"Control": "DO", "DO1":1 , "DO2":1, "DO3":1} => 02 01 01 01 (where 1 = on, 0 = off)
    bytes[0] = 0x02;
    bytes[1] = onoff(object.DO1);
    bytes[2] = onoff(object.DO2);
    bytes[3] = onoff(object.DO3);
  }
  ...
}

If you want to fail on invalid inputs then we’d need to investigate on how TTN acts on returning a null value or on throwing an error. (TTN Console might work differently from the APIs there; not sure if there’s any documentation about that.)

1 Like

Thanks Arjan
I was a little lazy with period - thanks for correction.
I am still trying to get the unit to uplink a payload. I have only been able to test the encoder. Just preparing an email to Dragino support. (yet other lansetic units work as soon as you turn then on.

The four voltage and current variables should be divided by 1000 and not 100 :stuck_out_tongue_closed_eyes:

Instead of this:

This:

decoded.ACI1 = ((bytes[0] << 8) + bytes[1])/1000;
decoded.ACI2 = ((bytes[2] << 8) + bytes[3])/1000;
decoded.AVI1 = ((bytes[4] << 8) + bytes[5])/1000;
decoded.AVI2 = ((bytes[6] << 8) + bytes[7])/1000;

Sorry @bryansmith but can’t confirm this.
The device documentation (LoRa_IO_Controller_UserManual_v1.0.2) defines on page 9:

For example if payload is: 0131013004AB04ACAA

The value for the interface is:
ACI2 channel current is 0x0130/100=3.04mA
AVI1 channel voltage is 0x04AB/100=11.95V
AVI2 channel voltage is 0x04AC/100=11.96V

My devices monitor their supply voltage (about 12v) and the readings are fine with /100:

{
“ACI1”: 0,
“ACI2”: 0,
“AVI1”: 11.97,
“AVI2”: 0,
“DI1”: 1,
“DI2”: 0,
“DI3”: 0,
“DO1”: 0,
“DO2”: 0,
“DO3”: 0,
“RO1”: 0,
“RO2”: 0
}

I see your reference to user manual version 1.0.2 yet under versions 1.1.0 and 1.1.1 the decoder provides inaccurate data.

Page 9 of version 1.0.2 payload format:
0131013004AB04ACAA

Page 11 of version 1.1.1 payload format:
1310 1300 04AB 04ACAA

Your decoder works fine on version 1.0.2 and 1.0.4 but anything above that Dragino changed the format by removing the leading 0 :see_no_evil:

1 Like

:roll_eyes: this is going to be funny in an environment with different firmware versions… thank you for pointing it out!

I’m a newbie regarding lorawan, scripting, et cetera. After some study on the internet I bought the I / O controller from Dragino. Now I am testing and trying out. I have made the configuration in the TTN, I can also enter AT commands. I also made the connection with Cheyenne. Then I installed the latest firmware. And now I get “payload:[not provided]”. I can’t find any help anymore.
Thanks in advance.

that’s not very much info for someone to help you I think.
I have made the configuration in the TTN
I can also enter AT commands
I also made the connection with Cheyenne
I installed the latest firmware

After installing the old firmware everything works again. Has nothing to do with payload. My be someone with better knowledge could configure all the wrong parameters with the newer firmware.

Thanks anyway.

1 Like

Does anyone know if this device can trigger uplink packets based on state changes on the Inputs, or is it limited to the timed period reporting? I am looking for the lowest possible delay to report an event.

Not at the moment, I made a request on Github over a year ago. The feature would make this device much more interesting.