Closed Mplex72 closed 3 years ago
The ADS1115 has only one ADC in it that is multiplexed.
This means an application cannot request channel 2 while acquiring channel 1
So you have to wait until a sample is made.
The trick used in the last sketch is to run 4 devices in parallel. That keeps the waiting down.
Apparently you have a requirement that the samples need to be made in the same window of 10 msec or less. Is that correct?
Do you have a hard 16 bit requirement or would 12 bit (ADS1015) be sufficient?
For better performance you should remove all the print statements (except printing the read values)
idea for improvement Now the code checks the isBusy() of every device. That takes time. If you know how much time it takes you can just set a clock and after e.g 2000 (to be determined) microseconds start with reading the sensors. Checking micros() takes a few microseconds while checking isBusy() takes microseconds. Given that the code might check four of them, this adds up
Give this a try
bool ADS_read_all()
{
static uint32_t lastTime = 0;
if (micros() - lastTime < 2000) return true;
lastTime = micros();
val0[idx] = ADS0.getValue();
val1[idx] = ADS1.getValue();
idx++;
if (idx < 4)
{
ADS_request_all();
return true;
}
idx = 0;
return false;
}
I had the idea to sample (low) datarate@475 SpS for lowest possible noise that should be enough for 100SpS for 4 values
Question: Sampling does that include the selection of the channel and the transport of the sampled value?
I had the idea to sample (low) datarate@475 SpS for lowest possible noise that should be enough for 100SpS for 4 values
Question: Sampling does that include the selection of the channel and the transport of the sampled value?
Yes , I tought to get the whole cyclus within 9 or 10 millis. and yes preferably all 16 values within the same 10 millis sampled. 100Hz output for all 16values in the Can messages.
I can go to the ads1015 when I keep the measurement range smaller. the last bits is perhaps only noise
If you really need to do all the samples in a small timeframe and 12 bit is sufficient, you might consider my MCP_ADC library in combination MCP3208. It is a SPI device that can read quite fast, my tests on an UNO about 8 channels in 1 millisecond. It is however not that precise...
If you really need to do all the samples in a small timeframe and 12 bit is sufficient, you might consider my MCP_ADC library in combination MCP3208. It is a SPI device that can read quite fast, my tests on an UNO about 8 channels in 1 millisecond. It is however not that precise...
with 1 milli times I can average over 3 or 4 samples to get very usefull 12 bits ?
doing the loop in , 11 millis
ADS_print_all
41412 -1 -1 1 1 -1 -1 -1 -1
ADS_request_all
loop
ADS_request_all
ADS_request_all
ADS_request_all
ADS_print_all
42422 -1 -1 0 0 -2 -2 -1 -1
ADS_request_all
loop
ADS_request_all
ADS_request_all
ADS_request_all
ADS_print_all
43431 1 1 -1 -1 -1 -1 -2 -2
ADS_request_all
loop
ADS_request_all
ADS_request_all
ADS_request_all
ADS_print_all
If you really need to do all the samples in a small timeframe and 12 bit is sufficient, you might consider my MCP_ADC library in combination MCP3208. It is a SPI device that can read quite fast, my tests on an UNO about 8 channels in 1 millisecond. It is however not that precise...
with 1 milli times I can average over 3 or 4 samples to get very usefull 12 bits ?
In theory that should work. .
What is this last run with all the -1 and -2?
Answer: update that is GND level fluctuating
Sorry , I have it now running with input signals This is not working as it gives no or random output . the older version from the beginning are also not working
This is not working as it gives no or random output .
what are you referring to with this ? I cannot see what you see...
it are zero values from the voltage divider
voltage divider?
its the zero value drifting around (16th bit is minus signal)
this is voltage 5volt on ADC1 channel 3
35378 25 25 19 19 -1 -1 -2 -2
ADS_request_all
loop
IDX: 0
ADS_request_all
IDX: 1
ADS_request_all
IDX: 2
ADS_request_all
IDX: 3
ADS_print_all
36387 28 28 20 20 -1 -1 -3 -3
ADS_request_all
5volt signal on ADC2 channel2
ADS_request_all
loop
IDX: 0
ADS_request_all
IDX: 1
ADS_request_all
IDX: 2
ADS_request_all
IDX: 3
ADS_print_all
131359 -2 -2 0 0 11117 11117 0 0```
with the isbusy check working
```cpp
bool ADS_read_all()
{
// if (ADS0.isBusy() || ADS1.isBusy()) return true;
Serial.print("IDX:\t");
Serial.println(idx);
val0[idx] = ADS0.getValue();
val1[idx] = ADS1.getValue();
idx++;
if (idx < 4)
{
ADS_request_all();
return true;
}
idx = 0;
return false;
This is the output with a normal read (ADS-read) from the lib voltage dider active.
Analog0: -1 -0.000
Analog1: 92 0.017
Analog2: 11095 2.080
Analog3: 10 0.002
Analog0: 4 0.001
Analog1: 91 0.017
Analog2: 11098 2.081
Analog3: 7 0.001
Analog0: 3 0.001
Analog1: 89 0.017
Analog2: 11133 2.088
Analog3: 6 0.001
Analog0: 3 0.001
Analog1: 90 0.017
Analog2: 11110 2.083
Analog3: 12 0.002
I do not understand your last 6 post.
Is the problem solved now?
Hi Rob,
Having problems with the read buffers ,
when running the sketch with real data it does not refresh the buffers.
no faults when compiling and don,t know it,s software or hardware related yet.
still puzzling to get it working as this should.
Take some time so close this for now ?
Klass
I have ordered 4x 1115 + 4x 1015, expect them Wednesday. So if time permits I can recreate the setup (without can-bus) later this week.
Would be fine to have this working . it is a pretty efficient method
The sensors are in, I had my soldering moment so made this setup
With the green wire (GND) on the right I checked every input and reading 4 ADS1115 in parallel works.
//
// FILE: ADS_async_8_channel.ino
// AUTHOR: Rob Tillaart
// VERSION: 0.1.0
// PURPOSE: demo reading two ADS1115 modules in parallel
// DATE: 2021-07-05
// URL: https://github.com/RobTillaart/ADS1X15
// Note all IO with the sensors are guarded by an isConnected()
// this is max robust, in non critical application one may either
// cache the value or only verify it in setup (least robust).
// Less robust may cause the application to hang - watchdog reset ?
#include "ADS1X15.h"
ADS1115 ADS0(0x48);
ADS1115 ADS1(0x49);
ADS1115 ADS2(0x4A);
ADS1115 ADS3(0x4B);
int16_t val0[4] = { 0, 0, 0, 0 };
int16_t val1[4] = { 0, 0, 0, 0 };
int16_t val2[4] = { 0, 0, 0, 0 };
int16_t val3[4] = { 0, 0, 0, 0 };
int idx = 0;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("ADS1X15_LIB_VERSION: ");
Serial.println(ADS1X15_LIB_VERSION);
ADS0.begin();
ADS1.begin();
ADS2.begin();
ADS3.begin();
Serial.println(ADS0.isConnected());
Serial.println(ADS1.isConnected());
Serial.println(ADS2.isConnected());
Serial.println(ADS3.isConnected());
idx = 0;
ADS_request_all();
}
void loop()
{
Serial.println(__FUNCTION__);
// wait until all is read...
while (ADS_read_all());
// we have all 8 values
ADS_print_all();
delay(1000); // wait a second.
ADS_request_all();
}
void ADS_request_all()
{
// Serial.println(__FUNCTION__);
if (ADS0.isConnected()) ADS0.requestADC(idx);
if (ADS1.isConnected()) ADS1.requestADC(idx);
if (ADS2.isConnected()) ADS2.requestADC(idx);
if (ADS3.isConnected()) ADS3.requestADC(idx);
}
bool ADS_read_all()
{
if (ADS0.isConnected() && ADS0.isBusy()) return true;
if (ADS1.isConnected() && ADS1.isBusy()) return true;
if (ADS2.isConnected() && ADS2.isBusy()) return true;
if (ADS3.isConnected() && ADS3.isBusy()) return true;
//Serial.print("IDX:\t");
//Serial.println(idx);
if (ADS0.isConnected()) val0[idx] = ADS0.getValue();
if (ADS1.isConnected()) val1[idx] = ADS1.getValue();
if (ADS2.isConnected()) val2[idx] = ADS2.getValue();
if (ADS3.isConnected()) val3[idx] = ADS3.getValue();
idx++;
if (idx < 4)
{
ADS_request_all();
return true;
}
idx = 0;
return false;
}
void ADS_print_all()
{
// Serial.println(__FUNCTION__);
// TIMESTAMP
Serial.println(millis());
// PRINT ALL VALUES OF ADC0
for (int i = 0; i < 4; i++)
{
Serial.print(val0[i]);
Serial.print("\t");
}
// PRINT ALL VALUES OF ADC1
for (int i = 0; i < 4; i++)
{
Serial.print(val1[i]);
Serial.print("\t");
}
Serial.println();
// PRINT ALL VALUES OF ADC2
for (int i = 0; i < 4; i++)
{
Serial.print(val2[i]);
Serial.print("\t");
}
// PRINT ALL VALUES OF ADC3
for (int i = 0; i < 4; i++)
{
Serial.print(val3[i]);
Serial.print("\t");
}
Serial.println();
}
// -- END OF FILE --
Output
ADS1X15_LIB_VERSION: 0.3.1
1
1
1
1
loop
62
2904 2946 3044 2850 2924 2979 3065 2880
2883 2934 -1 2917 2902 2973 3047 2946
loop
1122
2880 2950 3021 2942 2893 2970 3035 2999
2874 2935 -1 2996 2910 2955 3021 3041
loop
2183
2903 2925 2985 3023 2941 2937 3002 3067
2914 2911 -1 3052 2950 2925 2987 3101
The -1 is where I connected GND.
A loop takes 1061 millis of which 1000 is a delay, so total time = 61/62 millis ==> requesting + reading of 4 x 4 analog channels takes (rounded) 64 millis ==> 16 milliseconds for 4 devices in parallel.
Made a PR with two new examples including the above.
THAT IS FAST ! You,ve extracted the maximum out of this IC
Now do some crashtest but so far it seems perfectly stable , great , really great. I think this issue is closed as far as I can see ?
with the code in the push release not all values show zero. otherwise working perfect.
the code above with separate constructors is ok
1057
0 65534 0 0
1 65533 65535 1
1 65534 0 0
0 65534 65535 0
1057
1 65534 65535 0
3 65534 0 0
0 65533 0 0
2 65535 65535 0
1056
65535 65534 0 65535
2 65534 0 65534
1 65535 65535 1
0 65534 65535 0
1057
2 65534 0 1
1 65534 0 65535
0 65534 65535 0
0 65534 65535 0
Note that the value should be a 16 bit signed int ==>15 bits + sign
65535 is just an unsigned way to write -1 (and that is close to zero last time I looked)
Tweaked the code a bit and I got a few millis of
loop
1049
3181 2787 3028 3068 3241 2744 3026 3100
-1 2850 2848 3330 3229 2873 2908 3301
49 / 4 =~ 12 milliseconds per 4 samples (but the code looks not nice)
Yers I know that its zero but in the logs it looking very eratic. May I see the fast code ? Is it with serial output after each 4 samples ?
May I see the fast code ?
changed it already, It did 2 things
Need to recreate it.
MY current test uses the variation below uses the assumption that as ADS3 is started last, it will also be finished last.
bool ADS_read_all()
{
// if (ADS0.isBusy()) return true;
// if (ADS1.isBusy()) return true;
// if (ADS2.isBusy()) return true;
if (ADS3.isBusy()) return true;
val0[idx] = ADS0.getValue();
val1[idx] = ADS1.getValue();
val2[idx] = ADS2.getValue();
val3[idx] = ADS3.getValue();
//Serial.print("IDX:\t");
//Serial.println(idx);
idx++;
if (idx < 4)
{
ADS_request_all();
return true;
}
idx = 0;
return false;
}
it scores 1054
Also quite stable at 1056
bool ADS_read_all()
{
if (ADS0.isBusy()) return true;
val0[idx] = ADS0.getValue(); // fetch value as soon as possible.
if (ADS1.isBusy()) return true;
val1[idx] = ADS1.getValue();
if (ADS2.isBusy()) return true;
val2[idx] = ADS2.getValue();
if (ADS3.isBusy()) return true;
val3[idx] = ADS3.getValue();
//Serial.print("IDX:\t");
//Serial.println(idx);
idx++;
if (idx < 4)
{
ADS_request_all();
return true;
}
idx = 0;
return false;
}
With extra administration of the status per device the performance could be squeezed a bit more. However it would become quite complex and you still have to wait until the slowest device has made the 4 scans.
To speed up the code you might add these 4 lines in setup() after the connected test
ADS0.setDataRate(7);
ADS1.setDataRate(7);
ADS2.setDataRate(7);
ADS3.setDataRate(7);
default dataRate = 4, 7 is fastest, but 7 allows more noise, so depending on the quality of your signals increasing the dataRate improves the performance.
A datarate of 4 gives 128 samples / second = 8 millisecond per sample. As we do 4 samples the theoretical minimum == 4x 8 = 32. Add to it the duration of the calls of request(), isBusy() and getValue() (e.g. 3x12 x 0.5 ms ??? => 18 ms makes 50 millis ==> so 56 millis is not bad at all.
It is pretty maxed out this way ! The ADC is just slower as I was aware of because I didn,t read/understand it fully . I had it already on rate(6) and I have a capacitor on it to smooth the signal a little.
getting the 16bytes to the CANbus takes also 50 millis .... not able to find a faster way.
getting the 16bytes to the CANbus takes also 50 millis .... not able to find a faster way.
Mind you, you have 16 values of 2 bytes each...
You need to read every detail of the data sheet to see what is possible. And then find a good / efficient library that can get the most from the IC used.
However the ADS part, think we can close this issue.
indeed for the 32 bytes.
Yes , case closed thanks to your hard work , thank you .
a last look at your canbus code, gave a few ideas how to optimize it
void ADS0_read()
{
byte stmp[32]; // assume 16 x 2
uint8_t idx = 0;
for (uint8_t i = 0; i < 16; i++)
{
// assume all values are in val[] array by the above code
// val[i] = val[i] * 0.1875; // ==> 0.1875 * 3 /16
// ==> x = x * 3/ 16 = x * 1/8 + x * 1/16
// ==> x = x /8 + x /16;
// ==> x/16 == x/8 /2 !!
uint16_t temp = val[i] / 8; // no floating point math needed, and compiler can optimize division of powers of 2 !!
val[i] = temp + temp / 2;
// stmp[ i * 2] = val[i] / 256;
// stmp[ i * 2 + 1] = val[i] % 256;
// you split the data in 2 bytes here
// furthermore the index of smtp is "a lot of math" which can be simplified as we just need the next element.
stmp[idx++] = val[i] / 256; // power of 2 will be optimized.
stmp[idx++] = val[i] & 0xFF; // just do bit masking instead of % division - compiler might do that too?
// send data: id = 0x70, standard frame, data len = 32, stmp: data buf
CAN0.sendMsgBuf(0x70, 0, 32, stmp);
}
}
Get the idea?
however filling the array may not be the time consuming part....
last note - you still could try the ADS1015 (which is faster) and the MCP3208 if you need extra speed (and less resolution)
GIven that the data is roughly divided by 4 when sending over the CAN bus a 12 bit conversion could be good enough..
Hi ,
How can I read 4 channels asynchronus ? The problem is the readout for each value see line below ;
and gives the following error ; "invalid types 'int16_t {aka int}[int]' "
I have the followiing sketch to test ;
Klass