-
Notifications
You must be signed in to change notification settings - Fork 58
slow reading AnalogIn() #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@caternuson i wonder if its this line |
@caternuson thank you for looking into it. I have already modified that and the above result is with that modification. I am sorry forgot to mention, was kinda late at night. I was thinking that the speed looks exactly half of what it would be without differential thought. |
It could just be code execution time or the I2C transaction when trying to read this way. That section of code will probably change when I work a fix for #26. I'll keep track of this issue as part of that as well. |
I just post it to complement the information. I have added baud rate for i2c in
in file while not self._conversion_complete():
pass
# time.sleep(0.01) |
Just an update - I'm setup and testing on a Pi Zero W and have generally recreated the above results. I'm using a version of the library that also removes the import time
import board
import busio
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn
SAMPLES = 1000
RATE = 16
i2c = busio.I2C(board.SCL, board.SDA)
ads = ADS.ADS1115(i2c, data_rate=RATE)
chan = AnalogIn(ads, ADS.P0)
print("Acquiring...")
start = time.time()
for _ in range(SAMPLES):
foo = chan.value
end = time.time()
total_time = end - start
print("Time of capture: {}s".format(total_time))
print("Sample rate requested={} actual={}".format(RATE, SAMPLES / total_time)) Setting to a slow rate of 16Hz with the expectation that the ADS will be the slowest thing, results are generally as expected:
Setting to the faster 860Hz roughly repeats the same values as in first issue post:
The library in generally inefficient in that it sets the configuration register with every read. It might help to separate that out somehow. The investigation continues... |
Just dumping more of the story here as a I go. Looks like changing buadrate does not actually do anything: Here's the I2C traffic for |
thats in linux? that's oddly long for delays in between |
Yep, Raspbian Lite. System deets below. Trace is from pressing "Start" in Saleae/Logic and then calling pi@raspberrypi:~ $ python3 --version
Python 3.5.3
pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.14.79+ #1159 Sun Nov 4 17:28:08 GMT 2018 armv6l GNU/Linux
pi@raspberrypi:~ $ cat /proc/cpuinfo
processor : 0
model name : ARMv6-compatible processor rev 7 (v6l)
BogoMIPS : 697.95
Features : half thumb fastmult vfp edsp java tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xb76
CPU revision : 7
Hardware : BCM2835
Revision : 9000c1
Serial : 000000009ead533c |
you may want to try replacing |
Just to note. I have got ADS1015 as well, to be able to compare. The results are same. |
@caternuson you might try it on normal raspberry pi. I have tried it on Pi 3 B+ and the speeds are completely different. Maybe this issue might be hardware specific to Pi Zero. I do have v1.1 |
@ladyada good tip, that cleaned up the lag in the "register read" between the write and read: with self.i2c_device as i2c:
i2c.write(self.buf, end=1, stop=False)
i2c.readinto(self.buf, end=2) with self.i2c_device as i2c:
i2c.write_then_readinto(bytearray([reg]), self.buf, in_end=2, stop=False) The total transaction for @tomasinouk I'd imagine the gaps above between I2C calls would be shorter on a faster Pi, so total time would also be less the 5ms. But hopefully can can get something that'll work even on a Pi Zero. |
i like keeping the AnalogIn interface - could you use |
The AnalogIn interface gives a per channel object to use: chan0 = AnalogIn(ads, ADS.P0)
chan1 = AnalogIn(ads, ADS.P1)
knob0 = chan0.value
knob1 = chan1.value Under the hood, it modifies the ADC registers as needed to swap the mux, read Fast reading can really only be done on one channel at a time. So there would need to be some kind of channel-to-channel synchronization. |
yeah i just don't wanna change what we've got now ... we could do some caching so you aren't re-reading the mux? |
How about something like a lock mechanism? Analogous to how the I2C bus is shared with adc.data_rate = 860
adc.mode = Mode.CONTINUOUS
with chan0 as chan:
# can now do fast readings on chan0
for i in range(SAMPLES):
# still no synchronization :(
data[i] = chan.value
# this would throw an Error since chan1 does not have the lock
chan1.value
# lock is now released, so this would work again, but be slow
chan1.value |
I've implemented the above and pushed the working code up here: Testing was done on a Pi Zero W, which I realize is not the fastest Pi. Interestingly, the polling for the ready pin makes it slower than just reading a bunch of single shot conversions. Dealing with synchronization without interrupts is going to be an issue. pi@raspberrypi:~ $ python3 ads1x15_fast_read.py
Acquiring normal...
Time of capture: 5.0196709632873535s
Sample rate requested=860 actual=199.21624491201825
Acquiring fast...
Time of capture: 8.566477060317993s
Sample rate requested=860 actual=116.73410118988626
Acquiring fast w/o polling...
Time of capture: 1.148141860961914s
Sample rate requested=860 actual=870.9725113255598 Is this approach worth continuing to pursue? It has the following limitations:
|
Maybe a dumb comment, but isn't the problem related to the fact you're using the ADS1x15 in single-shot mode? According to the specs this is a mode designed for periodic sampling, where the device is woken from a 'sleep' mode, then sampled for a short time (1/8th second for data_rate 8, or 1/860th second for data_rate 860), then put back to sleep. Surely it's unreasonable to expect that 1000 repeat cycles of that would only take 100x the sampling rate? I'm pretty sure any longer sampling period needs the device to be used in Continuous mode. In that case the power stays fully up and you pick off the latest reading as many times as you like. In that case 1000 samples should take 1000x the sampling rate. But it seems the python library (at least the new circuitPython incarnation) presently can't be used to set Continuous mode. It would be great to see that fixed :) Chris. |
Yep. It would be used with continuous. The example linked above shows three ways to take a lot of readings. The first is single shot - which is just there for comparison purposes. Just to prove it's slow. The second two are continuous and more in line with what is trying to be achieved here. The crux of all this the two bullets above, and really, the second bullet. That example was tested using a local copy of the library that had PR #28 merged in. That's why it was able to use continuous. That PR is still waiting merge here. You can grab that code from the other link above if you want. |
Cool, thanks. I copied it verbatim and ran it on my 3B+, but got the following error when it tried to apply the second method. I'm running Python 3.5.3.
|
Did you also get a copy of the library code from the PR branch and switch to using that? |
Ok, that makes sense, but unfortunately my skills don't go far enough to make this happen. I copied the new library, I found where the old version is being stored in my Pi, but after many different approaches I simply can't get the old one renamed or the new one copied there. I think it's a permissions issue. If I uninstall the CircuitPython package and clone it again from github using the web url, will it pick up the new version of this library? Sorry for my ignorance. I feel like I'm a little kid found myself in big school. Chris. |
Since there isn't a pull request yet, easiest would be to just clone the source repo: |
I'm still a bit out of my depth I'm afraid. After several hours I've figured out how to clone your repo (the key was to fork it to my github profile first). And I can checkout the fast_read branch at the command line using git checkout fast_read. But I can't figure out how to point to that branch in the PYTHONPATH. And even still unclear about how to edit/setup that environment variable on the RPi. |
Ok, I've ended up using pip to uninstall the original library and then re-install from the fast_read branch (using pip3 install git+https://github.com/chrisjamesfell/Adafruit_CircuitPython_ADS1x15.git@fast_read) When I ran the test script the second part ("Acquiring fast") hung (endless loop). I deleted those lines and ran it again, so I could try the third part and this time it worked, but it reported an actual sample rate of 2362.05 (supposed to be 860). I presume this means something is wrong? |
What model Pi were you using? The example output a few comments back was on a Pi Zero W, which is one of the slowest Pi's. If you're running on a faster Pi, then the loop w/o polling will run much faster. |
I'm using a 3B+. I don't need lightning speed (I plan to use 8 samples/sec), but I want Continuous so I can keep sampling a noisy signal for an arbitrary length of time without breaks. I don't really understand what you're doing with the ready pin, but the high apparent speed makes me suspicious that the sampling isn't being done properly. Thoughts? |
The ready pin approach uses the ALRT/READY pin on the breakout. It gets wired to an available digital input on the Pi, in this case BCM 23: This is all still a work in progress, so hard to advise on best approach. But one idea for right now would be to "tune" a simple delay in this loop: Or try the same thing with the first approach: |
From reading the datasheet (https://cdn-shop.adafruit.com/datasheets/ads1115.pdf) (and also some other thread I can no longer find) I believe lower sampling rates incorperate averaging, i.e. there is some native very high sampling rate, and when you ask for 8SPS you actually get averaging of the high rate over 1/8 sec for each delivered sample; so quite a stable and noise-free result. It isn't clear to me that this would happen if an artificial delay was tuned into a read loop. If the board reports 2300SPS without an artificial delay, then it mustn't be sampling for very long; maybe even just a single point at its internal clock speed. If I insert a wait between calls, won't I just get poor quality data delivered slowly? |
i am running this code or similar with this, i get an error that module not found names "board" |
@wajeehAfridi It sounds like you haven't fully installed Blinka: |
@ladyada I bumped up I2C baud rate to 1MHz and ran the tests again, including the one you suggested:
That is TEST 2 in the results below. TEST 3 and TEST 4 use the modification I made to the library to additionally reduce the I2C traffic when re-reading the same channel (register). A context manager is used to engage that mode of operation. TEST 3 ignores the conversion ready pin and just sucks in the data - that might be what you were hoping for? You can generally ignore TEST 1. It is just the single-shot baseline for comparison. Tests run on a Pi Zero W with an ADS1115. pi@raspberrypi:~ $ python3 ads1x15_fast_read.py
ADS1x15 Timing Tests
----------------------------------------
TEST 1 - Acquiring normal in single shot mode...
Time of capture: 3.7274725437164307s
Sample rate requested=860 actual=268.278300717666
----------------------------------------
TEST 2 - Acquiring normal in continuous mode...
Time of capture: 2.3309268951416016s
Sample rate requested=860 actual=429.0138837405499
----------------------------------------
TEST 3 - Acquiring fast w/o polling...
Time of capture: 0.8692643642425537s
Sample rate requested=860 actual=1150.398015995243
----------------------------------------
TEST 4 - Acquiring fast with polling...
Time of capture: 5.555660963058472s
Sample rate requested=860 actual=179.99658486170213 |
huh! the numbers dont lie, lets go with the mod! |
OK. I cleaned up my experimental fork/branch to just the parts that matter for fast reading. I removed the stuff that was needed for setting up the ALRT pin to signal conversion complete since that's not going to be used. PR'd #32 |
Please try the 2.1.0 release of the library. It should improve the speed at which you can read from the ADC. Note that you'll want to bump up the I2C bus speed and that there is currently no synchronization with conversion complete - so it's a pretty crude and simple optimization. See new example for general usage: Closing this issue for now. |
Hi, I have notice that it gives the same read several times. In my case is up to 5000 samples per second but it only take 9 diferent values in 18.5 ms guivin 2ms per read 500SPS. I show my results. Time of capture: 0.01859169099952851s If we work with 1115 instead of 1015 we gets 860 SPS 1.16 ms per read. Its seems that 1015 have same problem because of you select 920 it get worse when it should keep at least the same. Show results of 1115. |
See explanation here: |
Hi! When I try to use the fast_read script on all 4 channels I only get 77sps. Does anyone know the reason? If I only read channel 0 I get 6000 "actual sample rate" |
Hi, I am using this library on Raspberry Pi Zero with ADS1115 to read ADXL335.
I am reading only one axis at the moment and getting sampling rate avg. 214 samples/sec. I would like to increase a speed to 1000samples/sec.
My sample code is modified example:
Which gives me:
Is something like this possible?
Thank you
The text was updated successfully, but these errors were encountered: