TTGO LoRa32 V2.1 - TTN sketch for Relays with BME280 support

I made a sketch for connecting a TTGO Paxcounter to the things network with support for a relay and a temperature sensor without the paxcounter functionality. I found a few sketches online based on the paxcounter but I didn’t find them lean and mean. There is support for the oled display wich displays the relay status, temperature, humidity and pressure.

The default configuration will send the status of the sensor and relays every 5 minutes but is fully customizable. I have it working for almost a month without any problems on a couple of paxcounter boards.

You can turn a relais on or off by sending downlink messages in the TTN Console. I added some payload formats (Decider, Converter, Validator and Encoder) to make the payload more easy to read. This is also necessary if you want to control the relais with Node-Red.

You can download it on:


Hi connectix,

When switching a relay via down-link messages please keep in mind the limitation of max 10 down-link messages per day. For more information see this link.

I have updated the topic title for the correct name of the board: TTGO LoRa32 V2.1.
TTGO-PAXCOUNTER-LoRa32-V2.1 is a (confusing) marketing name but not the name of the board.


Hello connectix, could you explain to me what command should I write in the TTN console to activate and deactivate the relay? thank you for your great contribution

Did you see this code?

So, a downlink with value 01 will switch on the relais, and anything else will switch it off.

Aside, the README mentions the following, but I failed to find any payload formats in the repository:

I added some payload formats (Decider, Converter, Validator and Encoder) to make the payload more easy to read. This is also necessary if you want to control the relais with Node-Red.

If you are using this code and want a payload formatter I modified the one I found in an Adafruit LoRaWAN tutorial.

// TTN Decoder for TTN OTAA Feather US915 DHT22 Sketch
// Link: arduino-lmic/ttn-otaa-feather-us915-dht22.ino at master · mcci-catena/arduino-lmic · GitHub
function Decoder(bytes, port) {
// Decode an uplink message from a buffer
// (array) of bytes to an object of fields.
var decoded = {};

// relay
rawRelay = bytes[0];
decoded.relay = sflt162f(rawRelay);

// humidity
rawHumid = bytes[1] + bytes[2] * 256;
decoded.humidity = sflt162f(rawHumid) * 100;

// pressure 

rawPress = bytes[3] + bytes[4] * 256;
decoded.pressure = sflt162f(rawPress) * 100000;

// temperature 

rawTemp = bytes[5] + bytes[6] * 256;
decoded.degreesC = sflt162f(rawTemp) * 100;

return decoded;

function sflt162f(rawSflt16)
// rawSflt16 is the 2-byte number decoded from wherever;
// it’s in range 0…0xFFFF
// bit 15 is the sign bit
// bits 14…11 are the exponent
// bits 10…0 are the the mantissa. Unlike IEEE format,
// the msb is transmitted; this means that numbers
// might not be normalized, but makes coding for
// underflow easier.
// As with IEEE format, negative zero is possible, so
// we special-case that in hopes that JavaScript will
// also cooperate.
// The result is a number in the open interval (-1.0, 1.0);

// throw away high bits for repeatability.
rawSflt16 &= 0xFFFF;

// special case minus zero:
if (rawSflt16 == 0x8000)
	return -0.0;

// extract the sign.
var sSign = ((rawSflt16 & 0x8000) != 0) ? -1 : 1;

// extract the exponent
var exp1 = (rawSflt16 >> 11) & 0xF;

// extract the "mantissa" (the fractional part)
var mant1 = (rawSflt16 & 0x7FF) / 2048.0;

// convert back to a floating point number. We hope 
// that Math.pow(2, k) is handled efficiently by
// the JS interpreter! If this is time critical code,
// you can replace by a suitable shift and divide.
var f_unscaled = sSign * mant1 * Math.pow(2, exp1 - 15);

return f_unscaled;
1 Like

Thanks, this was very usefull for me.