Skip to content

Commit 670f73b

Browse files
committed
Add analog pin autodetection
1 parent bfb5d9c commit 670f73b

4 files changed

Lines changed: 41 additions & 30 deletions

File tree

README.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@ Download the `Arduino IDE <https://www.arduino.cc/en/Main/Software>`__ on your c
4444

4545
You're all set!
4646

47-
An example graph for the default A0 analog port can be found `here <https://github.com/timeflux/timeflux_upsidedownlabs/blob/master/examples/uart.yaml>`__.
48-
You can run it like this:
47+
With `this example graph <https://github.com/timeflux/timeflux_upsidedownlabs/blob/master/examples/uart.yaml>`__, you can autodetect all the available analog ports and start streaming data.
48+
49+
You can run this graph like this:
4950

5051
::
5152

5253
$ conda activate timeflux
5354
$ timeflux -d examples/uart.yaml
5455

55-
For an example of querying multiple ports and naming the channels, see `this graph <https://github.com/timeflux/timeflux_upsidedownlabs/blob/master/examples/channels.yaml>`__.
56+
For an example of filtering and renaming the channels, see `this graph <https://github.com/timeflux/timeflux_upsidedownlabs/blob/master/examples/channels.yaml>`__.

examples/channels.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ graphs:
55
module: timeflux_upsidedownlabs.nodes.driver
66
class: UpsideDownLabs
77
params:
8-
rate: 100
8+
rate: 500
99
channels:
1010
0: ECG
1111
2: EMG1

examples/uart.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
graphs:
2-
- nodes:
2+
- id: demo
3+
nodes:
34
- id: data
45
module: timeflux_upsidedownlabs.nodes.driver
56
class: UpsideDownLabs
67
params:
7-
rate: 500
8+
rate: 250
89
- id: notch
910
module: timeflux_dsp.nodes.filters
1011
class: IIRFilter

timeflux_upsidedownlabs/nodes/driver.py

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class UpsideDownLabs(Node):
1919
Default: autodetect
2020
channels (dict): The pin/channel mapping.
2121
Keys are pin numbers and values are channel names.
22-
Default: {1: "signal"}
22+
Default: autodetect
2323
rate (int): The device rate in Hz.
2424
Default: ``500``.
2525
@@ -32,31 +32,34 @@ class UpsideDownLabs(Node):
3232
def __init__(self, port=None, channels=None, rate=500):
3333
if not port:
3434
port = Arduino.AUTODETECT
35-
if not channels:
36-
channels = {1: "signal"}
3735
self.channels = channels
3836
self.rate = rate
39-
self.timestamp = 0
4037
self.board = Arduino(port)
41-
self.meta = {"rate": rate}
42-
self._lock = Lock()
43-
self._blink()
44-
self.board.samplingOn(1000 / self.rate)
38+
if not self.channels:
39+
self.channels = {
40+
channel: f"A{channel}" for channel in range(len(self.board.analog))
41+
}
4542
for pin in list(self.channels.keys()):
4643
if pin >= len(self.board.analog):
4744
self.logger.warning(f"Removing invalid pin {pin}")
4845
del self.channels[pin]
46+
self.meta = {"rate": rate}
47+
self._lock = Lock()
48+
self._blink()
49+
self.board.samplingOn(1000 / self.rate)
4950
self._reset_buffer()
5051
self._reset_sample()
5152
for pin, channel in self.channels.items():
5253
# See: https://docs.python-guide.org/writing/gotchas/#late-binding-closures
53-
self.board.analog[pin].register_callback(lambda data, channel=channel : self._callback(data, channel))
54+
self.board.analog[pin].register_callback(
55+
lambda data, channel=channel: self._callback(data, channel)
56+
)
5457
self.board.analog[pin].enable_reporting()
5558

5659
def _blink(self):
57-
"""Show a cool led animation"""
60+
"""Show a cool led animation."""
5861
pins = [8, 9, 10, 11, 12, 13]
59-
for i in range(10):
62+
for i in range(5):
6063
for pin in pins:
6164
self.board.digital[pin].write(1)
6265
time.sleep(0.02)
@@ -65,38 +68,44 @@ def _blink(self):
6568

6669
def _callback(self, data, channel):
6770
"""Acquire and cache data."""
68-
if self.timestamp == 0:
69-
self.timestamp = time.time()
7071
self._lock.acquire()
72+
if self.sample["data"][channel] is not None:
73+
self.logger.warn("Corrupted sample")
74+
self._reset_sample()
75+
self.sample["data"][channel] = data
7176
self.sample["received"] += 1
72-
self.sample["data"][channel].append(data)
73-
if not self.sample["timestamp"]:
74-
self.sample["timestamp"] = self.timestamp
7577
if self.sample["received"] == len(self.channels):
7678
self._commit_sample()
77-
self.timestamp += 1 / self.rate
7879
self._lock.release()
7980

8081
def _reset_buffer(self):
81-
"""Reset the buffer"""
82+
"""Reset the buffer."""
8283
self.timestamps = []
8384
self.data = {}
8485
for channel in self.channels.values():
8586
self.data[channel] = []
8687

8788
def _reset_sample(self):
88-
"""Reset the sample"""
89+
"""Reset the sample."""
90+
try:
91+
timestamp = self.sample["timestamp"]
92+
if timestamp == 0:
93+
timestamp = time.time()
94+
else:
95+
timestamp += 1 / self.rate
96+
except:
97+
timestamp = 0
8998
self.sample = {
90-
"timestamp": None,
91-
"data": { channel: [] for channel in self.channels.values() },
92-
"received": 0
99+
"timestamp": timestamp,
100+
"data": {channel: None for channel in self.channels.values()},
101+
"received": 0,
93102
}
94103

95104
def _commit_sample(self):
96-
"""Append the sample"""
105+
"""Append the sample."""
97106
self.timestamps.append(self.sample["timestamp"])
98107
for channel in self.channels.values():
99-
self.data[channel] += self.sample["data"][channel]
108+
self.data[channel].append(self.sample["data"][channel])
100109
self._reset_sample()
101110

102111
def update(self):

0 commit comments

Comments
 (0)