Skip to content

Commit 70efdf1

Browse files
committed
Make I2C improvements.
- For functions that need to acquire the I2C lock, use a new decorator, i2c_lock. This decorator also adds new functionality. Specifically, it has a timeout if it fails to get the lock. - Consolidate common code between I2C Transaction and Command into a new internal function, _transmit. This is the same pattern used for serial.
1 parent 6ba782d commit 70efdf1

File tree

1 file changed

+61
-66
lines changed

1 file changed

+61
-66
lines changed

notecard/notecard.py

Lines changed: 61 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,33 @@ def decorator(self, *args, **kwargs):
9191
return decorator
9292

9393

94+
def i2c_lock(fn):
95+
"""Attempt to get a lock on the I2C bus used for Notecard comms."""
96+
97+
def decorator(self, *args, **kwargs):
98+
retries = 5
99+
while use_i2c_lock and retries != 0:
100+
if self.i2c.try_lock():
101+
break
102+
103+
retries -= 1
104+
# Try again after 100 ms.
105+
time.sleep(.1)
106+
107+
if retries == 0:
108+
raise Exception('Failed to acquire I2C lock.')
109+
110+
try:
111+
ret = fn(self, *args, **kwargs)
112+
finally:
113+
if use_i2c_lock:
114+
self.i2c.unlock()
115+
116+
return ret
117+
118+
return decorator
119+
120+
94121
class Notecard:
95122
"""Base Notecard class.
96123
@@ -150,24 +177,25 @@ def _transmit(self, req):
150177
req = self._preprocess_req(req)
151178
req_json = _prepare_request(req, self._debug)
152179

153-
transaction_timeout_secs = 30
154-
if self._transaction_manager:
155-
self._transaction_manager.start(transaction_timeout_secs)
156-
157-
seg_off = 0
158-
seg_left = len(req_json)
159-
while seg_left > 0:
160-
seg_len = seg_left
161-
if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN:
162-
seg_len = CARD_REQUEST_SEGMENT_MAX_LEN
180+
try:
181+
transaction_timeout_secs = 30
182+
if self._transaction_manager:
183+
self._transaction_manager.start(transaction_timeout_secs)
163184

164-
self.uart.write(req_json[seg_off:seg_off + seg_len].encode('utf-8'))
165-
seg_off += seg_len
166-
seg_left -= seg_len
167-
time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000)
185+
seg_off = 0
186+
seg_left = len(req_json)
187+
while seg_left > 0:
188+
seg_len = seg_left
189+
if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN:
190+
seg_len = CARD_REQUEST_SEGMENT_MAX_LEN
168191

169-
if self._transaction_manager:
170-
self._transaction_manager.stop()
192+
self.uart.write(req_json[seg_off:seg_off + seg_len].encode('utf-8'))
193+
seg_off += seg_len
194+
seg_left -= seg_len
195+
time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000)
196+
finally:
197+
if self._transaction_manager:
198+
self._transaction_manager.stop()
171199

172200
def _read_byte(self):
173201
"""Read a single byte from the Notecard."""
@@ -325,84 +353,51 @@ def _receive(self, timeout_secs, chunk_delay_secs, wait_for_newline):
325353
# behavior of other SDKs (e.g. note-c).
326354
time.sleep(chunk_delay_secs)
327355

328-
if (timeout_secs != 0 and has_timed_out(start, timeout_secs)):
356+
if timeout_secs != 0 and has_timed_out(start, timeout_secs):
329357
raise Exception("Timed out while reading data from the Notecard.")
330358

331359
return read_data
332360

333-
def Command(self, req):
334-
"""Perform a Notecard command and exit with no response."""
361+
def _transmit(self, req):
335362
req = self._preprocess_req(req)
336-
if 'cmd' not in req:
337-
raise Exception("Please use 'cmd' instead of 'req'")
338-
339363
req_json = _prepare_request(req, self._debug)
340364

341-
while not self.lock():
342-
pass
343-
344365
try:
345366
transaction_timeout_secs = 30
346367
if self._transaction_manager:
347368
self._transaction_manager.start(transaction_timeout_secs)
348369

349370
self._send_payload(req_json)
350371
finally:
351-
self.unlock()
352372
if self._transaction_manager:
353373
self._transaction_manager.stop()
354374

355-
def Transaction(self, req):
356-
"""Perform a Notecard transaction and return the result."""
357-
req = self._preprocess_req(req)
358-
359-
req_json = _prepare_request(req, self._debug)
360-
rsp_json = ""
361-
362-
while not self.lock():
363-
pass
375+
@i2c_lock
376+
def Command(self, req):
377+
"""Perform a Notecard command and exit with no response."""
378+
if 'cmd' not in req:
379+
raise Exception("Please use 'cmd' instead of 'req'")
364380

365-
try:
366-
transaction_timeout_secs = 30
367-
if self._transaction_manager:
368-
self._transaction_manager.start(transaction_timeout_secs)
381+
self._transmit(req)
369382

370-
self._send_payload(req_json)
383+
@i2c_lock
384+
def Transaction(self, req):
385+
"""Perform a Notecard transaction and return the result."""
386+
self._transmit(req)
371387

372-
read_data = self._receive(transaction_timeout_secs, 0.05, True)
373-
rsp_json = "".join(map(chr, read_data))
374-
finally:
375-
self.unlock()
376-
if self._transaction_manager:
377-
self._transaction_manager.stop()
388+
read_data = self._receive(30, 0.05, True)
389+
rsp_json = "".join(map(chr, read_data))
378390

379391
if self._debug:
380392
print(rsp_json.rstrip())
381393

382394
return json.loads(rsp_json)
383395

396+
@i2c_lock
384397
def Reset(self):
385398
"""Reset the Notecard."""
386-
while not self.lock():
387-
pass
388-
389-
try:
390-
# Read from the Notecard until there's nothing left to read.
391-
self._receive(0, .001, False)
392-
finally:
393-
self.unlock()
394-
395-
def lock(self):
396-
"""Lock the I2C port so the host can interact with the Notecard."""
397-
if use_i2c_lock:
398-
return self.i2c.try_lock()
399-
return True
400-
401-
def unlock(self):
402-
"""Unlock the I2C port."""
403-
if use_i2c_lock:
404-
return self.i2c.unlock()
405-
return True
399+
# Read from the Notecard until there's nothing left to read.
400+
self._receive(0, .001, False)
406401

407402
def __init__(self, i2c, address, max_transfer, debug=False):
408403
"""Initialize the Notecard before a reset."""

0 commit comments

Comments
 (0)