示例#1
0
def discover_devices_hcitool(timeout=5):
    """Discover Bluetooth Low Energy Devices nearby on Linux

    Using ``hcitool`` from Bluez in subprocess, which requires root privileges.
    However, ``hcitool`` can be allowed to do scan without elevated permission.

    Install linux capabilities manipulation tools:

    .. code-block:: bash

        $ sudo apt-get install libcap2-bin

    Sets the missing capabilities on the executable quite like the setuid bit:

    .. code-block:: bash

        $ sudo setcap 'cap_net_raw,cap_net_admin+eip' `which hcitool`

    **References:**

    * `StackExchange, hcitool without sudo <https://unix.stackexchange.com/questions/96106/bluetooth-le-scan-as-non-root>`_
    * `StackOverflow, hcitool lescan with timeout <https://stackoverflow.com/questions/26874829/hcitool-lescan-will-not-print-in-real-time-to-a-file>`_

    :param int timeout: Duration of scanning.
    :return: List of tuples with `(address, name)`.
    :rtype: list

    """
    p = subprocess.Popen(["hcitool", "lescan"],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    time.sleep(timeout)
    os.kill(p.pid, signal.SIGINT)
    out, err = p.communicate()
    if len(out) == 0 and len(err) > 0:
        if err == b'Set scan parameters failed: Operation not permitted\n':
            raise PyMetaWearException("Missing capabilites for hcitool!")
        if err == b'Set scan parameters failed: Input/output error\n':
            raise PyMetaWearException("Could not perform scan.")
    ble_devices = list(
        set([
            tuple(x.split(' '))
            for x in filter(None,
                            out.decode('utf8').split('\n')[1:])
        ]))
    filtered_devices = {}
    for d in ble_devices:
        if d[0] not in filtered_devices:
            filtered_devices[d[0]] = d[1]
        else:
            if filtered_devices.get(d[0]) == '(unknown)':
                filtered_devices[d[0]] = d[1]

    return [(k, v) for k, v in filtered_devices.items()]
示例#2
0
    def notifications(self, callback=None):
        """No subscriptions possible for LED module.

        :raises: :py:exc:`~PyMetaWearException`

        """
        raise PyMetaWearException("No notifications available for LED module.")
示例#3
0
    def notifications(self, callback=None):
        """No subscriptions possible for Haptic module.

        :raises: :py:exc:`~PyMetaWearException`

        """
        raise PyMetaWearException("Haptic module has no notifications.")
示例#4
0
    def notifications(self, callback=None):
        """Toggle notifications/subscriptions to data signals
        on the MetaWear board.

        :param callable callback: The function to call when
            data signal notification arrives. If ``None``, an
            unsubscription to notifications is sent.

        """
        data_signal = self.data_signal
        if callback is not None:
            if self._debug:
                log.debug("Subscribing to {0} changes. (Sig#: {1})".format(
                    self.module_name, data_signal))
            if self.callback is not None:
                raise PyMetaWearException(
                    "Subscription to {0} signal already in place!")
            self.callback = (callback, Fn_DataPtr(callback))
            libmetawear.mbl_mw_datasignal_subscribe(data_signal,
                                                    self.callback[1])
        else:
            if self._debug:
                log.debug("Unsubscribing to {0} changes. (Sig#: {1})".format(
                    self.module_name, data_signal))
            if self.callback is None:
                return
            libmetawear.mbl_mw_datasignal_unsubscribe(data_signal)
            self.callback = None
示例#5
0
 def wrapper(data):
     if data.contents.type_id == DataTypeId.CARTESIAN_FLOAT:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value, POINTER(CartesianFloat))
         func((epoch, (data_ptr.contents.x,
                       data_ptr.contents.y,
                       data_ptr.contents.z)))
     elif data.contents.type_id == DataTypeId.QUATERNION:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value, POINTER(Quaternion))
         func((epoch, (data_ptr.contents.w,
                       data_ptr.contents.x,
                       data_ptr.contents.y,
                       data_ptr.contents.z)))
     elif data.contents.type_id == DataTypeId.CORRECTED_CARTESIAN_FLOAT:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value,
                         POINTER(CorrectedCartesianFloat))
         func((epoch, (data_ptr.contents.x,
                       data_ptr.contents.y,
                       data_ptr.contents.z,
                       data_ptr.contents.accuracy)))
     elif data.contents.type_id == DataTypeId.EULER_ANGLES:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value, POINTER(EulerAngle))
         func((epoch, (data_ptr.contents.heading,
                       data_ptr.contents.pitch,
                       data_ptr.contents.roll,
                       data_ptr.contents.yaw)))
     else:
         raise PyMetaWearException('Incorrect data type id: {0}'.format(
             data.contents.type_id))
示例#6
0
 def wrapper(data):
     if data.contents.type_id == DataTypeId.FLOAT:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value, POINTER(c_float))
         func((epoch, data_ptr.contents.value))
     else:
         raise PyMetaWearException('Incorrect data type id: {0}'.format(
             data.contents.type_id))
示例#7
0
    def __init__(self,
                 address,
                 backend='pygatt',
                 interface='hci0',
                 timeout=None,
                 connect=True,
                 debug=False):
        """Constructor."""
        self._address = address
        self._debug = debug
        self._initialized = False

        if self._debug:
            add_stream_logger()
            log.info("Creating MetaWearClient for {0}...".format(address))

        # Handling of timeout.
        if timeout is None:
            timeout = os.environ.get('PYMETAWEAR_TIMEOUT', None)
            if timeout is not None:
                try:
                    timeout = float(timeout)
                except:
                    timeout = None

        if backend == 'pygatt':
            self._backend = PyGattBackend(self._address,
                                          interface=interface,
                                          timeout=timeout,
                                          debug=debug)
        elif backend == 'pybluez':
            self._backend = PyBluezBackend(self._address,
                                           interface=interface,
                                           timeout=timeout,
                                           debug=debug)
        else:
            raise PyMetaWearException("Unknown backend: {0}".format(backend))

        log.info(
            "Backend starter with {0} for device address {1} with timeout {2}..."
            .format(backend, address, timeout))
        self.firmware_version = None
        self.model_version = None
        self.accelerometer = None
        #self.gpio = None
        self.gyroscope = None
        self.magnetometer = None
        self.barometer = None
        self.ambient_light = None
        self.switch = None
        self.settings = None
        self.temperature = None
        self.haptic = None
        self.led = None
        self.sensorfusion = None

        if connect:
            self.connect()
示例#8
0
    def data_signal(self):
        """Returns the data signal pointer value for the switch module.

        :returns: The pointer value. (Long if on x64 architecture.)
        :rtype: :py:class:`ctypes.c_long` or :py:class:`ctypes.c_int`

        """
        raise PyMetaWearException(
            "No data signal exists for {0} module.".format(self))
示例#9
0
 def wrapper(data):
     if data.contents.type_id == DataTypeId.CARTESIAN_FLOAT:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value, POINTER(CartesianFloat))
         func((epoch, (data_ptr.contents.x, data_ptr.contents.y,
                       data_ptr.contents.z)))
     else:
         raise PyMetaWearException('Incorrect data type id: {0}'.format(
             data.contents.type_id))
示例#10
0
 def wrapper(data):
     if data.contents.type_id == DataTypeId.BATTERY_STATE:
         epoch = int(data.contents.epoch)
         data_ptr = cast(data.contents.value, POINTER(BatteryState))
         func((epoch, (int(data_ptr.contents.voltage),
                       int(data_ptr.contents.charge))))
     else:
         raise PyMetaWearException('Incorrect data type id: {0}'.format(
             data.contents.type_id))
示例#11
0
    def set_sample_delay(self, data_source, delay=None, differential=False):
        """
        Change the delay between samples using the onboard time processor
        module to change the effective sampling rate of a specific data source.

        :param data_source: A data source from sensor.SensorFusion
        :param delay: The delay in ms between samples,
            or None to reset to default
        :param differential: Set Time Preprocessor mode to differential,
            instead of the default, absolute
        """

        global current_processor
        global waiting_for_processor

        if self._data_source_signals[data_source] is None:
            log.debug("Getting data signal for data source {0}".format(
                data_source
            ))
            self._data_source_signals[data_source] = \
                libmetawear.mbl_mw_sensor_fusion_get_data_signal(
                    self.board, data_source
                )

        if delay is not None:
            mode = processor.Time.MODE_DIFFERENTIAL if differential else \
                processor.Time.MODE_ABSOLUTE
            waiting_for_processor = True
            log.debug("Creating time dataprocessor for signal {0}".format(
                self._data_source_signals[data_source]
            ))
            libmetawear.mbl_mw_dataprocessor_time_create(
                self._data_source_signals[data_source],
                mode,
                delay,
                Fn_VoidPtr(processor_set))

            wait_time = 0
            while waiting_for_processor and wait_time < max_processor_wait_time:
                sleeptime = 0.1
                time.sleep(sleeptime)
                wait_time += sleeptime
            if current_processor is not None:
                self._data_source_signals[data_source] = current_processor
                current_processor = None
            else:
                raise PyMetaWearException("Can't set data processor!")

        else:
            data_signal = libmetawear.mbl_mw_sensor_fusion_get_data_signal(
                    self.board, data_source)
            if self._data_source_signals[data_source] != data_signal:
                libmetawear.mbl_mw_dataprocessor_remove(
                    self._data_source_signals[data_source]
                )
                self._data_source_signals[data_source] = data_signal
示例#12
0
def _callback(data):
    """Handle a (x,y,z) gyroscope tuple."""
    if data.contents.type_id == DataTypeId.CARTESIAN_FLOAT:
        data_ptr = cast(data.contents.value, POINTER(CartesianFloat))
        print("X: {0}, Y: {1}, Z: {2}".format(*(data_ptr.contents.x,
                                     data_ptr.contents.y,
                                     data_ptr.contents.z)))
    else:
        raise PyMetaWearException('Incorrect data type id: ' +
            str(data.contents.type_id))
示例#13
0
    def set_sample_delay(self, data_source, delay=None, differential=False):
        """
        Change the delay between samples using the onboard time processor
        module to change the effective sampling rate of a specific data source.

        :param data_source: A data source from sensor.SensorFusion
        :param delay: The delay in ms between samples,
            or None to reset to default
        :param differential: Set Time Preprocessor mode to differential,
            instead of the default, absolute
        """
        if self._data_source_signals[data_source] is None:
            log.debug("Getting data signal for data source {0}".format(
                data_source
            ))
            self._data_source_signals[data_source] = \
                libmetawear.mbl_mw_sensor_fusion_get_data_signal(
                    self.board, data_source
                )

        if delay is not None:
            mode = TimeMode.DIFFERENTIAL if differential else \
                TimeMode.ABSOLUTE
            log.debug("Creating time dataprocessor for signal {0}".format(
                self._data_source_signals[data_source]
            ))
            _done = Event()

            def _processor_set(processor):
                """
                Set global variables as the libmetawear callback can't handle the self
                parameter of instance methods.

                :param processor: The processor that was created
                """
                self._data_source_signals[data_source] = processor
                _done.set()

            libmetawear.mbl_mw_dataprocessor_time_create(
                self._data_source_signals[data_source],
                mode,
                delay,
                FnVoid_VoidP(_processor_set))
            _done.wait(timeout=PROCESSOR_SET_WAIT_TIME)

            if self._data_source_signals[data_source] is None:
                raise PyMetaWearException("Can't set data processor!")
        else:
            data_signal = libmetawear.mbl_mw_sensor_fusion_get_data_signal(
                    self.board, data_source)
            if self._data_source_signals[data_source] != data_signal:
                libmetawear.mbl_mw_dataprocessor_remove(
                    self._data_source_signals[data_source]
                )
                self._data_source_signals[data_source] = data_signal
示例#14
0
    def handle_notify_char_output(self, handle, value):

        self._log("Notify", handle, value, 0)

        if handle == self._notify_char_handle:
            sb = self._response_2_string_buffer(value)
            libmetawear.mbl_mw_metawearboard_notify_char_changed(
                self.board, sb.raw, len(sb.raw))
        else:
            raise PyMetaWearException(
                "Notification on unexpected handle: {0}".format(handle))
示例#15
0
    def handle_notify_char_output(self, handle, value):

        self._log("Notify", handle, value, 0)

        if handle == self._notify_char_handle:
            sb_temp = self._response_2_string_buffer(value)
            sb = (ctypes.c_ubyte * len(sb_temp)).from_buffer_copy(sb_temp)
            libmetawear.mbl_mw_metawearboard_notify_char_changed(
                self.board, sb, len(sb))
        else:
            raise PyMetaWearException(
                "Notification on unexpected handle: {0}".format(handle))
示例#16
0
    def start_logging(self):
        """Setup and start logging of data signals on the MetaWear board"""
        data_signal = self.data_signal
        if getattr(self, 'high_frequency_stream', False):
            raise PyMetaWearException(
                "Cannot log on high frequency stream signal.")
        self._logger_ready_event = Event()
        logger_ready = FnVoid_VoidP_VoidP(context_callback(self._logger_ready))
        libmetawear.mbl_mw_datasignal_log(self.data_signal, None, logger_ready)
        self._logger_ready_event.wait()
        if self._logger_address is None:
            raise PyMetaWearException(
                'Failed to start logging for {0} module!'.format(
                    self.module_name))

        log.debug("Start Logger (Logger#: {0}, Signal#: {1})".format(
            self._logger_address, data_signal))

        self._logger_running = True
        self._download_done = False
        libmetawear.mbl_mw_logging_start(self.board, 0)
        self.toggle_sampling(True)
        self.start()
示例#17
0
    def __init__(self, address, interface=None, timeout=None, debug=False):
        if _import_failure is not None:
            raise PyMetaWearException(
                "pybluez[ble] package error: {0}".format(_import_failure))
        self.name = 'pybluez/gattlib'
        self._primary_services = {}
        self._characteristics_cache = {}
        self._response = GATTResponse()
        if debug:
            log.setLevel(logging.DEBUG)

        super(PyBluezBackend,
              self).__init__(address, interface,
                             10.0 if timeout is None else timeout, debug)
示例#18
0
文件: main.py 项目: takotab/nano4run
def log():
    client = MetaWearClient(str(address), debug=False)
    print("New client created: {0}".format(client))

    settings = client.accelerometer.get_possible_settings()
    print("Possible accelerometer settings of client:")
    for k, v in settings.items():
        print(k, v)

    print("Write accelerometer settings...")
    client.accelerometer.set_settings(data_rate=400, data_range=4.0)

    settings = client.accelerometer.get_current_settings()
    print("Accelerometer settings of client: {0}".format(settings))

    client.accelerometer.high_frequency_stream = False
    client.accelerometer.start_logging()
    print("Logging accelerometer data...")

    for i in range(5):
        time.sleep(1.0)
        print(i)

    client.accelerometer.stop_logging()
    print("Logging stopped.")

    print("Downloading data...")
    download_done = False
    n = 0
    data = None
    while (not download_done) and n < 3:
        try:
            data = client.accelerometer.download_log()
            download_done = True
        except PyMetaWearDownloadTimeout:
            print("Download of log interrupted. Trying to reconnect...")
            client.disconnect()
            client.connect()
            n += 1
    if data is None:
        raise PyMetaWearException("Download of logging data failed.")

    print("Disconnecting...")
    client.disconnect()

    with open('data.txt', 'w') as f:
        for d in data:
            f.write(str(d) + "\n")
示例#19
0
    def __init__(self, address, interface=None, timeout=None, debug=False):
        if _import_failure is not None:
            raise PyMetaWearException(
                "pygatt[GATTTOOL] package error: {0}".format(_import_failure))

        self.name = 'pygatt'

        log.info("PyGattBackend: Creating new GATTToolBackend and starting GATTtool process...")
        self._backend = None

        if debug:
            log.setLevel(logging.DEBUG)

        super(PyGattBackend, self).__init__(
            address, interface,
            gatttool.DEFAULT_CONNECT_TIMEOUT_S if timeout is None else timeout, debug)
示例#20
0
    def get_handle(self, characteristic_uuid, notify_handle=False):
        """Get handle for a characteristic UUID.

        :param uuid.UUID characteristic_uuid: The UUID for the characteristic to look up.
        :param bool notify_handle:
        :return: The handle for this UUID.
        :rtype: int

        """
        if isinstance(characteristic_uuid, string_types):
            characteristic_uuid = characteristic_uuid.UUID(characteristic_uuid)
        handle = self._characteristics_cache.get(
            characteristic_uuid, [None, None])[int(notify_handle)]
        if handle is None:
            raise PyMetaWearException("Incorrect characteristic.")
        else:
            return handle
示例#21
0
 def check_and_change_callback(self, data_signal, callback):
     if callback is not None:
         if self._debug:
             log.debug("Subscribing to {0} changes. (Sig#: {1})".format(
                 self.module_name, data_signal))
         if data_signal in self._callbacks:
             raise PyMetaWearException(
                 "Subscription to {0} signal already in place!")
         self._callbacks[data_signal] = (callback, Fn_DataPtr(callback))
         libmetawear.mbl_mw_datasignal_subscribe(
             data_signal, self._callbacks[data_signal][1])
     else:
         if data_signal not in self._callbacks:
             return
         if self._debug:
             log.debug("Unsubscribing to {0} changes. (Sig#: {1})".format(
                 self.module_name, data_signal))
         libmetawear.mbl_mw_datasignal_unsubscribe(data_signal)
         del self._callbacks[data_signal]
示例#22
0
    def connect(self, clean_connect=True):
        """Connect this client to the MetaWear device.

        :param bool clean_connect: If old backend components should be replaced.
            Default is ``False``.

        """
        if self.backend.is_connected:
            return
        self.backend.connect(clean_connect=clean_connect)

        self.deserialize()

        if self._debug:
            log.debug("Waiting for MetaWear board to be fully initialized...")

        while not self.backend.initialized:
            self.backend.sleep(0.1)

        # Check if initialization has been completed successfully.
        if self.backend.initialization_status != Const.STATUS_OK:
            if self.backend.initialization_status == Const.STATUS_ERROR_TIMEOUT:
                raise PyMetaWearConnectionTimeout(
                    "libmetawear initialization status 16: Timeout")
            else:
                raise PyMetaWearException(
                    "libmetawear initialization status {0}".format(
                        self.backend.initialization_status))

                self.serialize()
        # Read out firmware and model version.
        self.firmware_version_str = self.backend.read_gatt_char_by_uuid(
            specs.DEV_INFO_FIRMWARE_CHAR[1]).decode()
        self.firmware_version = tuple(
            [int(x) for x in self.firmware_version_str.split('.')])
        self.model_version = int(
            self.backend.read_gatt_char_by_uuid(
                specs.DEV_INFO_MODEL_CHAR[1]).decode())
        self.model_name = _model_names[
            libmetawear.mbl_mw_metawearboard_get_model(self.board) + 1]

        self._initialize_modules()
示例#23
0
 def wrapper(*args, **kwargs):
     if getattr(args[0], 'gyro_r_class', None) is None:
         raise PyMetaWearException("There is not Gyroscope "
                                   "module of your MetaWear board!")
     return f(*args, **kwargs)
示例#24
0
 def wrapper(*args, **kwargs):
     if getattr(args[0], 'available', False) is False:
         raise PyMetaWearException("There is no Sensor Fusion "
                                   "module on your MetaWear board!")
     return f(*args, **kwargs)
示例#25
0
print("Downloading data...")
download_done = False
n = 0
data = None
while (not download_done) and n < 3:
    try:
        data = c.sensorfusion.download_log()
        download_done = True
    except PyMetaWearDownloadTimeout:
        print("Download of log interrupted. Trying to reconnect...")
        c.disconnect()
        c.connect()
        n += 1
if data is None:
    raise PyMetaWearException("Download of logging data failed.")

for d in data:
    print(d)
    #v = d['value']



# print(str(v))
# dat = str(v)
# print(type(dat))
# #print(dat[4:10])
# x = float(dat[4:10])
# print(x)
# print(type(x))
# #print(dat[15:22])
示例#26
0
 def wrapper(*args, **kwargs):
     if getattr(args[0], 'mag_class', None) is None:
         raise PyMetaWearException("There is not Magnetometer "
                                   "module on your MetaWear board!")
     return f(*args, **kwargs)
示例#27
0
 def set_settings(self, **kwargs):
     raise PyMetaWearException(
         "No settings exists for {0} module.".format(self))
示例#28
0
 def wrapper(*args, **kwargs):
     if getattr(args[0], 'ambient_light_gain_class', None) is None:
         raise PyMetaWearException("There is no Ambient Light "
                                   "module on your MetaWear board!")
     return f(*args, **kwargs)