Integrating LoRaWAN sensors into Home Assistant

Uiguy, I was amazed when I read your post about your LoRaWAN network and seeing the distances that it can communicate. What hardware are you using? It’s astounding considering the endpoints from Jersey and the mainland aren’t even line-of-sight. What type of antennas and power levels do you use?

Hi,

There is nothing special about the hardware, it just needs to be positioned correctly. That specific gateway is line of of sight actually.

That gateway is a HP0D from Dragino which is actually mining Helium as well as broadcasting our network (LNS) at the same time!

The setup is straight out the box (standard setup - 3dBm Antenna)

These gateways are actually just a raspberry pi with a helium/LoRaWAN HAT, so power consumption is pretty negligible

Yea, I don’t think that is possible with that Gateway. You need to install on dediccated Hardware or VM.

If you like, you are more than welcome to use our Chirpstack core for testing. PM me if you are interested and I can setup a tenant on there.

@rbauman70,
Can you please share the device (heltec) code?
I’m trying to run the examples but i’m getting a lot of errors and don’t know why.

Sure. Here’s my LoRa_GNSS.ino code.

/*
 * LoRaWAN GNSS tracker
 *
 */

#include "LoRaWan_APP.h"
#include "CayenneLPP.h"

#include <Wire.h>  
#include "HT_SSD1306Wire.h"

/* OTAA para*/
uint8_t devEui[] = { 0x22, 0x32, 0x33, 0x00, 0x00, 0x88, 0x88, 0x02 };
uint8_t appEui[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t appKey[] = { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x66, 0x01 };

/* we're using OTAA, but these still need to be defined to compile successfully
/* ABP para*/
uint8_t nwkSKey[] = { 0x15, 0xb1, 0xd0, 0xef, 0xa4, 0x63, 0xdf, 0xbe, 0x3d, 0x11, 0x18, 0x1e, 0x1e, 0xc7, 0xda,0x85 };
uint8_t appSKey[] = { 0xd7, 0x2c, 0x78, 0x75, 0x8c, 0xdc, 0xca, 0xbf, 0x55, 0xee, 0x4a, 0x77, 0x8d, 0x16, 0xef,0x67 };
uint32_t devAddr =  ( uint32_t )0x007e6ae1;

/*LoraWan channelsmask*/
/* using channels 8-15 */
uint16_t userChannelsMask[6]={ 0xFF00,0x0000,0x0000,0x0000,0x0000,0x0000 };

/*LoraWan region, select in arduino IDE tools*/
LoRaMacRegion_t loraWanRegion = ACTIVE_REGION;

/*LoraWan Class, Class A and Class C are supported*/
DeviceClass_t  loraWanClass = CLASS_A;

/*the application data transmission duty cycle.  value in [ms].*/
uint32_t appTxDutyCycle = 30000;

/*OTAA or ABP*/
bool overTheAirActivation = true;

/*ADR enable*/
bool loraWanAdr = true;


/* Indicates if the node is sending confirmed or unconfirmed messages */
bool isTxConfirmed = true;

/* Application port */
uint8_t appPort = 2;
/*!
* Number of trials to transmit the frame, if the LoRaMAC layer did not
* receive an acknowledgment. The MAC performs a datarate adaptation,
* according to the LoRaWAN Specification V1.0.2, chapter 18.4, according
* to the following table:
*
* Transmission nb | Data Rate
* ----------------|-----------
* 1 (first)       | DR
* 2               | DR
* 3               | max(DR-1,0)
* 4               | max(DR-1,0)
* 5               | max(DR-2,0)
* 6               | max(DR-2,0)
* 7               | max(DR-3,0)
* 8               | max(DR-3,0)
*
* Note, that if NbTrials is set to 1 or 2, the MAC will not decrease
* the datarate, in case the LoRaMAC layer did not receive an acknowledgment
*/
uint8_t confirmedNbTrials = 4;

// GNSS settings

// U-BLOX firmware version
#define   UBLOX_FW_VERSION  7

// Enable Serial debbug on Serial UART to see registers written
#define GNSS_DEBUG Serial

#define TX_PIN  46
#define RX_PIN  45
#define LED_PIN 35

#include "ublox_GNSS.h"

// Use hardware serial support
#define Serial_GNSS Serial1

float lat, lon, alt, acc, dist;

fixType_t fix = NO_FIX;

GNSS gnss( Serial_GNSS, UBLOX_FW_VERSION );
extern SSD1306Wire display;


typedef struct coords_s {
  float lat;
  float lon;
} COORDS;

COORDS home = { 35.90477, -78.83601 };

/* This is the first call to the display after sleep so we have to duplicate
 * LoRaWanClass::displaySending()
 */
void displayAcquiring()
{
	digitalWrite(Vext,LOW);
	display.init();
	display.setFont(ArialMT_Plain_16);
	display.setTextAlignment(TEXT_ALIGN_CENTER);
	display.clear();
	display.drawString(display.getWidth()/2, display.getHeight()/2, "ACQUIRING LOC");
	display.display();
}

void displayLocation()
{
	char temp[30];
	sprintf(temp,"dist: %3.1f km, hAcc %3.0f m",dist, acc);
	display.setFont(ArialMT_Plain_10);
	display.setTextAlignment(TEXT_ALIGN_RIGHT);
	display.drawString(128, 0, temp);
	display.display();
  delay( 2000 );
}

/* Prepares the payload of the frame */

static void prepareTxFrame( uint8_t port )
{
	/*appData size is LORAWAN_APP_DATA_MAX_SIZE which is defined in "commissioning.h".
	*appDataSize max value is LORAWAN_APP_DATA_MAX_SIZE.
	*if enabled AT, don't modify LORAWAN_APP_DATA_MAX_SIZE, it may cause system hanging or failure.
	*if disabled AT, LORAWAN_APP_DATA_MAX_SIZE can be modified, the max value is reference to lorawan region and SF.
	*for example, if use REGION_CN470, 
	*the max value for different DR can be found in MaxPayloadOfDatarateCN470 refer to DataratesCN470 and BandwidthsCN470 in "RegionCN470.h".
	*/
  gnss_acquire();

  CayenneLPP *lpp = new CayenneLPP( LORAWAN_APP_DATA_MAX_SIZE );

  lpp->reset();
  lpp->addGPS( 1, lat, lon, alt/1000 );
  lpp->addDistance( 2, acc );
  lpp->addDistance( 3, dist * 1000 );
  lpp->addDigitalOutput( 4, ( digitalRead( LED_PIN ) == HIGH ? 1 : 0 ));

  appDataSize = lpp->copy( appData );

  delete lpp;
}

void decodeDownlinkMsg( uint8_t *buf, uint8_t bufsize, uint8_t port )
{
    StaticJsonDocument<256> jsonBuffer;
    CayenneLPP lpp(0);

    JsonArray array = jsonBuffer.to<JsonArray>();
    lpp.decode( buf, bufsize, array );
    //serializeJsonPretty(array, Serial);
    //Serial.println();
    for ( JsonObject item : array ) {
    
      int lpp_type = item["type"];
      switch ( lpp_type )
      {
        case LPP_DIGITAL_OUTPUT:

          if ( item["channel"] == 4 )
          {
            int setval = LOW;
            if ( item["value"] == 1 )
            {
              setval = HIGH;
            }
            gpio_hold_dis( (gpio_num_t )LED_PIN );
            gpio_deep_sleep_hold_dis();
            pinMode(LED_PIN, OUTPUT);
            digitalWrite( LED_PIN, setval );
            gpio_deep_sleep_hold_en();
            gpio_hold_en( (gpio_num_t )LED_PIN );
          }
          break;

        default:
          break;
       } 
    }  
}

void downLinkDataHandle(McpsIndication_t *mcpsIndication)
{
	printf("+REV DATA:%s,RXSIZE %d,PORT %d\r\n",mcpsIndication->RxSlot?"RXWIN2":"RXWIN1",mcpsIndication->BufferSize,mcpsIndication->Port);
	printf("+REV DATA:");
	for(uint8_t i=0;i<mcpsIndication->BufferSize;i++)
	{
		printf("%02X",mcpsIndication->Buffer[i]);
	}
	printf("\r\n");
  if ( mcpsIndication->RxData )
  {
    decodeDownlinkMsg( mcpsIndication->Buffer, mcpsIndication->BufferSize, mcpsIndication->Port );
  }
}

RTC_DATA_ATTR bool firstrun = true;

void gnss_setup( void )
{

  Serial_GNSS.begin( 9600, SERIAL_8N1, RX_PIN, TX_PIN );

  #define ACQ_TIMEOUT     40
  #define SEARCH_PERIOD   0
  #define UPDATE_PERIOD   0
  #define ON_TIME         15

  if ( gnss.config( ON_OFF ) )
  {
    Serial.println("\nGNSS configured.");
  }
  else
  {
    Serial.println("\nGNSS configuration failed.");
  }

  /* wait for first fix */
  while ( gnss.getCoodinates(lon, lat, alt, fix, acc, 50) == 0 )
  {
    Serial.println("\nInitial fix failed.");
  }

  float distance = gnss.HaverSine( home.lat, home.lon, lat, lon );
  Serial.println("Distance to home: " + String( distance, 2 ) + " km" );


  if ( gnss.init( ON_OFF, ACQ_TIMEOUT, SEARCH_PERIOD, UPDATE_PERIOD, ON_TIME ) )
  {
    Serial.println("\nGNSS initialized.");
  }
  else
  {
    Serial.println("\nFailed to initialize GNSS module.");
  }

  gnss.conditionalOff();
}

void gnss_acquire( void )
{
  Serial.println("Get location");

  gnss.reinst( Serial_GNSS, UBLOX_FW_VERSION );

  Serial_GNSS.begin( 9600, SERIAL_8N1, RX_PIN, TX_PIN );

  gnss.wake();

  delay( 1000 );

  if ( gnss.getCoodinates(lon, lat, alt, fix, acc, 50) == 0) 
  {
    Serial.println("Failed to get coordinates, check signal, accuracy required or wiring");
  }

  Serial.println("Location lat:" + String(lat, 7) +" lon:" + String(lon, 7) +
                  + " alt: " + String( alt ) + "m horizontal accuracy: " + String(acc) + "m");

  //Serial.println("\nOr try the following link to see on google maps:");
  //Serial.println(String("https://www.google.com/maps/search/?api=1&query=") + String(lat,7) + "," + String(lon,7));

  dist = gnss.HaverSine( home.lat, home.lon, lat, lon );
  Serial.println("Distance to home: " + String( dist, 2 ) + " km" );

  gnss.conditionalOff();

}

void setup() {

  Serial.begin(115200);
  Mcu.begin();
  if(firstrun)
  {
    LoRaWAN.displayMcuInit();
    firstrun = false;

    gnss_setup();

 }

	deviceState = DEVICE_STATE_INIT;
}

void loop()
{

  switch( deviceState )
	{
		case DEVICE_STATE_INIT:
		{
#if(LORAWAN_DEVEUI_AUTO)
			LoRaWAN.generateDeveuiByChipID();
#endif
			LoRaWAN.init(loraWanClass,loraWanRegion);

      break;
		}
		case DEVICE_STATE_JOIN:
		{
      LoRaWAN.displayJoining();
			LoRaWAN.join();
			break;
		}
		case DEVICE_STATE_SEND:
		{
      displayAcquiring();
			prepareTxFrame( appPort );
      displayLocation();

      LoRaWAN.displaySending();

			LoRaWAN.send();
			deviceState = DEVICE_STATE_CYCLE;
			break;
		}
		case DEVICE_STATE_CYCLE:
		{
			// Schedule next packet transmission
			txDutyCycleTime = appTxDutyCycle + randr( 0, APP_TX_DUTYCYCLE_RND );
			LoRaWAN.cycle(txDutyCycleTime);
			deviceState = DEVICE_STATE_SLEEP;
			break;
		}
		case DEVICE_STATE_SLEEP:
		{
      LoRaWAN.displayAck();
			LoRaWAN.sleep(loraWanClass);

			break;
		}
		default:
		{
			deviceState = DEVICE_STATE_INIT;
			break;
		}
	}
}

The contents of the Arduino sketch.yaml file.

default_fqbn: heltec:esp32:WIFI_LoRa_32_V3:UploadSpeed=921600,CPUFreq=240,DebugLevel=none,LORAWAN_REGION=3,LoRaWanDebugLevel=0,LoopCore=1,EventsCore=1,LORAWAN_PREAMBLE_LENGTH=0,LORAWAN_DEVEUI=1
default_port: COM5

And the decoder.js file.

/**
 * @reference https://github.com/myDevicesIoT/cayenne-docs/blob/ff6da3d400d406a3cbc6b380abe3eeaa7009bbb9/docs/LORA.md
 * @reference http://openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html#extlabel
 *
 * Adapted for lora-app-server from https://gist.github.com/iPAS/e24970a91463a4a8177f9806d1ef14b8
 *
 * Type                 IPSO    LPP     Hex     Data Size   Data Resolution per bit
 *  Digital Input       3200    0       0       1           1
 *  Digital Output      3201    1       1       1           1
 *  Analog Input        3202    2       2       2           0.01 Signed
 *  Analog Output       3203    3       3       2           0.01 Signed
 *  Illuminance Sensor  3301    101     65      2           1 Lux Unsigned MSB
 *  Presence Sensor     3302    102     66      1           1
 *  Temperature Sensor  3303    103     67      2           0.1 °C Signed MSB
 *  Humidity Sensor     3304    104     68      1           0.5 % Unsigned
 *  Accelerometer       3313    113     71      6           0.001 G Signed MSB per axis
 *  Barometer           3315    115     73      2           0.1 hPa Unsigned MSB
 *  Time                3333    133     85      4           Unix time MSB
 *  Gyrometer           3334    134     86      6           0.01 °/s Signed MSB per axis
 *  GPS Location        3336    136     88      9           Latitude  : 0.0001 ° Signed MSB
 *                                                          Longitude : 0.0001 ° Signed MSB
 *                                                          Altitude  : 0.01 meter Signed MSB
 *
 * Additional types
 *  Generic Sensor      3300    100     64      4           Unsigned integer MSB
 *  Voltage             3316    116     74      2           0.01 V Unsigned MSB
 *  Current             3317    117     75      2           0.001 A Unsigned MSB
 *  Frequency           3318    118     76      4           1 Hz Unsigned MSB
 *  Percentage          3320    120     78      1           1% Unsigned
 *  Altitude            3321    121     79      2           1m Signed MSB
 *  Concentration       3325    125     7D      2           1 PPM unsigned : 1pmm = 1 * 10 ^-6 = 0.000 001
 *  Power               3328    128     80      2           1 W Unsigned MSB
 *  Distance            3330    130     82      4           0.001m Unsigned MSB
 *  Energy              3331    131     83      4           0.001kWh Unsigned MSB
 *  Colour              3335    135     87      3           R: 255 G: 255 B: 255
 *  Direction           3332    132     84      2           1º Unsigned MSB
 *  Switch              3342    142     8E      1           0/1

 * 
 */

// lppDecode decodes an array of bytes into an array of ojects, 
// each one with the channel, the data type and the value.
function lppDecode(bytes) {
    
    var sensor_types = {
        0  : {'size': 1, 'name': 'digital_in', 'signed': false, 'divisor': 1},
        1  : {'size': 1, 'name': 'digital_out', 'signed': false, 'divisor': 1},
        2  : {'size': 2, 'name': 'analog_in', 'signed': true , 'divisor': 100},
        3  : {'size': 2, 'name': 'analog_out', 'signed': true , 'divisor': 100},
        100: {'size': 4, 'name': 'generic', 'signed': false, 'divisor': 1},
        101: {'size': 2, 'name': 'illuminance', 'signed': false, 'divisor': 1},
        102: {'size': 1, 'name': 'presence', 'signed': false, 'divisor': 1},
        103: {'size': 2, 'name': 'temperature', 'signed': true , 'divisor': 10},
        104: {'size': 1, 'name': 'humidity', 'signed': false, 'divisor': 2},
        113: {'size': 6, 'name': 'accelerometer', 'signed': true , 'divisor': 1000},
        115: {'size': 2, 'name': 'barometer', 'signed': false, 'divisor': 10},
        116: {'size': 2, 'name': 'voltage', 'signed': false, 'divisor': 100},
        117: {'size': 2, 'name': 'current', 'signed': false, 'divisor': 1000},
        118: {'size': 4, 'name': 'frequency', 'signed': false, 'divisor': 1},
        120: {'size': 1, 'name': 'percentage', 'signed': false, 'divisor': 1},
        121: {'size': 2, 'name': 'altitude', 'signed': true, 'divisor': 1},
        125: {'size': 2, 'name': 'concentration', 'signed': false, 'divisor': 1},
        128: {'size': 2, 'name': 'power', 'signed': false, 'divisor': 1},
        130: {'size': 4, 'name': 'distance', 'signed': false, 'divisor': 1000},
        131: {'size': 4, 'name': 'energy', 'signed': false, 'divisor': 1000},
        132: {'size': 2, 'name': 'direction', 'signed': false, 'divisor': 1},
        133: {'size': 4, 'name': 'time', 'signed': false, 'divisor': 1},
        134: {'size': 6, 'name': 'gyrometer', 'signed': true , 'divisor': 100},
        135: {'size': 3, 'name': 'colour', 'signed': false, 'divisor': 1},
        136: {'size': 9, 'name': 'gps', 'signed': true, 'divisor': [10000,10000,100]},
        142: {'size': 1, 'name': 'switch', 'signed': false, 'divisor': 1},
    };

    function arrayToDecimal(stream, is_signed, divisor) {

        var value = 0;
        for (var i = 0; i < stream.length; i++) {
            if (stream[i] > 0xFF)
                throw 'Byte value overflow!';
            value = (value << 8) | stream[i];
        }

        if (is_signed) {
            var edge = 1 << (stream.length) * 8;  // 0x1000..
            var max = (edge - 1) >> 1;             // 0x0FFF.. >> 1
            value = (value > max) ? value - edge : value;
        }

        value /= divisor;

        return value;

    }

    var sensors = [];
    var i = 0;
    while (i < bytes.length) {

        var s_no   = bytes[i++];
        var s_type = bytes[i++];
        if (typeof sensor_types[s_type] == 'undefined') {
            throw 'Sensor type error!: ' + s_type;
        }

        var s_value = 0;
        var type = sensor_types[s_type];
        switch (s_type) {

            case 113:   // Accelerometer
            case 134:   // Gyrometer
                s_value = {
                    'x': arrayToDecimal(bytes.slice(i+0, i+2), type.signed, type.divisor),
                    'y': arrayToDecimal(bytes.slice(i+2, i+4), type.signed, type.divisor),
                    'z': arrayToDecimal(bytes.slice(i+4, i+6), type.signed, type.divisor)
                };
                break;
            
            case 136:   // GPS Location
                s_value = {
                    'latitude': arrayToDecimal(bytes.slice(i+0, i+3), type.signed, type.divisor[0]),
                    'longitude': arrayToDecimal(bytes.slice(i+3, i+6), type.signed, type.divisor[1]),
                    'altitude': arrayToDecimal(bytes.slice(i+6, i+9), type.signed, type.divisor[2])
                };
                break;
			case 135:   // Colour
				s_value = {
                    'r': arrayToDecimal(bytes.slice(i+0, i+1), type.signed, type.divisor),
                    'g': arrayToDecimal(bytes.slice(i+1, i+2), type.signed, type.divisor),
                    'b': arrayToDecimal(bytes.slice(i+2, i+3), type.signed, type.divisor)
                };
                break;

            default:    // All the rest
                s_value = arrayToDecimal(bytes.slice(i, i + type.size), type.signed, type.divisor);
                break;
        }
        
        sensors.push({
            'channel': s_no,
            'type': s_type,
            'name': type.name,
            'value': s_value
        });

        i += type.size;

    }

    return sensors;

}

// To use with TTN
function decodeUplink(input) {

    bytes = input.bytes;
    fPort = input.fPort;

    // flat output (like original decoder):
    var response = {};
    lppDecode(bytes, 1).forEach(function(field) {
        response[field['name'] + '_' + field['channel']] = field['value'];
    });
    return {
       data: response
    };

    // field output
    //return {'fields': lppDecode(bytes, fPort)};

}

// To use with NodeRED
// Assuming msg.payload contains the LPP-encoded byte array
/*
msg.fields = lppDecode(msg.payload);
return msg;
*/
// Encode downlink function.
//
// Input is an object with the following fields:
// - data = Object representing the payload that must be encoded.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - bytes = Byte array containing the downlink payload.
// 
// our data looks like: {"array":[{"channel":4,"name":"digital_out","value":1}]}

function encodeDownlink(input) {

  var arr = parseExtJSON( input.data );
  var i = 0;
  var output = [];

  arr.array.forEach(( item ) => {

    switch ( item.name ) {
      case "digital_out":
        item.type = 1;
        break;
      default:
        item.type = 255;
        break;
    }

    output[i++] = item.channel;
    output[i++] = item.type;
    output[i++] = item.value;

  });

  return {
    bytes: output
  };
}

I recommend getting the LoRaWan_OLED example running on the Heltec before tackling this.

Greate,
Thanks a lot :upside_down_face:

Hi,
I am searching a „remote Lorawan button“. I want to use the button to reliably trigger a light. I do want to use a small physical button, I can carry around, I know I could use my mobile with HA, but fiddling out the mobile on my bike is not very pleasant, my idea is to press this button about 200m before my garage door, so that it starts opening, and I just drive directly in.
I found MClimate Multipurpose Button LoRaWAN | MClimate LoRaWAN Devices e.g but do not understand to to integrate it in my dragino gateway. The setup is otaa, but I have no clue how to use the dragino with otaa. All sensors I use I setup with ABP. So does anyone has a remote button locally integrated into the dragino, or succsessfully setup otaa on the dragino server? I am googling without luck since about over 8 month.

Many thanks for a pointer to something that could do this job
Jürgen

You could use zone (with conditions) and then simply trigger the garage door to open when your mobile phone enters the zone.

Whilst your use case is feasible on LoRWAN, it is not 100% suited because:

  1. LoRaWAN is better for stationary sensors

  2. Latency can sometimes be an issue

Also, check your Dragino gateway is LoRaWAN (8 channels) and not a single channel gateway. OTAA does not work on single channel gateways

Hello community,
using the TTN integration I have integrated a LoraWAN water level sensor (Dragino PS-LB), which sends the water level in cm. I can see the sensor values as an entity of the TTN integration, the values correctly arrive at HA. Unfortunately
I cannot view the historical values as a curve or bar chart, but only get a single horizontal bar with different colors:
Screenshot 2024-11-10 154235

According to my research, I have to enter the unit_of_measurement somewhere for other display variants, but I don’t know where and how. Can any of you help me?

Thank you very much!
Franz Josef

Hi, Franz. You should first check the settings for the device in the integration panel for TTN. Typically the developer has set the sensor’s attributes correctly. I do not use that integration so I cannot help you there. I do have an MQTT depth sensor that measures in “ft” as the unit_of_measurement and will display a history graph. You can also use a “customize” entry to set the unit_of_measurement for the sensor. This thread discusses a situation similar to yours with suggestions: Unit of Measurement - 2022.4.

Thank you for your fast reply. I wanted to avoid MQTT thinking it´s too complicated - but maybe I´m wrong an the TTN-integration is even more…
The customization possibility is exactly missing the “Show as” input like we can see it in the following example:

So, I have to write anything in the configuration.yaml (but don´t know what…) or deal myself with the “mqtt topic”.

Hi, I have just created an integration from Lora sensors for TTN to Home Assistant where you can configure your own sensors. Maybe this is an option for you. Here an example from my MClimate Vicki - LoRaWAN Smart Radiator Thermostat:

and here a presence sensor:

Placed it on github:

Hello Community!

I just started to explore Lora. I was very impressed by its technology. But i have one simple question. Is it actually usable to monitor and control an apartment? I mean it has so much limitation.
For example in my case there is a small cottage in the woods owned by a friend. It is 18-20 kilometers far in the air. There is no wired electricity, water, GSM signal, nothing. But it has a battery powered solar system.

With lots of effort i can make a tall mast for the line of sight communication. Because the solar system i can install some smoke sensors, temperature sensors, swich, etc. So my friend will be able to monitor and controll his apartment and get information aboute any problem, or start the heating manually if he want to spend some time there in the winter.

But i start to count the necessary sensors (heat, smoke, water, battery, switches, etc.) so for full controll it needs at least 15-20 entity in HA. But lora has a limitation for packet size and time on the air. As i know it is 30 sec in every 24 hours. I’ve check some tasmota sensors mqtt message size and for me it is looks like they generating so much data. Much more what Lora can handle within its imitations.

So i missed something? Or Lora is a grate solution for 1-2 sensor to play and learn with radio waves but it is not the technology for remote home control?

This is specific to your use case but not with Lorawan… How about just tying your mobile location from the companion app into HA. Then you could have a bluetooth button that talks to your mobile HA to tell it to open the garage (only if it is closed and you are near your home), and the same button could be used to tell it to close the gareage (if open and regardless of whether or not you are near your home) - so also pressing it when far away from home would not open it by mistake when in your pocket etc…? And you could even leave the button on the bike as it would not work if your phone was nowhere near it… ? Also handy if you have a separate entrance to the garage, when you go in there from a different door, just pres the button on the bike to open the door for you (but I assume too much of your use case)…?

@rbauman70 did you every try putting the lora module in listening mode and putting the ESP to sleep? i do this on one of my projects… this way when the lora module receives a message it can wake the ESP up and process it.

I use lora rather than lorawan… and wrote my own encryption… but its ugly and hard to maintain and develop further.

one of the key things was handling the re-initialisation of the lora module after the ESP32 wakes up. it would be really good to see a working solution for instant 2 way lora comms… without having to wait for the device to come out of sleep mode

Lora has its uses just like every other type of technology. You need to evaluate your requirements against its limitations.
First of all, 18-20 kilometers is about the maximum advertised range for Lora. And that’s in rural areas with little interference and perfect weather conditions.
You’ll find the 30-second limitation is not really a problem. Even at the 400 ms and 64 bytes per packet limit, that’s still 75 packets per day or about 3 per hour. You have 12 bytes of payload to work with. Many sensors are binary, like smoke or water detection and only require one bit and others change slowly enough like temperature or battery charge that once per hour monitoring is sufficient.
While Lora is fine for monitoring you must realize its latency is large compared to many other wireless technologies so “real-time” control loops are not really practical.
You might also consider the effort required both initially and long-term. You mentioned the mast but also think about how you would diagnose problems in the Lora link if you had to go on site with no cellular communication with the base station. Your friend will be calling you whenever data is not available.

No, I did not try that. I’m not sure the LoRaWAN Arduino library provides support for wake from sleep. I wasn’t worried about power consumption in my testing. I’m glad you’re having fun with Lora!

Low power was a key requirement for my plantation shutters project… I found sleep examples in this library GitHub - beegee-tokyo/SX126x-Arduino: Arduino library to use Semtech SX126x LoRa chips and modules to communicate which is the lora module used in the heltec devices

it didnt work out of the box but i managed to get it working, basically i had to do the hardware_re_init, then set the callbacks, then do irqprocessafterdeepsleep(). but this only allowed receiving of the data that woke the ESP32. after processing that i have to do a full re-initialisation of the radio to be able to transmit. the cool thing is you can actually read the received data from when the ESP was sleeping

a couple of months ago there was an update to the library to get the network and app session keys, i think these need to be kept during the sleep too for quick reconnection to the network when using lorawan.

I’ve progressed the blinds code to a point where its very manually configured with a handful of devices… but its quite messy code… and definitely not ready for release… would be great to have a proper off the shelf solution rather than my own custom solution… we really don’t need another gateway/hub device on the market. plus I’m really struggling to find time to progress it any further…

1 Like

Thank you so much for your explanation. Now i understand better and has sense to work with.
When he is onsite we can communicate via radio. It is just a favor from me and he is understand if the system has any problem i will not to run asap. But i walk there every month so it is not a big deal. The big deal is the cost of the mast and before i wanna test, but for test i may need some more stuff.
So one more question. Please can you tell we what can i do if the distance is too much? I search for repeaters but i found only Lora Enless what seems to much more inflexibility in the whole planned system…