Skip to content

Commit bff0d2f

Browse files
Merge pull request #10 from nextroundwinner/usb
USB connection
2 parents 01b6e3e + 654d7c2 commit bff0d2f

12 files changed

Lines changed: 204 additions & 80 deletions

File tree

.github/workflows/build-validation.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ jobs:
1515
uses: actions/setup-python@v5
1616
with:
1717
python-version: "3.x"
18-
- name: Install pypa/build/pylint
18+
- name: Install pypa/build/pylint/requirements.txt
1919
run: >-
2020
python3 -m
2121
pip install
2222
build
23-
pyserial
2423
pylint
24+
-r src/science_mode_4/requirements.txt
2525
--user
2626
- name: Build a binary wheel and a source tarball
2727
run: python3 -m build

.github/workflows/publish-to-pypi.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jobs:
2323
python3 -m
2424
pip install
2525
build
26+
-r src/science_mode_4/requirements.txt
2627
--user
2728
- name: Build a binary wheel and a source tarball
2829
run: python3 -m build

.github/workflows/publish-to-test-pypi.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
python3 -m
2121
pip install
2222
build
23+
-r src/science_mode_4/requirements.txt
2324
--user
2425
- name: Build a binary wheel and a source tarball
2526
run: python3 -m build

.vscode/launch.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"name": "Python Debugger: Current File",
99
"type": "debugpy",
1010
"request": "launch",
11-
// "program": "__main__.py",
12-
"module": "examples.dyscom.example_dyscom_write_csv",
11+
"program": "__main__.py",
12+
// "module": "examples.dyscom.example_dyscom_write_csv",
1313
"justMyCode": false,
1414
// "args": ["COM3"],
1515
"console": "integratedTerminal"

README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,31 @@ Python 3.11 or higher
1111
# Library
1212

1313
## Installation
14-
1514
- Install science_mode_4 library inclusive dependencies via pip
1615
- `pip install science_mode_4`
1716
- https://pypi.org/project/science-mode-4/
1817

1918
## Dependencies
20-
2119
- PySerial
2220
- https://pypi.org/project/pyserial/
2321
- `pip install pyserial`
24-
- PyUSB - currently not used
22+
- PyUSB
2523
- https://pypi.org/project/pyusb/
26-
- `pip install pyusb`
24+
- `pip install pyusb`
2725
- On Windows
28-
- Download libusb from https://libusb.info/
29-
- Copy libusb-XX.dll into environment root folder (besides python.exe)
30-
- Under Windows there are driver issues
31-
- Code is currently commented out and not usable
26+
- Install libusb-package to get _libusb-XX.dll_
27+
- https://pypi.org/project/libusb-package/
28+
- `pip install libusb-package`
29+
- Under Windows there may be driver issues
30+
- See https://github.com/libusb/libusb/wiki/Windows#How_to_use_libusb_on_Windows
31+
- Use Zadig to change driver for _STM32 Virtual ComPort_ to _libusb-XX.dll_ and reinstall driver
3232

3333
## Build library
3434
- Only necessary, if you made changes to the library or install a version from a branch
35-
- Install dependencies
35+
- Install build dependencies
3636
- `python -m pip install --upgrade build`
37+
- Install other library dependencies
38+
- `pip install -r src/science_mode_4/requirements.txt`
3739
- Optional run linter
3840
- `pip install pylint`
3941
- `pylint .\src\science_mode_4\`
@@ -87,6 +89,13 @@ Python 3.11 or higher
8789
- https://pypi.org/project/matplotlib/
8890
- `pip install matplotlib`
8991
- Fastplotlib with glfw backend
92+
- https://pypi.org/project/fastplotlib/
9093
- `pip install -U fastplotlib`
9194
- `pip install -U glfw`
9295

96+
# Changes
97+
98+
## 0.0.11
99+
- Implemented UsbConnection class
100+
- Alternative for SerialPortConnection, both share the same base class Connection
101+
- Added _PyUSB_ and _libusb-package_ as dependencies

__main__.py

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,104 @@
11
"""Test program how to use library without installing the library,
22
DO NOT USE THIS FILE, USE EXAMPLES INSTEAD"""
33

4+
from timeit import default_timer as timer
5+
6+
import logging
47
import sys
58
import asyncio
69

10+
from science_mode_4.device_i24 import DeviceI24
711
from science_mode_4.device_p24 import DeviceP24
12+
from science_mode_4.dyscom.ads129x.ads129x_config_register_1 import Ads129xOutputDataRate, Ads129xPowerMode
13+
from science_mode_4.dyscom.dyscom_get_operation_mode import PacketDyscomGetAckOperationMode
14+
from science_mode_4.dyscom.dyscom_send_live_data import PacketDyscomSendLiveData
15+
from science_mode_4.dyscom.dyscom_types import DyscomFilterType, DyscomGetType, DyscomInitParams, DyscomPowerModulePowerType, DyscomPowerModuleType, DyscomSignalType
16+
from science_mode_4.protocol.commands import Commands
17+
from science_mode_4.protocol.types import ResultAndError
18+
from science_mode_4.utils import logger
819
from science_mode_4.utils.serial_port_connection import SerialPortConnection
20+
from science_mode_4.utils.usb_connection import UsbConnection
921

1022

1123

1224
async def main() -> int:
1325
"""Main function"""
1426

15-
devices = SerialPortConnection.list_science_mode_device_ports()
16-
connection = SerialPortConnection(devices[0].device)
17-
# devices = UsbConnection.list_science_mode_devices()
18-
# connection = UsbConnection(devices[0])
27+
logger().disabled = True
28+
29+
logger().setLevel(logging.DEBUG)
30+
# devices = SerialPortConnection.list_science_mode_device_ports()
31+
# connection = SerialPortConnection(devices[0].device)
32+
devices = UsbConnection.list_science_mode_devices()
33+
connection = UsbConnection(devices[0])
1934
# connection = NullConnection()
2035
connection.open()
2136

22-
device = DeviceP24(connection)
37+
device = DeviceI24(connection)
2338
await device.initialize()
24-
general = device.get_layer_general()
25-
print(f"Device id: {general.device_id}")
26-
print(f"Firmware version: {general.firmware_version}")
27-
print(f"Science mode version: {general.science_mode_version}")
39+
# get dyscom layer to call dyscom level commands
40+
dyscom = device.get_layer_dyscom()
41+
42+
# call enable measurement power module for measurement
43+
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_ON)
44+
# call init with 4k sample rate and enable signal types
45+
init_params = DyscomInitParams()
46+
init_params.filter = DyscomFilterType.PREDEFINED_FILTER_2
47+
init_params.signal_type = [DyscomSignalType.BI, DyscomSignalType.EMG_1,\
48+
DyscomSignalType.EMG_2, DyscomSignalType.BREATHING, DyscomSignalType.TEMPERATURE]
49+
init_params.register_map_ads129x.config_register_1.output_data_rate = Ads129xOutputDataRate.HR_MODE_4_KSPS__LP_MODE_2_KSPS
50+
init_params.register_map_ads129x.config_register_1.power_mode = Ads129xPowerMode.HIGH_RESOLUTION
51+
await dyscom.init(init_params)
52+
53+
# start dyscom measurement
54+
await dyscom.start()
55+
56+
start_time = timer()
57+
total_count = 0
58+
59+
# loop for some time
60+
for x in range(1000):
61+
# check operation mode from time to time, this function is not waiting for response
62+
# so we have to handle it by ourself later
63+
if x % 100 == 0:
64+
dyscom.send_get_operation_mode()
65+
66+
live_data_counter = 0
67+
while True:
68+
# process all available packages
69+
ack = dyscom.packet_buffer.get_packet_from_buffer(live_data_counter == 0)
70+
if ack:
71+
# because there are multiple get commands, we need to additionally check kind,
72+
# which is always associated DyscomGetType
73+
if ack.command == Commands.DL_GET_ACK and ack.kind == DyscomGetType.OPERATION_MODE:
74+
om_ack: PacketDyscomGetAckOperationMode = ack
75+
print(f"Operation mode {om_ack.operation_mode.name}")
76+
# check if measurement is still active
77+
if om_ack.result_error != ResultAndError.NO_ERROR:
78+
break
79+
elif ack.command == Commands.DL_SEND_LIVE_DATA:
80+
live_data_counter += 1
81+
total_count += 1
82+
83+
sld: PacketDyscomSendLiveData = ack
84+
if sld.status_error:
85+
print(f"SendLiveData status error {sld.samples}")
86+
break
87+
88+
else:
89+
# print(f"Live data acknowledges per iteration {live_data_counter}")
90+
break
91+
92+
# await asyncio.sleep(0.01)
93+
94+
# print stats
95+
end_time = timer()
96+
print(f"Samples: {total_count}, duration: {end_time - start_time}, sample rate: {total_count / (end_time - start_time)}")
97+
98+
# stop measurement
99+
await dyscom.stop()
100+
# turn power module off
101+
await dyscom.power_module(DyscomPowerModuleType.MEASUREMENT, DyscomPowerModulePowerType.SWITCH_OFF)
28102

29103
connection.close()
30104

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "science_mode_4"
7-
version = "0.0.10"
7+
version = "0.0.11"
88
authors = [
99
{ name="Marc Hofmann", email="marc-hofmann@gmx.de" },
1010
]
@@ -20,7 +20,9 @@ license-files = [
2020
"LICENSE"
2121
]
2222
dependencies = [
23-
"pyserial >= 3.5",
23+
"pyserial",
24+
"pyusb",
25+
"libusb-package"
2426
]
2527

2628
[project.urls]

src/science_mode_4/protocol/protocol.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ def packet_to_bytes(packet: Packet) -> bytes:
4848
bb.append_byte(Protocol.STOP_BYTE)
4949

5050
logger().debug("Build package, %s", packet)
51-
logger().debug("Outgoing data, %s", bb)
5251
result = bb.get_bytes()
5352
return bytes(result)
5453

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pyserial
2+
pyusb
3+
libusb-package

src/science_mode_4/utils/connection.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from abc import ABC, abstractmethod
44

5+
from .logger import logger
6+
57

68
class Connection(ABC):
79
"""Abstract base class for connection"""
@@ -22,16 +24,23 @@ def is_open(self) -> bool:
2224
"""Checks if connection is open"""
2325

2426

25-
@abstractmethod
2627
def write(self, data: bytes):
2728
"""Write data to connection"""
29+
logger().debug("Outgoing data, length: %d, bytes: %s", len(data), data.hex(" ").upper())
2830

2931

30-
@abstractmethod
3132
def read(self) -> bytes:
3233
"""Read all data from connection"""
34+
result = self._read_intern()
35+
logger().debug("Incoming data, length: %d, bytes: %s", len(result), result.hex(" ").upper())
36+
return result
3337

3438

3539
@abstractmethod
3640
def clear_buffer(self):
3741
"""Clear buffer from connection"""
42+
43+
44+
@abstractmethod
45+
def _read_intern(self):
46+
"""Read all data from connection"""

0 commit comments

Comments
 (0)