Beispiel #1
0
 def cr(self, val):
     ''' set coding rate '''
     if (val not in __class__.LORA_CODING_RATE):
         log.warning("invalid coding rate '%s', ought to be one of %s" %
                     (val, str(__class__.LORA_CODING_RATE)))
         return
     self._command("radio set cr " + val, True)
Beispiel #2
0
 def crc(self, val):
     ''' set crc parameter in ['on','off'] '''
     if (val not in __class__.LORA_ON_OFF):
         log.warning("invalid CRC parameter '%s', ought to be one of %s" %
                     (val, str(__class__.LORA_ON_OFF)))
         return
     self._command("radio set crc " + val, True)
Beispiel #3
0
 def modulation(self, val):
     ''' set modulation mode in ['lora','fsk'] '''
     if (val not in __class__.LORA_RADIO_MODULATION):
         log.warning(
             "invalid radio modulation '%s', ought to be one of %s" %
             (val, str(__class__.LORA_RADIO_MODULATION)))
         return
     self._command("radio set mod " + val, True)
Beispiel #4
0
 def sf(self, val):
     ''' set spreading factor '''
     if (val not in __class__.LORA_SPREAD_FACTOR):
         log.warning(
             "invalid spreading factor '%s', ought to be one of %s" %
             (val, str(__class__.LORA_SPREAD_FACTOR)))
         return
     self._command("radio set sf " + val, True)
Beispiel #5
0
 def wdt(self, val):
     ''' set rx watchdog '''
     try:
         if (0 <= int(val) <= __class__.LORA_RX_WATCHDOG):
             self._command("radio set wdt " + str(val), True)
             return
         log.warning("out of limits rx watchdof '%s' ?!?!" % str(val))
     except Exception as ex:
         log.error("error in watchdog timeout value '%s' ?!?!" % str(val))
         raise ex
Beispiel #6
0
 def pwr(self, val):
     ''' set TX power '''
     try:
         if (int(val) not in __class__.LORA_RADIO_TX_POWER):
             log.warning("invalid radio tx pwer provided")
             return
     except ValueError as ex:
         log.error("tx PWR '%s' ought to be an integer ... aborting" %
                   (str(val)))
         raise ex
     self._command("radio set pwr " + str(val), True)
Beispiel #7
0
 def freq(self, val):
     ''' set radio frequency '''
     try:
         for f in __class__.LORA_RADIO_FREQUENCIES:
             if (int(val) == int(f * 1000000)):
                 self._command("radio set freq " + str(val), True)
                 return
         log.warning("unknwon frequency '%s' ?!?!" % str(val))
     except Exception as ex:
         log.error("error in frequency value '%s' ?!?!" % str(val))
         raise ex
Beispiel #8
0
 def classDevice(self, val):
     ''' set end-device class '''
     if (str(val)[0] not in __class__.LORA_DEVICES_CLASSES):
         log.warning(
             "invalid end-device class '%c', ought to be one of %s" %
             (str(val)[0], str(__class__.LORA_DEVICES_CLASSES)))
         return
     if (self._is_fwrev_ge('1.0.5')):
         self._command("mac set class " + str(val)[0], True)
     else:
         log.warning("device FW_rev '%s' is < to '1.0.5'" %
                     str(self._fw_rev))
Beispiel #9
0
 def bw(self, val):
     ''' set bandwidth '''
     try:
         for _bw in __class__.LORA_BANDWIDTH:
             if (int(val) == _bw):
                 self._command("radio set bw " + str(val), True)
                 return
         log.warning("unknwon bandwidth '%s', ought to be one of %s" %
                     (str(val), str(__class__.LORA_BANDWIDTH)))
     except Exception as ex:
         log.error("error in bandwidth value '%s' ?!?!" % str(val))
         raise ex
Beispiel #10
0
 def loraSleep(self, val):
     ''' activate lora system sleep '''
     try:
         if (__class__.LORA_SYS_SLEEP[0] <= int(val) <=
                 __class__.LORA_SYS_SLEEP[1]):
             log.info("LoRa module will enter in sleep mode for %dms ..." %
                      int(val))
             self._command("sys sleep " +
                           str(val))  # respond 'ok' after wakeup
             return
         log.warning("out of range sleep value '%s' !" % str(val))
     except Exception as ex:
         log.error("invalid sleep timeout (ms) '%s' !" % str(val))
Beispiel #11
0
    def start_serial(self):
        ''' verify serial link is valid ... and a RN2483 is connected to '''
        if (self._link is not None):
            log.warning("# serial link is already active !")
            return

        try:
            # warning: port is immediately opened on link creation
            log.info("Initialise serial '%s' at speed '%d' ..." %
                     (self.serial_port, self.serial_speed))
            self._link = serial.Serial(
                port=self.serial_port,
                baudrate=self.serial_speed,
                bytesize=__class__.DEFAULT_SERIAL_BITS,
                parity=__class__.DEFAULT_SERIAL_PARITY,
                stopbits=__class__.DEFAULT_SERIAL_STOP,
                timeout=__class__.DEFAULT_SERIAL_TIMEOUT,
                exclusive=True)
            # hardware reset of the radio module
            self.radioModuleHWreset()

            # on reset, RN2483 will send its firmware release
            # TODO: launch thread waiting for serial data
            _answer = str()
            _answer = self._link.read_until().decode(
                "utf-8")  # serial timeout applies here
            if (len(_answer)):
                log.debug("[init] recveived str: %s" % (_answer))
                if (not ("rn2483" in _answer.lower())):
                    raise Exception(
                        "RN2483 module not found because answer was: " +
                        str(_answer))
                self._fw_rev = [
                    int(x) for x in _answer.lower().split()[1].split('.')
                ]
            else:
                raise Exception("Lora module didn't answered!")

        except ValueError as ex:
            log.error("Firmware revision number are not integers: '%s'" %
                      _answer.lower() + str(ex))
            raise ex
        except Exception as ex:
            log.error("while opening serial port '%s' with baudrate=%d " %
                      (self.serial_port, self.serial_speed) + str(ex))
            raise ex
        log.debug("link '%s' @ '%d'bauds is validated as a serial port :)" %
                  (self.serial_port, self.serial_speed))

        # activate locking mechanism
        self._serial_lock = threading.Lock()
Beispiel #12
0
    def _loraOTAAjoin(self, deveui=None, appeui=None, appkey=None):
        ''' join with OTAA protocol
            returns True, False or raise exception '''
        log.info("Start OTAA join procedure ...")
        if (deveui is not None): self.devEUI = deveui
        if (appeui is not None): self.appEUI = appeui
        if (appkey is not None): self.appKEY = appkey

        # try to join
        _retry = 5
        _sleep = 2
        while (self._shutdown_event.is_set() is not True and _retry > 0):
            _res = self._command(
                "mac join otaa"
            )  # check=false because we don't want an exception if answer differs from 'ok'
            print("(1) join res = %s" % _res)
            # check for acceptance of command
            if (_res != "ok"):
                log.info(
                    "[join][devEUI=0x%s, appEUI=0x%s, appKEY=0x%s] command failed: '%s'"
                    % (self.devEUI, self.appEUI, self.appKEY, _res))
                log.debug("... sleeping a bit before retrying join ...")
                time.sleep(_sleep)
                _sleep *= 2
                _retry -= 1
                continue

            # ... and now wating for the join result
            print("(2) join ...")
            _res = self.receiveData(timeout=10000)  # wait up to ten seconds
            print("(3) join _res = %s" % str(_res))
            if (_res is None or _res.endswith('\r\n') is not True):
                log.warning("# WARNING: partial retrieval from serial link !!")
                raise Eception("partial data from serial received :(")
            _res = _res.rstrip('\r\n')
            log.debug("\t received join answer: '%s'" % _res)
            if (_res == 'accepted'):
                log.info("\t JOIN successful :)")
                return True

            log.info(
                "[join][devEUI=0x%s, appEUI=0x%s, appKEY=0x%s] connection failed with code '%s'"
                % (self.devEUI, self.appEUI, self.appKEY, str(_res)))
            log.debug("... sleeping a bit before retrying join ...")
            time.sleep(_sleep)
            _sleep *= 2
            _retry -= 1

        return False
Beispiel #13
0
 def batlvl(self, val):
     ''' set battery-level '''
     if (val is None):
         self._batlvl = None
         return
     try:
         if (int(val) in __class__.LORA_BAT_LVL_RNG):
             _res = self._command("mac set bat %d" % val, True)
             if (_res is None or _res != 'ok'):
                 raise Exception("error while setting battery level!")
             return
         log.warning("out-of-range battery-level '%s'" % str(val))
     except Exception as ex:
         log.error("error in battery-level value '%s' ?!?!" % str(val))
         raise ex
Beispiel #14
0
def main():

    # Global variables
    global _shutdownEvent

    #
    print("\n###\nLoRaWAN powermeter demo based on RN2483")
    print("###")

    # create threading.event
    _shutdownEvent = threading.Event()

    # Trap CTRL+C (kill -2)
    signal.signal(signal.SIGINT, ctrlc_handler)

    # Parse arguments
    parser = argparse.ArgumentParser(
        description="LoRaWAN powermeter based on RN2483 \
                         \n Hit <ENTER> to terminate program.")

    parser.add_argument('-p',
                        '--port',
                        type=str,
                        dest="serial_link",
                        nargs='?',
                        help="Serial port to use, eg. /dev/ttyAMA0.")

    parser.add_argument('-s',
                        '--speed',
                        type=int,
                        dest="serial_link_speed",
                        nargs='?',
                        help="Absolute path to labels file.")

    parser.add_argument('--reset-pin',
                        type=int,
                        dest="reset_pin",
                        nargs='?',
                        help="RPi's GPIO connected to RST pin of RN2483.")

    parser.add_argument(
        '--set-dcycle',
        type=int,
        dest="duty_cycle",
        nargs='?',
        help="Set duty cycle percent from 0 to 100 on all channels.")

    # debug mode
    parser.add_argument('-d',
                        '--debug',
                        action="store_true",
                        help="Toggle debug mode (True as default).")

    ARGS = parser.parse_args()
    #print(ARGS)

    # logging
    if (ARGS.debug is True):
        print("\n[DBG] DEBUG mode activated ... ")
        setLogLevel(logging.DEBUG)
    else:
        print("\n[LOG] level set to %d" % int(settings.log_level))
        setLogLevel(settings.log_level)

    # Setup GPIOs
    GPIO.setmode(GPIO.BCM)
    '''
    #
    # Modbus initialisation
    log.info("Instantiate and enable modbus backend ...")
    try:
        kwargs = dict()
        kwargs['shutdown_event'] = _shutdownEvent
        if( hasattr(settings, 'modbus_debug') is True ):
            kwargs['modbus_debug'] = settings.modbus_debug
        _backend = ModbusBackend(settings.modbus_link, settings.modbus_link_speed, **kwargs)
        _backend.enable()
    except Exception as ex:
        log.error("unable to initialise Modbus backend: " + str(ex) )
        raise ex
    time.sleep(1)
    '''

    #
    # LoRaWAN
    #
    # RN2483 serial initialisation
    log.info("Starting RN2483 initialisation ...")
    try:
        # instantiate device with optional specific configuration
        _deviceConfig = dict()
        # register shurdownEvent
        _deviceConfig['shutdown_event'] = _shutdownEvent
        # enable/disable ADR mode
        _deviceConfig['adr'] = True
        if (hasattr(settings, 'disable_adr') is True):
            if settings.disable_adr is True:
                _deviceConfig['adr'] = False
        # set data exchange DataRate mode 0:SF12/125kHz to 5:SF7/125kHz
        _deviceConfig['dr'] = 0  # SF12/125kHz
        if (hasattr(settings, 'data_rate') is True):
            _deviceConfig['dr'] = settings.data_rate
        # set data exchange DataRate spreading factor (default is SF12)
        if (hasattr(settings, 'data_sf') is True):
            _deviceConfig['sf'] = settings.data_sf
        # optional reset pin
        if (ARGS.reset_pin is not None):
            _deviceConfig['reset_pin'] = ARGS.reset_pin
        elif (hasattr(settings, 'reset_pin') is True):
            _deviceConfig['reset_pin'] = settings.reset_pin
        # optional duty cycle
        if (ARGS.duty_cycle is not None):
            _deviceConfig['duty_cycle'] = ARGS.duty_cycle
        elif (hasattr(settings, 'duty_cycle') is True):
            _deviceConfig['duty_cycle'] = settings.duty_cycle

        #
        # instantiate LoRaWAN device
        device = RN2483(
            ARGS.serial_link
            if ARGS.serial_link is not None else settings.serial_link,
            ARGS.serial_link_speed if ARGS.serial_link_speed is not None else
            settings.serial_link_speed, **_deviceConfig)

        # tell we're on external power supply
        device.batlvl = 0

    except Exception as ex:
        log.error("### ERROR on RN2483 instantiation: " + str(ex))
        raise ex
    log.info("RN2483 successfully instantiated :)")

    # RN2483 LoRaWAN initialisation
    log.info("Starting LoRaWAN initialisation ...")
    try:
        # [RADIO] LoRa parameters
        #device.mod      = 'lora'    # default modulation: LoRa
        #device.freq     = int(868.1*1000000)  # default to 868.1MHz
        device.pwr = 14
        #device.sf       = 'sf7'     # default sf12
        #device.cr       = '4/8'     # default 4/5
        #device.bw       = 250       # default 125kHz
        #device.crc      = 'off'     # default on

        # [MAC] LoRaWAN parameters
        device.devEUI = settings.deveui
        device.appEUI = settings.appeui
        device.appKEY = settings.appkey

        # save parameters
        #device.loraMacSave()

    except Exception as ex:
        log.error("### LoRaWAN initialization error: " + str(ex))
        raise ex
    log.info("LoRaWAN setup successful :)")
    print(device)
    time.sleep(2)

    #
    # main loop
    txData = None

    while (not _shutdownEvent.is_set()):
        try:
            # need to join ?
            if (device.isConnected() is not True):
                # activate OTAA join
                device.connect(mode='otaa')
                # are we connected ?
                if (device.isConnected() is not True):
                    time.sleep(5)
                    continue
                # print device detailed status
                print(device)
                log.debug("sleeping a bit before continuing ...")
                time.sleep(5)

            _startTime = datetime.now()

            #
            # TX some data
            # TODO: make use of optional port :)
            log.info("Sending some data ...")

            txData = "hello from RN2483!"
            print("MyData = %s" % str(txData))

            #
            # TX messages with or without acknowledge
            # Note: if 1% duty-cycle is disabled, best is to ask for acknowledge
            _ask4cnf = True
            if (hasattr(settings, 'ask_cnf') is True):
                _ask4cnf = settings.ask_cnf
            if (device.transmitData(str(txData), ack=_ask4cnf) is not True):
                log.debug("tx command not accepted by LoRaWAN stack :(")
                log.debug("... sleeping a bit before retrying ...")
                time.sleep(7)
                continue

            _txAnswer = None
            # LoRaWAN Class A | wait for some data in return from previous transmit
            log.info("Waiting for some data ...")
            while ((datetime.now() - _startTime).total_seconds() <
                   settings.loraTimer and not _shutdownEvent.is_set()):
                print("_*_", end='', flush=True)
                rxData = device.receiveData()
                if (rxData is None): continue
                if (not rxData.endswith('\r\n')):
                    log.warning("Partial data received !!\n\t'%s'" %
                                str(rxData))
                    time.sleep(1)
                    continue
                rxData = rxData.rstrip('\r\n')
                if (_txAnswer is not None):
                    # this RX1 or RX2 or class C device ?
                    log.info("Received data = '%s'" % str(rxData))
                    time.sleep(1)
                    continue
                # TX answer received
                log.info("TX answer received: '%s'" % rxData)
                if (rxData == 'mac_tx_ok'):
                    _txAnswer = rxData
                    continue
                elif (rxData.startswith('mac_rx')):
                    # RX1 or RX2 slots received data

                    _rxFields = rxData.split()
                    print("\n\tPort[%d] answer received: '%s'" %
                          (int(_rxFields[1]), str(_rxFields[2:])))

                    # TODO: process incomming data

                    print("\tTODO: process incoming data!")
                    _txAnswer = rxData
                    time.sleep(1)
                    continue
                elif (rxData == 'mac_err'):
                    # failure to send
                    log.warning(
                        "'mac_err' ... failed to transmit data ... mac status = 0x%08X ... try to resend :|"
                        % (device.macStatus))
                    time.sleep(4)
                    break
                elif (rxData.startswith('invalid_data_len')):
                    # payload too large
                    log.warning(
                        "oversized payload ... (ADR mode??) ... try to resend :|"
                    )
                    time.sleep(4)
                    break
                elif (rxData.startswith('invalid')):
                    # [aug.19] let's consider this as a warning ...
                    log.warning(
                        "there's something wrong in the TX process ... ... mac status = 0x%08X ... try to resend"
                        % (device.macStatus))
                    time.sleep(4)
                else:
                    log.warning("unknwown TX answer '%s' ?!?! ... continuing" %
                                rxData)
                    time.sleep(1)

        except Exception as ex:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            log.error("exception '%s'(line %d): " %
                      (exc_type, exc_tb.tb_lineno) + str(ex))
            time.sleep(3)
            continue

    # destroy instance
    log.info("ending LoRaWAN ops ...")
    del (device)

    # GPIO cleanup
    GPIO.cleanup()
Beispiel #15
0
 def loraMacPause(self):
     ''' pause LoRaWAN stack to enable RADIO modifications when a join has
         already been activated '''
     log.warning("#WARNING: pausing LoRaWAN stack ...")
     self._command("mac pause")
Beispiel #16
0
 def appEUI(self, val):
     ''' set appEUI '''
     if (val is None):
         log.warning("unsupported appEUI '%s'" % str(val))
         return
     self._command("mac set appeui " + str(val), True)
Beispiel #17
0
 def appKEY(self, val):
     if (val is None):
         log.warning("unsupported appKEY '%s'" % str(val))
         return
     if (self._command("mac set appkey " + str(val), True) == 'ok'):
         self._appKEY = str(val)