-
-
Notifications
You must be signed in to change notification settings - Fork 33
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
Occasional timeout on readADC() when running on system with RTOS such as ESP6288 #82
Comments
Thanks for reporting this issue and proposing a solution. Will look into it asap, might be a few days. |
I have sent a pull request with the fix yesterday (my first pull request ever) but it seems to have disappeared. |
Strange, please try it again. |
Found it. Its in branch deKees687-patch-1
<https://github.com/deKees687/ADS1X15/tree/deKees687-patch-1>
Regards, Kees
Op do 17 okt 2024 om 10:01 schreef Rob Tillaart ***@***.***>:
… Strange, please try it again.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS4L52ACX7XB6HCKZT3Z35VHNAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJYHA2DANRRGY>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Read once that e.g. the "networking threads" stay happy if it can check about every 5 ms. Propose to place the call to yield() both before and after the timeout test. Something like int16_t ADS1X15::_readADC(uint16_t readmode)
{
_requestADC(readmode);
if (_mode == ADS1X15_MODE_SINGLE)
{
yield(); // yield for RTOS e.g. ESP.
uint32_t start = millis();
// timeout == { 129, 65, 33, 17, 9, 5, 3, 2 }
// a few ms more than max conversion time.
uint8_t timeOut = (128 >> (_datarate >> 5)) + 1;
while (isBusy())
{
if ( (millis() - start) > timeOut)
{
return ADS1X15_ERROR_TIMEOUT;
}
}
yield();
}
else
{
// needed in continuous mode too, otherwise one get old value.
delay(_conversionDelay);
}
return getValue();
} |
I would not do that.
The first yield() does not add much but can do no harm either.
The second yield must be inside the while loop, after the timeout-check. No
yield() between isBusy() and timeout, and always an isBusy() after the
yield()
Just try to imagine what happens if the yield() takes 10mS to complete due
to task switching.
Regards, Kees.
Op do 17 okt 2024 om 10:29 schreef Rob Tillaart ***@***.***>:
… Read once that e.g. the "networking threads" stay happy if it can check
about every 5 ms.
Propose to place the call to *yield()* both before and after the timeout
test.
Something like
int16_t ADS1X15::_readADC(uint16_t readmode)
{
_requestADC(readmode);
if (_mode == ADS1X15_MODE_SINGLE)
{
yield(); // yield for RTOS e.g. ESP.
uint32_t start = millis();
// timeout == { 129, 65, 33, 17, 9, 5, 3, 2 }
// a few ms more than max conversion time.
uint8_t timeOut = (128 >> (_datarate >> 5)) + 1;
while (isBusy())
{
if ( (millis() - start) > timeOut)
{
return ADS1X15_ERROR_TIMEOUT;
}
}
yield();
}
else
{
// needed in continuous mode too, otherwise one get old value.
delay(_conversionDelay);
}
return getValue();
}
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS26VDI6UQBS6Q345ZDZ35YPXAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJYHA4TSMJWGA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
The difference is that I moved yield() completely out of the loop. Need more time to think over the consequences.
Adding the 10 ms is quite a lot for certain data rates as the timeout (specs) is data rate dependent. |
Also note that not all RTOS versions depend on the yield() function to
switch context. Most systems do context switching on timer interrupt, so
you have no control over when they may occur.
So for these cases you need a longer timeout value. I would suggest a 10 mS
minimum.
Op do 17 okt 2024 om 10:29 schreef Rob Tillaart ***@***.***>:
… Read once that e.g. the "networking threads" stay happy if it can check
about every 5 ms.
Propose to place the call to *yield()* both before and after the timeout
test.
Something like
int16_t ADS1X15::_readADC(uint16_t readmode)
{
_requestADC(readmode);
if (_mode == ADS1X15_MODE_SINGLE)
{
yield(); // yield for RTOS e.g. ESP.
uint32_t start = millis();
// timeout == { 129, 65, 33, 17, 9, 5, 3, 2 }
// a few ms more than max conversion time.
uint8_t timeOut = (128 >> (_datarate >> 5)) + 1;
while (isBusy())
{
if ( (millis() - start) > timeOut)
{
return ADS1X15_ERROR_TIMEOUT;
}
}
yield();
}
else
{
// needed in continuous mode too, otherwise one get old value.
delay(_conversionDelay);
}
return getValue();
}
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS26VDI6UQBS6Q345ZDZ35YPXAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJYHA4TSMJWGA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Agree, |
OK, the 10 ms will be added, as it will only delay a failing conversion so. I will make a develop branch and bump the version number for it. |
Indeed.
I would say that a longer timeout is no problem since you always get a
response as long as the system is working properly.
Op do 17 okt 2024 om 10:40 schreef Rob Tillaart ***@***.***>:
… The difference is that I moved *yield()* completely out of the loop.
Which on second sight could cause my code to be blocking way too long.
Need more time to think over the consequences.
Can be solved by postponing the yield until after the timeout-check.
And by allowing a minimum timeout of 10 milliseconds.
Adding the 10 ms is quite a lot for certain data rates as the timeout
(specs) is data rate dependent.
However as timeouts do not happen often this seems acceptable.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS6BE5FEDVF6SQ76BLTZ35ZXNAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJYHEZDCMRVGI>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Yes it does make a big difference:
Imagine:
Is busy() Yes
yield() Context switch in yield takes 10 ms
Is timeout? Yes -> error
Or
Is busy() Yes
Is timeout? No not yet -> continue
yield() Context switch in yield takes 10 ms
Is Busy? No -> readValue
Op do 17 okt 2024 om 10:44 schreef Rob Tillaart ***@***.***>:
… I would not do that.
Agree,
(thinking out loud) however if one adds 10 ms to the timeout is it really
needed to move the yield() after the timeout check?
this check takes a few micros so before or after should not make a big
difference.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS2IF4IZA32ZLGBYVC3Z352GRAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJYHEZDSOBSGU>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Develop branch and PR created. |
Not agree, If the context switch is timer based (as you state also occurs) the switch can still be between the isBusy() check and the timeout check. So moving the yield() call after the timeout check does not solve this and scenario 1 can still occur. The 10 ms extra will catch this. In case of a good conversion,
That said, moving the yield() is for yield() based context switching better so it was moved. |
Can you give the develop branch a try? (now quickly catch up my other work ;) |
Read the https://www.circuitsonline.net/forum/view/167087 thread. It also mentioned that the library returning an error code instead of a value was not smart. if ( (millis() - start) > timeOut)
{
return ADS1X15_ERROR_TIMEOUT;
} Will look into that too.
|
Looks Ok to me, except the timeout values in the comment:
Should now be :
// timeout == { 138, 74, 42, 26, 18, 14, 12, 11 }
Op do 17 okt 2024 om 11:33 schreef Rob Tillaart ***@***.***>:
… @deKees687 <https://github.com/deKees687>
Can you give the develop branch a try?
If OK I will merge it into master and release version 0.5.1
(now quickly catch up my other work ;)
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZASZBGIQM4SN2IXMMZDDZ3576ZAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJZGAZTONZRHE>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
This one is more difficult to fix as it will change the API.
Perhaps just update the examples to tell the users they should use
getError() before using the ADC value.
Op do 17 okt 2024 om 11:49 schreef Rob Tillaart ***@***.***>:
… Read the https://www.circuitsonline.net/forum/view/167087 thread.
It also mentioned that the library returning an error code instead of a
value was not smart.
if ( (millis() - start) > timeOut)
{
return ADS1X15_ERROR_TIMEOUT;
}
Will look into that too.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS72UD5TZFJ5YY46WADZ36B2FAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJZGA3TCMRVGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
errorQuestion is, If timeout what value to return as any value >= 0 incl last valid value is as incorrect as the current -101 constant. Not changing the -101 is at least backwards compatible. Examples should indeed be updated (or at least some) |
Correct, with timer based context switches it makes no difference, only
good for yield() based context switches
I checked Free-RTOS, which is very popular on ESP processors.
This OS uses yield() only between tasks with the same priority level.
Higher-priority tasks can interrupt at any point.
So moving the yield will help a little and the longer timeout will also
help.
But users should still be encouraged to check for getError()
Op do 17 okt 2024 om 11:32 schreef Rob Tillaart ***@***.***>:
… Yes it does make a big difference:
Imagine:
Is busy() Yes
yield() Context switch in yield takes 10 ms
Is timeout? Yes -> error
Or
Is busy() Yes
Is timeout? No not yet -> continue
yield() Context switch in yield takes 10 ms
Is Busy? No -> readValue
Not agree,
If the context switch is timer based (as you state also occurs) the switch
can still be between the *isBusy()* check and the *timeout* check. So
moving the *yield()* call after the timeout check does not solve this and
scenario 1 can still occur.
The 10 ms extra will catch this.
In case of a good conversion,
- the isBusy() returns true and the duration is below the timeout as
specified in the datasheet for actual data rate.
- Assume context switching the timeout check is up to 10 ms later.
- Then the timeout check has the10 ms extra margin to properly detect
the timeout or not.
That said, moving the yield() is for yield() based context switching
better so it was moved.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS3U5OLSZN3CNW27NP3Z357ZPAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJZGAZTINBYGA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Well, indeed. In case of timeout there is no value that can be returned.
The ADS1115 is a 16-bit A/D converter, so valid values are between +32K and
-32K.
That leaves no room for error codes as return value.
Personally I would prefer an API in the form of
bool readADC(int &value)
Which returns false in case of errors and true as well as value when all is
Ok.
Users can then do
if(readADC(value))
handle value
else
handle error
And you may keep the existing function for backward compatibility, perhaps
with a 'deprecated' warning.
Op do 17 okt 2024 om 14:05 schreef Rob Tillaart ***@***.***>:
… error
Question is,
If timeout what value to return as any value >= 0 incl last valid value is
as incorrect as the current -101 constant.
Not changing the -101 is at least backwards compatible.
Examples should indeed be updated (or at least some)
Documentation must be extended.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZAS5FLR7GMDPZXPCUHI3Z36RXFAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJZGM2TEMZXGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
That would be a better interface. Breaking but better, bool readADC(int channel, int &value) |
Created issue for API - #84 |
It is not breaking if you keep the original function for backward
compatibility.
That would give problems in a C only environment but is Ok for a C++
environment like Arduino.
You may even consider a nonblocking interface. No more loops in readADC(),
not waiting for a result.
Just do 1 attempt and return false when ADC is still busy (or error), true
on succes.
Op do 17 okt 2024 om 14:32 schreef Rob Tillaart ***@***.***>:
… That would be a better interface. Breaking but better, *bool readADC(int
channel, int &value)*
Would need to patch more functions in the API to apply this same pattern.
And update and test all examples.
Will be a major update so I'll make a separate issue for that.
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZASYGCKHMWVRJMVTEVCLZ36U5NAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJZGQYTMNJTGA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Is already available, from readme.md To read the ADC in an asynchronous way (e.g. to minimize blocking) you need call three functions:
in terms of code: void setup()
{
// other setup things here
ADS.setMode(1); // SINGLE SHOT MODE
ADS.requestADC(pin);
}
void loop()
{
if (ADS.isReady())
{
value = ADS.getValue();
ADS.requestADC(pin); // request new conversion
}
// do other things here
} |
Yes indeed. Users who want to go non-blocking can do just that with the
existing interface.
So no need to provide an alterntive for that.
An offering a single blocking call that does a complete conversion is also
something to be expected from such a library.
Without any further changes, it is already possible to check for errors
using the getError() function. Perhaps that is good enough.
Just tell your users to do:
Value = readADC()
if (getError() == ADS1X15_OK)
Use value
else
handle error
Op do 17 okt 2024 om 16:37 schreef Rob Tillaart ***@***.***>:
… You may even consider a nonblocking interface.
Is already available, from readme.md
------------------------------
To read the ADC in an asynchronous way (e.g. to minimize blocking) you
need call three functions:
- *void requestADC(uint8_t pin = 0)* Start the conversion. pin = 0..3.
Default pin = 0 as this is convenient for 1 channel devices.
- *bool isBusy()* Is the conversion not ready yet? Works only in
SINGLE mode!
- *bool isReady()* Is the conversion ready? Works only in SINGLE mode!
(= wrapper around *isBusy()* )
- *int16_t getValue()* Read the result of the conversion.
in terms of code:
void setup()
{
// other setup things here
ADS.setMode(1); // SINGLE SHOT MODE
ADS.requestADC(pin);
}
void loop()
{
if (ADS.isReady())
{
value = ADS.getValue();
ADS.requestADC(pin); // request new conversion
}
// do other things here
}
—
Reply to this email directly, view it on GitHub
<#82 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/A67ZASZG2LTFFRMO6I6T6A3Z37DRXAVCNFSM6AAAAABQDDX3COVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMJZG4ZTGMJTGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Discussions about API can continue in #84 |
The readADC() functions uses a very short timeout of only a few milliseconds more than the actual conversion time. The yield() function may cause a context switch resulting in a delay for several milliseconds. As a result, the readADC() reports a timeout.
See https://www.circuitsonline.net/forum/view/167087
Can be solved by postponing the yield until after the timeout-check.
And by allowing a minimum timeout of 10 milliseconds.
The text was updated successfully, but these errors were encountered: