diff --git a/Pipfile b/Pipfile index f8dd557..c941a19 100644 --- a/Pipfile +++ b/Pipfile @@ -17,3 +17,4 @@ tqdm = "*" pytest = "*" sphinx = "*" sphinx-rtd-theme = "*" +pylint = "*" diff --git a/docs/source/client.rst b/docs/source/client.rst index 2962469..b579f8d 100644 --- a/docs/source/client.rst +++ b/docs/source/client.rst @@ -10,7 +10,7 @@ The MetaWear client provided by this package. It can be used as such: from pymetawear.client import MetaWearClient c = MetaWearClient('DD:3A:7D:4D:56:F0') -The client can now be used for e.g. subscribing to data signals or logging data. +The client can now be used for either reading the current module data or activating some functionality in it. API --- diff --git a/docs/source/modules/accelerometer.rst b/docs/source/modules/accelerometer.rst index 3fb470c..ba52267 100644 --- a/docs/source/modules/accelerometer.rst +++ b/docs/source/modules/accelerometer.rst @@ -10,7 +10,10 @@ It is initialized at the creation of the :py:class:`~MetaWearClient` client and can then be accessed in the ``accelerometer`` attribute of the client. -Example usage: +Data streaming example +---------------------- + +If you need a real time stream of sensor data, use the :py:method:`notifications` method on the :py:mod:`accelerometer` module: .. code-block:: python @@ -28,6 +31,58 @@ Example usage: # Enable notifications and register a callback for them. c.accelerometer.notifications(acc_callback) +Logging example +--------------- + +If you want to log data to the MetaWear board and retrieve it after some time, then use the +:py:method:`start_logging`, :py:method:`stop_logging` and :py:method:`download_log` methods: + +.. code-block:: python + + import json + from pymetawear.client import MetaWearClient + from pymetawear.exceptions import PyMetaWearException, PyMetaWearDownloadTimeout + + c = MetaWearClient('DD:3A:7D:4D:56:F0') + + # Set data rate to 200 Hz and measuring range to +/- 8g + c.accelerometer.set_settings(data_rate=200.0, data_range=8) + + # Log data for 10 seconds. + client.accelerometer.start_logging() + print("Logging accelerometer data...") + + time.sleep(10.0) + + client.accelerometer.stop_logging() + print("Finished logging.") + + # Download the stored data from the MetaWear board. + print("Downloading data...") + download_done = False + n = 0 + data = None + while 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() + + # Save the logged data. + data_file = os.path.join(os.getcwd(), "logged_data.json") + print("Saving the data to file: {0}".format(data_file)) + with open("logged_data.json", "wt") as f: + json.dump(data, f, indent=2) + API --- diff --git a/docs/source/modules/ambientlight.rst b/docs/source/modules/ambientlight.rst new file mode 100644 index 0000000..2634f17 --- /dev/null +++ b/docs/source/modules/ambientlight.rst @@ -0,0 +1,39 @@ +.. _modules_accelerometer: + +Ambient light module +==================== + +The PyMetaWear implementation of the ``libmetawear`` +ambient light module. + +It is initialized at the creation of the :py:class:`~MetaWearClient` +client and can then be accessed in the ``ambient_light`` +attribute of the client. + +Data streaming example +---------------------- + +If you need a real time stream of sensor data, use the :py:method:`notifications` method on the :py:mod:`ambient_light` module: + +.. code-block:: python + + from pymetawear.client import MetaWearClient + + c = MetaWearClient('DD:3A:7D:4D:56:F0') + + print("Write ambient light settings...") + c.ambient_light.set_settings(gain=4, integration_time=200, measurement_rate=200) + + def ambient_light_callback(data): + """Handle a (epoch, data) ambient light data tuple.""" + print("Epoch time: [{0}] Data: {1}".format(data[0], data[1])) + + # Enable notifications and register a callback for them. + c.ambient_light.notifications(ambient_light_callback) + + +API +--- + +.. automodule:: pymetawear.modules.ambientlight + :members: diff --git a/docs/source/modules/gyroscope.rst b/docs/source/modules/gyroscope.rst index c5e7bf7..80a1114 100644 --- a/docs/source/modules/gyroscope.rst +++ b/docs/source/modules/gyroscope.rst @@ -12,7 +12,10 @@ attribute of the client. The only MetaWear gyroscope available is the BMI160 sensor. -Example usage: +Data streaming example +---------------------- + +If you need a real time stream of sensor data, use the :py:method:`notifications` method on the :py:mod:`gyroscope` module: .. code-block:: python @@ -30,6 +33,58 @@ Example usage: # Enable notifications and register a callback for them. c.gyroscope.notifications(gyro_callback) +Logging Example +--------------- + +If you want to log data to the MetaWear board and retrieve it after some time, then use the +:py:method:`start_logging`, :py:method:`stop_logging` and :py:method:`download_log` methods: + +.. code-block:: python + + import json + from pymetawear.client import MetaWearClient + from pymetawear.exceptions import PyMetaWearException, PyMetaWearDownloadTimeout + + c = MetaWearClient('DD:3A:7D:4D:56:F0') + + # Set data rate to 200 Hz and measuring range to +/- 1000 DPS + c.gyroscope.set_settings(data_rate=200.0, data_range=1000.0) + + # Log data for 10 seconds. + client.gyroscope.start_logging() + print("Logging gyroscope data...") + + time.sleep(10.0) + + client.gyroscope.stop_logging() + print("Finished logging.") + + # Download the stored data from the MetaWear board. + print("Downloading data...") + download_done = False + n = 0 + data = None + while download_done and n < 3: + try: + data = client.gyroscope.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() + + # Save the logged data. + data_file = os.path.join(os.getcwd(), "logged_data.json") + print("Saving the data to file: {0}".format(data_file)) + with open("logged_data.json", "wt") as f: + json.dump(data, f, indent=2) + API --- diff --git a/docs/source/modules/index.rst b/docs/source/modules/index.rst index bc6fa5e..752d3a7 100644 --- a/docs/source/modules/index.rst +++ b/docs/source/modules/index.rst @@ -12,8 +12,51 @@ MetaWear modules magnetometer barometer led + ambientlight settings haptic switch temperature sensor_fusion + + +There are two major modalities for obtaining data from sensors: subscribing to data from it or logging the data +to the MetaWear board for subsequent download. + +Streaming data +-------------- + +Streaming the data is a method of data extraction that is preferable if you have a need of the data in real-time, +e.g. for IMU navigation. It sends a epoch time tagged dictionary to a callback function specified by you, to process +as you see fit. + +Streaming is also the only option that allows for access to high frequency (>400 Hz) data for accelerometer and gyroscope. + +Modules supporting continuous data streaming: + +- :py:mod:`accelerometer` +- :py:mod:`gyroscope` +- :py:mod:`magnetometer` +- :py:mod:`barometer` +- :py:mod:`switch` +- :py:mod:`ambientlight` +- :py:mod:`sensor_fusion` + +Modules supporting notification protocol, but notifications are received by manually triggering them: + +- :py:mod:`temperature` +- :py:mod:`settings` (battery) + +Logging data +------------ + +If you are not dependent on having data delivered continuously but rather just need it saved for analysis later on, then +logging it to the board is a better choice. It reduces the potential for BLE disconnections during data recording, making +it a more stable means of ensuring that data is actually collected. + +Modules supporting logging data (at least with PyMetaWear implementation): + +- :py:mod:`accelerometer` +- :py:mod:`gyroscope` +- :py:mod:`magnetometer` +- :py:mod:`sensor_fusion` diff --git a/docs/source/modules/magnetometer.rst b/docs/source/modules/magnetometer.rst index 40103ff..3d20053 100644 --- a/docs/source/modules/magnetometer.rst +++ b/docs/source/modules/magnetometer.rst @@ -10,7 +10,10 @@ It is initialized at the creation of the :py:class:`~MetaWearClient` client and can then be accessed in the ``magnetometer`` attribute of the client. -Example usage: +Example streaming data +---------------------- + +If you need a real time stream of sensor data, use the :py:method:`notifications` method on the :py:mod:`magnetometer` module: .. code-block:: python @@ -30,6 +33,59 @@ Example usage: # Enable notifications and register a callback for them. c.magnetometer.notifications(magnetometer_callback) +Logging Example +--------------- + +If you want to log data to the MetaWear board and retrieve it after some time, then use the +:py:method:`start_logging`, :py:method:`stop_logging` and :py:method:`download_log` methods: + +.. code-block:: python + + import json + from pymetawear.client import MetaWearClient + from pymetawear.exceptions import PyMetaWearException, PyMetaWearDownloadTimeout + + c = MetaWearClient('DD:3A:7D:4D:56:F0') + + # Set Power preset to low power. + c.magnetometer.set_settings(power_preset='low_power') + + # Log data for 10 seconds. + client.magnetometer.start_logging() + print("Logging magnetometer data...") + + time.sleep(10.0) + + client.magnetometer.stop_logging() + print("Finished logging.") + + # Download the stored data from the MetaWear board. + print("Downloading data...") + download_done = False + n = 0 + data = None + while download_done and n < 3: + try: + data = client.magnetometer.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() + + # Save the logged data. + data_file = os.path.join(os.getcwd(), "logged_data.json") + print("Saving the data to file: {0}".format(data_file)) + with open("logged_data.json", "wt") as f: + json.dump(data, f, indent=2) + + API --- diff --git a/examples/accelerometer_logging.py b/examples/accelerometer_logging.py index 430c09d..96b9b24 100644 --- a/examples/accelerometer_logging.py +++ b/examples/accelerometer_logging.py @@ -62,13 +62,5 @@ for d in data: print(d) -pattern = client.led.load_preset_pattern('blink', repeat_count=10) -client.led.write_pattern(pattern, 'g') -client.led.play() - -time.sleep(5.0) - -client.led.stop_and_clear() - print("Disconnecting...") client.disconnect() diff --git a/examples/gyroscope_logging.py b/examples/gyroscope_logging.py index aea1146..b250367 100644 --- a/examples/gyroscope_logging.py +++ b/examples/gyroscope_logging.py @@ -49,13 +49,5 @@ for d in data: print(d) -pattern = client.led.load_preset_pattern('blink', repeat_count=10) -client.led.write_pattern(pattern, 'g') -client.led.play() - -time.sleep(5.0) - -client.led.stop_and_clear() - print("Disconnecting...") client.disconnect() diff --git a/examples/magnetometer.py b/examples/magnetometer.py index eecc691..fad5a32 100755 --- a/examples/magnetometer.py +++ b/examples/magnetometer.py @@ -29,7 +29,7 @@ time.sleep(1.0) -print("Write accelerometer settings...") +print("Write magnetometer settings...") c.magnetometer.set_settings(power_preset='REGULAR') time.sleep(1.0) diff --git a/examples/magnetometer_logging.py b/examples/magnetometer_logging.py new file mode 100644 index 0000000..baec07c --- /dev/null +++ b/examples/magnetometer_logging.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +:mod:'magnetometer_logging.py' +================== +Created by hbldh +Created on 2018-04-20 + +""" + +from __future__ import division +from __future__ import print_function +from __future__ import absolute_import + +import time + +from pymetawear.discover import select_device +from pymetawear.client import MetaWearClient + +address = select_device() + +client = MetaWearClient(str(address), debug=False) +print("New client created: {0}".format(client)) + +settings = client.magnetometer.get_possible_settings() +print("Possible magnetometer settings of client:") +for k, v in settings.items(): + print(k, v) + +print("Write magnetometer settings...") +c.magnetometer.set_settings(power_preset='REGULAR') + +settings = client.magnetometer.get_current_settings() +print("Magnetometer settings of client: {0}".format(settings)) + +time.sleep(0.2) + +client.magnetometer.high_frequency_stream = False +client.magnetometer.start_logging() +print("Logging magnetometer data...") + +time.sleep(3.0) + +client.magnetometer.stop_logging() +print("Logging stopped.") + +print("Downloading data...") +data = client.magnetometer.download_log() +for d in data: + print(d) + +print("Disconnecting...") +client.disconnect() diff --git a/pymetawear/client.py b/pymetawear/client.py index 9c6a743..beb9d1e 100755 --- a/pymetawear/client.py +++ b/pymetawear/client.py @@ -15,7 +15,7 @@ import logging from mbientlab.metawear import MetaWear, libmetawear -# Temporary for money patch +# Temporary for monkey patch from mbientlab.metawear.cbindings import FnVoid_VoidP_Int, Const from mbientlab.metawear import Event diff --git a/pymetawear/modules/barometer.py b/pymetawear/modules/barometer.py index 37ab1be..9a29309 100755 --- a/pymetawear/modules/barometer.py +++ b/pymetawear/modules/barometer.py @@ -143,7 +143,8 @@ def _get_standby_time(self, value): return float(value) def get_current_settings(self): - return "oversampling variables: {} IR filter average: {} standby time in ms: {}".format(self.current_oversampling, self.current_iir_filter, self.current_standby_time) + return "oversampling variables: {} IR filter average: {} standby time in ms: {}".format( + self.current_oversampling, self.current_iir_filter, self.current_standby_time) def get_possible_settings(self): return { diff --git a/pymetawear/modules/base.py b/pymetawear/modules/base.py index 4df569f..f00e25f 100644 --- a/pymetawear/modules/base.py +++ b/pymetawear/modules/base.py @@ -157,7 +157,7 @@ def notifications(self, callback=None): class PyMetaWearLoggingModule(PyMetaWearModule): - """Special class with additions for sensors with logging support.""" + """Special class with additions for modules with logging support.""" def __init__(self, board, debug=False): super(PyMetaWearLoggingModule, self).__init__(board, debug) diff --git a/pymetawear/modules/magnetometer.py b/pymetawear/modules/magnetometer.py index 1c278b2..bad5036 100755 --- a/pymetawear/modules/magnetometer.py +++ b/pymetawear/modules/magnetometer.py @@ -33,7 +33,8 @@ def wrapper(*args, **kwargs): return wrapper -class MagnetometerModule(PyMetaWearLoggingModule): +class +MagnetometerModule(PyMetaWearLoggingModule): """MetaWear accelerometer module implementation. :param ctypes.c_long board: The MetaWear board pointer value.