Skip to content

Commit c8dde2d

Browse files
committed
Convert I2C sensor oscilloscope into non-blocking method
Fixes fossasia#58 . Separate methods for starting acquisition, fetching data buffer, and processing data buffer have been implemented in Peripherals.I2C
1 parent 3e799a1 commit c8dde2d

File tree

4 files changed

+126
-6
lines changed

4 files changed

+126
-6
lines changed

PSL/Peripherals.py

Lines changed: 124 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from __future__ import print_function
2-
import PSL.commands_proto as CP
2+
import commands_proto as CP
33
import numpy as np
44
import time, inspect
55

@@ -33,7 +33,11 @@ class I2C():
3333
>>> print (x,y,z)
3434
3535
"""
36-
36+
samples = 0
37+
total_bytes=0
38+
channels = 0
39+
tg=100
40+
MAX_SAMPLES = 10000
3741
def __init__(self, H):
3842
self.H = H
3943
from PSL import sensorlist
@@ -375,6 +379,124 @@ def scan(self, frequency=100000, verbose=False):
375379
self.stop()
376380
return addrs
377381

382+
def __captureStart__(self,address,location,sample_length,total_samples,tg):
383+
"""
384+
Blocking call that starts fetching data from I2C sensors like an oscilloscope fetches voltage readings
385+
You will then have to call `__retrievebuffer__` to fetch this data, and `__dataProcessor` to process and return separate channels
386+
387+
refer to `capture` if you want a one-stop solution.
388+
389+
.. tabularcolumns:: |p{3cm}|p{11cm}|
390+
================== ============================================================================================
391+
**Arguments**
392+
================== ============================================================================================
393+
address Address of the I2C sensor
394+
location Address of the register to read from
395+
sample_length Each sample can be made up of multiple bytes startng from <location> . such as 3-axis data
396+
total_samples Total samples to acquire. Total bytes fetched = total_samples*sample_length
397+
tg timegap between samples (in uS)
398+
================== ============================================================================================
399+
400+
:return: Arrays X(timestamps),Y1,Y2 ...
401+
402+
"""
403+
if(tg<20):tg=20
404+
total_bytes = total_samples*sample_length
405+
print ('total bytes calculated : ',total_bytes)
406+
if(total_bytes>self.MAX_SAMPLES*2):
407+
print ('Sample limit exceeded. 10,000 int / 20000 bytes total')
408+
total_bytes = self.MAX_SAMPLES*2
409+
total_samples = total_bytes/sample_length #2* because sample array is in Integers, and we're using it to store bytes
410+
411+
412+
print ('length of each channel : ',sample_length)
413+
self.total_bytes = total_bytes
414+
self.channels = sample_length
415+
self.samples = total_samples
416+
self.tg = tg
417+
418+
self.H.__sendByte__(CP.I2C_HEADER)
419+
self.H.__sendByte__(CP.I2C_START_SCOPE)
420+
self.H.__sendByte__(address)
421+
self.H.__sendByte__(location)
422+
self.H.__sendByte__(sample_length)
423+
self.H.__sendInt__(total_samples) #total number of samples to record
424+
self.H.__sendInt__(tg) #Timegap between samples. 1MHz timer clock
425+
self.H.__get_ack__()
426+
return 1e-6*self.samples*self.tg+.01
427+
428+
def __retrievebuffer__(self):
429+
'''
430+
Fetch data acquired by the I2C scope. refer to :func:`__captureStart__`
431+
432+
'''
433+
total_int_samples = self.total_bytes/2
434+
DATA_SPLITTING = 500
435+
print ('fetchin samples : ',total_int_samples,' split',DATA_SPLITTING)
436+
data=b''
437+
for i in range(int(total_int_samples/DATA_SPLITTING)):
438+
self.H.__sendByte__(CP.ADC)
439+
self.H.__sendByte__(CP.GET_CAPTURE_CHANNEL)
440+
self.H.__sendByte__(0) #starts with A0 on PIC
441+
self.H.__sendInt__(DATA_SPLITTING)
442+
self.H.__sendInt__(i*DATA_SPLITTING)
443+
rem = DATA_SPLITTING*2+1
444+
for a in range(200):
445+
partial = self.H.fd.read(rem) #reading int by int sometimes causes a communication error. this works better.
446+
rem -=len(partial)
447+
data+=partial
448+
#print ('partial: ',len(partial), end=",")
449+
if rem<=0:
450+
break
451+
data=data[:-1]
452+
#print ('Pass : len=',len(data), ' i = ',i)
453+
454+
if total_int_samples%DATA_SPLITTING:
455+
self.H.__sendByte__(CP.ADC)
456+
self.H.__sendByte__(CP.GET_CAPTURE_CHANNEL)
457+
self.H.__sendByte__(0) #starts with A0 on PIC
458+
self.H.__sendInt__(total_int_samples%DATA_SPLITTING)
459+
self.H.__sendInt__(total_int_samples-total_int_samples%DATA_SPLITTING)
460+
rem = 2*(total_int_samples%DATA_SPLITTING)+1
461+
for a in range(20):
462+
partial = self.H.fd.read(rem) #reading int by int sometimes causes a communication error. this works better.
463+
rem -=len(partial)
464+
data+=partial
465+
#print ('partial: ',len(partial), end="")
466+
if rem<=0:
467+
break
468+
data=data[:-1]
469+
print ('Final Pass : len=',len(data))
470+
return data
471+
472+
def __dataProcessor__(self,data,*args):
473+
'''
474+
Interpret data acquired by the I2C scope. refer to :func:`__retrievebuffer__` to fetch data
475+
476+
================== ============================================================================================
477+
**Arguments**
478+
================== ============================================================================================
479+
data byte array returned by :func:`__retrievebuffer__`
480+
*args supply optional argument 'int' if consecutive bytes must be combined to form short integers
481+
================== ============================================================================================
482+
483+
'''
484+
485+
try:
486+
data = [ord(a) for a in data]
487+
if('int' in args):
488+
for a in range(self.channels*self.samples/2): self.buff[a] = np.int16((data[a*2]<<8)|data[a*2+1])
489+
else:
490+
for a in range(self.channels*self.samples): self.buff[a] = data[a]
491+
492+
yield np.linspace(0,self.tg*(self.samples-1),self.samples)
493+
for a in range(int(self.channels/2)):
494+
yield self.buff[a:self.samples*self.channels/2][::self.channels/2]
495+
except Exception as ex:
496+
msg = "Incorrect number of bytes received",ex
497+
raise RuntimeError(msg)
498+
499+
378500
def capture(self, address, location, sample_length, total_samples, tg, *args):
379501
"""
380502
Blocking call that fetches data from I2C sensors like an oscilloscope fetches voltage readings

PSL/SENSORS/BH1750.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, I2C, **args):
3737
self.ADDRESS = args.get('address', 0x23)
3838
self.init()
3939

40-
def init(self):
40+
def init(self):
4141
self.I2C.writeBulk(self.ADDRESS, [self.RES_500mLx])
4242

4343
def setRange(self, g):

PSL/SENSORS/HMC5883L.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def __init__(self, I2C, **args):
5353
'''
5454
self.init()
5555

56-
def init(self):
56+
def init(self):
5757
self.__writeCONFA__()
5858
self.__writeCONFB__()
5959
self.I2C.writeBulk(self.ADDRESS, [self.MODE, 0]) # enable continuous measurement mode

PSL/SENSORS/TSL2561.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
from __future__ import print_function
66
import time
77

8-
98
def connect(route, **args):
109
return TSL2561(route, **args)
1110

12-
1311
class TSL2561:
1412
VISIBLE = 2 # channel 0 - channel 1
1513
INFRARED = 1 # channel 1

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy