Coder Social home page Coder Social logo

Downlink support about dragino HOT 14 CLOSED

computenodes avatar computenodes commented on August 16, 2024
Downlink support

from dragino.

Comments (14)

BNNorman avatar BNNorman commented on August 16, 2024 1

As it stands no but the changes to make it work are relatively simple. I have this working for a project I worked on last year.
Firstly you need to add a new function to register the callback function.
Secondly, you need to make changes to the on_rx_done() function in dragino.py.

step 1 - add a variable to hold the callback function address in class Dragino __init__()

self.msgCallback=None

step 2 - add a new method to class Dragino

 def setMsgCallback(self,callback):
        self.msgCallback=callback

step 3 modify class Dragino on_rx_done() - this is my changed on_rx_done(). Note this change only looks for unconfirmed downlink messages i.e. TTN isn't expecting a reply.

def on_rx_done(self):
        """
            Callback on RX complete, signalled by I/O
        """
        self.clear_irq_flags(RxDone=1)
        self.logger.debug("on_rx_done() Received downlink message")
       
        payload = self.read_payload(nocheck=True)
        if payload is None:
            self.logger("on_rx_done() Received message but payload is None")
            return  
           
        self.logger.debug("on_rx_done() payload="+"".join(format(x, '02x') for x in bytes(payload)))
        
        try:

            if self.network_key is None: # not joined yet
                self.logger.debug("on_rx_done() processing JOIN_ACCEPT payload")
                lorawan = lorawan_msg([], self.appkey)
                lorawan.read(payload)
                lorawan.get_payload()
            else:
                self.logger.info("on_rx_done() processing payload after joined")
                lorawan = lorawan_msg(self.network_key, self.apps_key)
                lorawan.read(payload)
                decodedPayload=lorawan.get_payload()
                self.logger.debug("on_rx_done() decodedPayload=%s", decodedPayload)
            
        except Exception as e:
            self.logger.exception("on_rx_done() exception %s",e)
            return None
         
        self.logger.debug("on_rx_done() mversion=%s", lorawan.get_mhdr().get_mversion())
        
        mtype=lorawan.get_mhdr().get_mtype()
        
        if mtype == MHDR.JOIN_ACCEPT:
            self.logger.info("on_rx_done() Processing JOIN_ACCEPT")
            #It's a response to a join request
            lorawan.valid_mic()
            self.device_addr = lorawan.get_devaddr()
            self.logger.debug("on_rx_done() Device: %s", self.device_addr)
            self.network_key = lorawan.derive_nwskey(self.devnonce)
            self.logger.debug("on_rx_done() Network key: %s", self.network_key)
            self.apps_key = lorawan.derive_appskey(self.devnonce)
            self.logger.debug("on_rx_done() APPS key: %s", self.apps_key)
            return
        
        elif mtype == MHDR.UNCONF_DATA_DOWN:
            self.logger.info("on_rx_done() processing UNCONF_DATA_DOWN")
            try:
                # keys obtained at OTAA join?
                lorawan = lorawan_msg(self.network_key, self.apps_key)
                lorawan.read(payload)
                decodedPayload = lorawan.get_payload()


                # note downlink messages are hex bytes in the TTN console
                self.logger.debug("on_rx_done() UNCONF_DATA_DOWN decodedPayload %s",decodedPayload)
                if self.msgCallback is not None:
                    self.logger.info("on_rx_done() starting callback to downlink manager")
                    self.msgCallback(decodedPayload)
                else:
                    self.logger.error("on_rx_done() no callback configured")
                return
            except Exception as e:
                self.logger.exception("on_rx_done() UNCONF_DATA_DOWN %s",e)
                return
                
        elif mtype == MHDR.CONF_DATA_DOWN :
            self.logger.warning("on_rx_done() CONF_DATA_DOWN - not supported")
            
            #if self.msgCallback is not None:
            #    self.logger.info("on_rx_done() starting callback to downlink manager")
            #    self.msgCallback(payload)
        else:
            self.logger.info("on_rx_done() callback ignored mtype=%s",mtype)

This works for me on an older version (last year) of this code. I haven't, but need to, modified the latest version

Be aware that queued downlink messages might only sent be after TTN sees an uplink message - depends which LoraWAN class you use. In my use case the uplink message interval was about 6 minutes so that would be the rate that downlinks could be sent.

Note, also, that the get_gps() method is not blocking (albeit for a configurable wait period). But that's another story.

from dragino.

pjb304 avatar pjb304 commented on August 16, 2024

I'm afraid I don't have time to add that into the main code, but if either of you would like to do so and issue a pull request I'll happily review.

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

I'll be updating my modified copy of your code in the near future, with your latest version. I've never done a pull request before so not sure how to go about that other than to attach the changed file (dragino.py) for you to look at.

from dragino.

pjb304 avatar pjb304 commented on August 16, 2024

Thank you. There's plenty of documentation about how to do a pull request. The short version is you create your own fork of the code make the changes to it, and then use the pull request tab on this project to create a request to merge the changes from your version into this version.

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

Ok, I'll try doing it that way.

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

Just looking at adding something to readme.md - the todo list includes adding GPS support but that is already included. Does readme.md need to be updated?

from dragino.

pjb304 avatar pjb304 commented on August 16, 2024

Almost certainly!

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

I have made the required changes but have yet to test on my Dragino - busy busy , like you, with more critical stuff. But I'll get there.

from dragino.

pjb304 avatar pjb304 commented on August 16, 2024

Thanks for working on this :)

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

Having some problems with receiving downlinks (Other than JOIN_ACCEPT).

Each time an uplink is sent the dragino.py code chooses a different frequency (it stayed with one frequency before IIRC). If TTN does not send the downlink on the current frequency then the downlink message is lost. The question is "How long to wait for a possible downlink message before we can transmit again, on a randomly selected frequency". When I get my test code sorted out I'll try to put a number on that but I suspect there will be a lot of lost downlink messages and they are thwarting my testing.

One other issue I had was shutting down my dragino overnight - It would not talk to TTN the next day. This, I think, has something to do with credential caching and (maybe) aging thereof (but not sure). I removed the appskey/devaddr and nwkskey which had been appended to the dragino.ini file and finally got the dragino and TTN talking to each other again. Personally I would prefer the caching to go in a seperate file and maybe add a timestamp to check it's staleness. If stale then issue a rejoin before a send. Possibly that could be controlled by the code using dragino.py but would need a good explanation/example for people to follow.

Back to the grindstone...

from dragino.

pjb304 avatar pjb304 commented on August 16, 2024

The LoRaWAN specs include the details of how long a device should wait for messages after transmit (class A), and to listen all the time (class C), I've not looked into the channel assignment for downlink - I think it works differently to uplink.

I believe that once cached OTAA credentials ahould not expire and so can be re-used for as long as needed, although I don't have time to find the reference for that now. In my testing with v3 the OTAA has seemed more solid than ABP which would often just get dropped.

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

Yes of course. I forgot about rx1 and rx2 though rx2 is a different frequency to rx1 iirc.

from dragino.

BNNorman avatar BNNorman commented on August 16, 2024

It's working now. A bit of tidying up and I'll be ready to push/pull.

from dragino.

pjb304 avatar pjb304 commented on August 16, 2024

The original request for downlink support is has been implemented by #11 and is now in release v0.0.06 (https://github.com/computenodes/dragino/releases/tag/v0.0.6)

I've found blog posts from TTN from a few years ago saying that OTAA sessions will don't expire https://www.thethingsnetwork.org/forum/t/lorawan-session-termination-expiration/3556 The most recent documentation I've found https://www.thethingsindustries.com/docs/devices/abp-vs-otaa/ says that the OTAA credentials can be cached and then used at a later date.

The best practice would be to occasionally send uplinks with Acks and after X messages where you don't get an Ack back perform a rejoin, but that's a subject for another issue/discussion.

As the requested down link functionality is now in the code I'll close this one.

from dragino.

Related Issues (19)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.