diff --git a/gattlib-py/examples/nordic_thingy/__init__.py b/gattlib-py/examples/nordic_thingy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gattlib-py/examples/nordic_thingy/environment_service.py b/gattlib-py/examples/nordic_thingy/environment_service.py new file mode 100644 index 0000000..30287c6 --- /dev/null +++ b/gattlib-py/examples/nordic_thingy/environment_service.py @@ -0,0 +1,183 @@ +import struct +import sys +import threading + +from dbus.mainloop.glib import DBusGMainLoop +try: + from gi.repository import GLib, GObject +except ImportError: + import gobject as GObject +import sys + +import numpy +from matplotlib.pylab import * +from mpl_toolkits.axes_grid1 import host_subplot +import matplotlib.animation as animation + +from gattlib import uuid + +last_measures = { + 'temperature': { 'value': None, 'min': None, 'max': None }, + 'pressure': { 'value': None, 'min': None, 'max': None }, + 'humidity': { 'value': None, 'min': None, 'max': None }, +} + + +def temperature_notification(value, user_data): + last_measures['temperature']['value'] = float("%d.%d" % (value[0], value[1])) + print("Temperature: %f" % last_measures['temperature']['value']) + + +def pressure_notification(value, user_data): + (pressure_integer, pressure_decimal) = struct.unpack("= xmax - 1.00: + temp_line.axes.set_xlim(x - xmax + 1.0, x + 1.0) + hum_line.axes.set_xlim(x - xmax + 1.0, x + 1.0) + + return temp_line, hum_line + + +def environment_service(args, gatt_device): + NORDIC_THINGY_WEATHER_STATION_SERVICE = uuid.gattlib_uuid_str_to_int("EF680200-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_TEMPERATURE_CHAR = uuid.gattlib_uuid_str_to_int("EF680201-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_PRESSURE_CHAR = uuid.gattlib_uuid_str_to_int("EF680202-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_HUMIDITY_CHAR = uuid.gattlib_uuid_str_to_int("EF680203-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_AIR_QUALITY_CHAR = uuid.gattlib_uuid_str_to_int("EF680204-9B35-4933-9B10-52FFA9740042") + + temperature_characteristic = gatt_device.characteristics[NORDIC_THINGY_TEMPERATURE_CHAR] + pressure_characteristic = gatt_device.characteristics[NORDIC_THINGY_PRESSURE_CHAR] + humidity_characteristic = gatt_device.characteristics[NORDIC_THINGY_HUMIDITY_CHAR] + air_quality_characteristic = gatt_device.characteristics[NORDIC_THINGY_AIR_QUALITY_CHAR] + + # Initialize graph + threading.Thread(target=graph_init).start() + + try: + DBusGMainLoop(set_as_default=True) + mainloop = GLib.MainLoop() + + temperature_characteristic.register_notification(temperature_notification) + temperature_characteristic.notification_start() + + pressure_characteristic.register_notification(pressure_notification) + pressure_characteristic.notification_start() + + humidity_characteristic.register_notification(humidity_notification) + humidity_characteristic.notification_start() + + mainloop.run() + except KeyboardInterrupt: + mainloop.quit() + finally: + humidity_characteristic.notification_stop() + pressure_characteristic.notification_stop() + temperature_characteristic.notification_stop() + gatt_device.disconnect() diff --git a/gattlib-py/examples/nordic_thingy/nordic_thingy.py b/gattlib-py/examples/nordic_thingy/nordic_thingy.py index b2db0b8..4100192 100755 --- a/gattlib-py/examples/nordic_thingy/nordic_thingy.py +++ b/gattlib-py/examples/nordic_thingy/nordic_thingy.py @@ -1,194 +1,32 @@ #!/usr/bin/env python3 import argparse -import struct -import sys -import threading -from dbus.mainloop.glib import DBusGMainLoop -try: - from gi.repository import GLib, GObject -except ImportError: - import gobject as GObject -import sys - -import numpy -from matplotlib.pylab import * -from mpl_toolkits.axes_grid1 import host_subplot -import matplotlib.animation as animation - -from gattlib import device, uuid - -last_measures = { - 'temperature': { 'value': None, 'min': None, 'max': None }, - 'pressure': { 'value': None, 'min': None, 'max': None }, - 'humidity': { 'value': None, 'min': None, 'max': None }, -} - - -def temperature_notification(value, user_data): - last_measures['temperature']['value'] = float("%d.%d" % (value[0], value[1])) - print("Temperature: %f" % last_measures['temperature']['value']) - - -def pressure_notification(value, user_data): - (pressure_integer, pressure_decimal) = struct.unpack("= xmax - 1.00: - temp_line.axes.set_xlim(x - xmax + 1.0, x + 1.0) - hum_line.axes.set_xlim(x - xmax + 1.0, x + 1.0) - - return temp_line, hum_line +from gattlib import device +from environment_service import environment_service +from sound_service import sound_service if __name__ == '__main__': parser = argparse.ArgumentParser(description='Gattlib example for Nordic Thingy') parser.add_argument('mac', type=str, help='Mac Address of the GATT device to connect') + subparsers = parser.add_subparsers(help='sub-command help') + + environment_parser = subparsers.add_parser('environment', help='Environment Command') + environment_parser.set_defaults(func=environment_service) + + sound_parser = subparsers.add_parser('sound', help='Sound Command') + sound_parser.add_argument('--wav', type=str, help='WAV file to play') + sound_parser.set_defaults(func=sound_service) + args = parser.parse_args() - NORDIC_THINGY_WEATHER_STATION_SERVICE = uuid.gattlib_uuid_str_to_int("EF680200-9B35-4933-9B10-52FFA9740042") - NORDIC_THINGY_TEMPERATURE_CHAR = uuid.gattlib_uuid_str_to_int("EF680201-9B35-4933-9B10-52FFA9740042") - NORDIC_THINGY_PRESSURE_CHAR = uuid.gattlib_uuid_str_to_int("EF680202-9B35-4933-9B10-52FFA9740042") - NORDIC_THINGY_HUMIDITY_CHAR = uuid.gattlib_uuid_str_to_int("EF680203-9B35-4933-9B10-52FFA9740042") - NORDIC_THINGY_AIR_QUALITY_CHAR = uuid.gattlib_uuid_str_to_int("EF680204-9B35-4933-9B10-52FFA9740042") + if not hasattr(args, 'func'): + raise RuntimeError("Please specify the command to launch: 'environment', 'sound'") gatt_device = device.Device(adapter=None, addr=args.mac) gatt_device.connect() gatt_device.discover() - temperature_characteristic = gatt_device.characteristics[NORDIC_THINGY_TEMPERATURE_CHAR] - pressure_characteristic = gatt_device.characteristics[NORDIC_THINGY_PRESSURE_CHAR] - humidity_characteristic = gatt_device.characteristics[NORDIC_THINGY_HUMIDITY_CHAR] - air_quality_characteristic = gatt_device.characteristics[NORDIC_THINGY_AIR_QUALITY_CHAR] - - # Initialize graph - threading.Thread(target=graph_init).start() - - try: - DBusGMainLoop(set_as_default=True) - mainloop = GLib.MainLoop() - - temperature_characteristic.register_notification(temperature_notification) - temperature_characteristic.notification_start() - - pressure_characteristic.register_notification(pressure_notification) - pressure_characteristic.notification_start() - - humidity_characteristic.register_notification(humidity_notification) - humidity_characteristic.notification_start() - - mainloop.run() - except KeyboardInterrupt: - mainloop.quit() - finally: - humidity_characteristic.notification_stop() - pressure_characteristic.notification_stop() - temperature_characteristic.notification_stop() - gatt_device.disconnect() + # Launch the sub-command specific function + args.func(args, gatt_device) diff --git a/gattlib-py/examples/nordic_thingy/sound_service.py b/gattlib-py/examples/nordic_thingy/sound_service.py new file mode 100644 index 0000000..3efb7e7 --- /dev/null +++ b/gattlib-py/examples/nordic_thingy/sound_service.py @@ -0,0 +1,122 @@ +import time +import threading +import wave + +from gattlib import uuid + +from dbus.mainloop.glib import DBusGMainLoop +try: + from gi.repository import GLib, GObject +except ImportError: + import gobject as GObject + +m_thingy_buffer_free = threading.Event() +m_mainloop = None + + +def speaker_status_notification(value, user_data): + global m_thingy_buffer_free + + if value == b'\x01': + print("Thingy's Buffer warning") + m_thingy_buffer_free.clear() + elif value == b'\x02': + print("Thingy's Buffer ready") + m_thingy_buffer_free.set() + elif value == b'\x10': + print("Thingy's Packet disregarded") + elif value == b'\x11': + print("Thingy's Invalid command") + elif value == b'\x00': + print("Thingy's Finished") + else: + raise RuntimeError("Invalid Speaker notification value: %s" % value) + + +def play_sample(config_characteristic, speaker_characteristic): + # Read the current configuration and only change the speaker configuration (not the microphone configuration) + sound_config = config_characteristic.read() + sound_config[0] = 0x03 + config_characteristic.write(sound_config) + # Test speaker + speaker_characteristic.write(b'\x03') + m_mainloop.quit() + + +def play_wav_file(config_characteristic, speaker_characteristic, wav_filepath): + global m_thingy_buffer_free + + wav_file = wave.open(wav_filepath) + + # Python library only support non-compressed WAV file + if wav_file.getcomptype() != 'NONE': + raise RuntimeError("Please give a non-compressed WAV file") + if wav_file.getsampwidth() != 1: + raise RuntimeError("Nordic Thingy52 only supports 8-bit WAV file") + if wav_file.getframerate() != 8000: + raise RuntimeError("Nordic Thingy52 only supports 8kHz WAV file") + if wav_file.getnchannels() == 2: + print("Warning: Your WAV file is a stereo file") + + frames = wav_file.readframes(wav_file.getnframes()) + + # Read the current configuration and only change the speaker configuration (not the microphone configuration) + sound_config = config_characteristic.read() + sound_config[0] = 0x02 + config_characteristic.write(sound_config) + + stream = speaker_characteristic.stream_open() + + # We assume the buffer is free when we start + m_thingy_buffer_free.set() + + # We send one frame at a time + max_frame_size = stream.mtu * 1 + + while len(frames) > 0: + if not m_thingy_buffer_free.is_set(): + m_thingy_buffer_free.wait() + + stream.write(frames[0:max_frame_size]) + frames = frames[max_frame_size:] + + # Arbitraty value + time.sleep(0.03) + + stream.close() + print("All WAV file has been sent") + m_mainloop.quit() + + +def sound_service(args, gatt_device): + global m_mainloop + + NORDIC_THINGY_SOUND_SERVICE = uuid.gattlib_uuid_str_to_int("EF680500-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_CONFIG_CHAR = uuid.gattlib_uuid_str_to_int("EF680501-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_SPEAKER_CHAR = uuid.gattlib_uuid_str_to_int("EF680502-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_SPEAKER_STATUS_CHAR = uuid.gattlib_uuid_str_to_int("EF680503-9B35-4933-9B10-52FFA9740042") + NORDIC_THINGY_MICROPHONE_CHAR = uuid.gattlib_uuid_str_to_int("EF680504-9B35-4933-9B10-52FFA9740042") + + config_characteristic = gatt_device.characteristics[NORDIC_THINGY_CONFIG_CHAR] + speaker_characteristic = gatt_device.characteristics[NORDIC_THINGY_SPEAKER_CHAR] + speaker_status_characteristic = gatt_device.characteristics[NORDIC_THINGY_SPEAKER_STATUS_CHAR] + microphone_characteristic = gatt_device.characteristics[NORDIC_THINGY_MICROPHONE_CHAR] + + try: + DBusGMainLoop(set_as_default=True) + m_mainloop = GLib.MainLoop() + + speaker_status_characteristic.register_notification(speaker_status_notification) + speaker_status_characteristic.notification_start() + + if args.wav: + threading.Thread(target=play_wav_file, args=(config_characteristic, speaker_characteristic, args.wav)).start() + else: + threading.Thread(target=play_sample, args=(config_characteristic, speaker_characteristic)).start() + + m_mainloop.run() + except KeyboardInterrupt: + m_mainloop.quit() + finally: + speaker_status_characteristic.notification_stop() + gatt_device.disconnect()