I am using the MAX31865 library for the PT100 temp sensor, however it uses delays which are blocking other parts of my sketch. I have tried to modify it to use millis but it does not work. (produces wrong temp reading)
Would be great if someone could point me in the right direction for replacing the delays with a non-blocking method.
the delays are probably needed by the underlying hardware, I suppose they are somewhere in the spec.
if you want to have a fast call, that is an async readRTD(), then since there are 2 delays you need to split the function into 3 parts (3 functions) that you would call from your main loop once enough ms have elapsed. You can do that right in the library if you modify a bit the signature of the function so that it lets you know if it got the data
here is a simple example. the function getValBlocking() would be your existing readRTD() method with the delay(10) and delay(65). if you call it, it's blocking.
the function getValAsync() returns true when the value has been made available (and updates its parameter). You can call it repetitively until the data is available. It's not blocking, so you can do something else whilst waiting for the data (in this example I'm counting with the tick variable)
unsigned long tick = 0;
uint16_t getValBlocking()
{
Serial.println("some initial code");
delay(10);
Serial.println("some middle code");
delay(65);
Serial.println("some final code");
uint16_t val = random(101);
return val;
}
bool getValAsync(uint16_t& val)
{
enum t_state : byte {STATE1, STATE2, STATE3};
static t_state state = STATE1;
static uint32_t chrono = 0;
bool valueAvailable = false;
switch (state) {
case STATE1:
Serial.println("some initial code");
chrono = millis();
state = STATE2;
break;
case STATE2:
if (millis() - chrono >= 10) {
Serial.println("some middle code");
chrono = millis();
state = STATE3;
}
break;
case STATE3:
if (millis() - chrono >= 65) {
Serial.println("some final code");
val = random(101);
valueAvailable = true; // signal computation is done
state = STATE1;
}
break;
}
return valueAvailable;
}
void setup() {
Serial.begin(115200);
}
void loop()
{
uint16_t myResult;
if (getValAsync(myResult)) {
Serial.print("Result is = "); Serial.println(myResult);
while (true); // stop here
}
Serial.println(tick++); // you can do something else whilst the value is being computed
}
Thanks so much for taking the time to help me! I have been studying the code example you provided and I understand how it works now.
However I am struggling a bit to integrate it into the library. I had a few attempts but they are not working, could you give me a final clue to get it working?
Thanks a lot!
Jack
bool getValAsync(uint16_t& val)
{
enum t_state : byte {STATE1, STATE2, STATE3};
static t_state state = STATE1;
static uint32_t chrono = 0;
bool valueAvailable = false;
switch (state) {
case STATE1:
clearFault(); // first section of code
enableBias(true);
chrono = millis();
state = STATE2;
break;
case STATE2:
if (millis() - chrono >= 10) {
uint8_t t = readRegister8(MAX31856_CONFIG_REG); // second section of code
t |= MAX31856_CONFIG_1SHOT;
writeRegister8(MAX31856_CONFIG_REG, t);
chrono = millis();
state = STATE3;
}
break;
case STATE3:
if (millis() - chrono >= 65) {
uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG);
// remove fault
rtd >>= 1;
return rtd;
val = random(101);
valueAvailable = true; // signal computation is done
state = STATE1;
}
break;
}
return valueAvailable;
}
uint16_t Adafruit_MAX31865::readRTD(void) {
uint16_t myResult;
if (getValAsync(myResult)) {
// Serial.print("Result is = "); Serial.println(myResult);
while (true); // stop here
}
}
you are not too far but cannot have a return in the middle of the code or you break the state machine.
an async Method could look like this
bool Adafruit_MAX31865::readRTDAsync(uint16_t& rtd) {
enum t_state : byte {STATE1, STATE2, STATE3};
static t_state state = STATE1;
static uint32_t chrono = 0;
bool valueAvailable = false;
switch (state) {
case STATE1:
clearFault();
enableBias(true);
chrono = millis();
state = STATE2;
break;
case STATE2:
if (millis() - chrono >= 10) {
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
t |= MAX31856_CONFIG_1SHOT;
writeRegister8(MAX31856_CONFIG_REG, t);
chrono = millis();
state = STATE3;
}
break;
case STATE3:
if (millis() - chrono >= 65) {
rtd = readRegister16(MAX31856_RTDMSB_REG); // EDITED FOR CORRECTNESS (SEE @countrypaul POST #11)
rtd >>= 1; // remove fault
state = STATE1; // get ready for next time
valueAvailable = true; // signal computation is done
}
break;
}
return valueAvailable;
}
Note the & in readRTDAsync(uint16_t[color=red]&[/color] rtd). This is passing by reference, meaning that the method will modify the actual memory position of the parameter.
you would call it this way in your loop/code
uint16_t rtd;
if (myMax31865.readRTDAsync(rtd)) {
// the value was ready and we got it in rtd now
...
} else {
// value was not ready do something else
}
This means the call is non blocking so that you can proceed with the other things you need to handle but you have to come back to this readRTDAsync() call often enough or you won't be able to meet the exact timing requirement (I don't know if it's bad if you come back 11ms after instead of 10ms for example. You would need to read the hardware spec for that).
Thanks a lot for your reply, however I am more confused now haha. I get how this would work in a normal sketch but making this work in the library is proving to be challenging!
Epic we are nearly there, thanks for sticking with me. I am defiantly not that experienced just yet haha.
So I added the line to the .h file and the function to the .cpp leaving the current code there with the delays. I understand that we are just calling this new function without the delay. Is that right?
Using this code in my actual program sketch to call the new function. However I get an error saying
uint16_t rtd;
if (myMax31865.readRTDAsync(rtd)) {
// the value was ready and we got it in rtd now
...
} else {
// value was not ready do something else
}
error: 'myMax31865' was not declared in this scope
&
error: expected primary-expression before '...' token
Should I be using this code in my main sketch or have I confused things there? :o
So I am getting the RTD value now all good, thanks so much!
But... I just realised I actually need the temp in degC, for my PID loop of temp control. That calculation is done in the lib using the RTD value, so I would think to just replace the value that we got from the code you provided, but reality does not seam that easy haha. At this point my brain is smoking...
add a new method to the library to calculate the temperature based on an RTD you have acquired asynchronously, ie you just add Rt as a parameter and you don't acquire it in the code. basically almost the same code as the other one minus the line [s] Rt = readRTD();[/s]
then you just call this method once you have read the RTD
uint16_t rtd;
if (myMax31865.readRTDAsync(rtd)) {
// the value was ready and we got it in rtd now
float temp = myMax31865. temperatureAsync(rtd, ...);
...
} else {
// value was not ready do something else
}
you of course need to add the method to the public part of the .h as well.
case STATE1:
clearFault();
enableBias(true);
chrono = millis();
state = STATE2;
break;
case STATE2:
if (millis() - chrono >= 10) {
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
t |= MAX31856_CONFIG_1SHOT;
writeRegister8(MAX31856_CONFIG_REG, t);
chrono = millis();
state = STATE3;
}
break;
case STATE3:
if (millis() - chrono >= 65) {
uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG);
rtd >>= 1; // remove fault
state = STATE1; // get ready for next time
valueAvailable = true; // signal computation is done
}
break;
}
return valueAvailable;
}
In STATE3 what is the point of calling ReadRegister16? It appears to return a value to rtd which is then shifted and then discarded since rtd is declared local within the scope of the case statement.
countrypaul:
In STATE3 what is the point of calling ReadRegister16? It appears to return a value to rtd which is then shifted and then discarded since rtd is declared local within the scope of the case statement.
Gee - You are totally right - my bad.. (I'm writing this from my iPad so never tested)
there should be NO uint16_t in front of that rtd, we want to use the parameter passed by reference!
Good catch. +1 karma!
so corrected code is:
bool Adafruit_MAX31865::readRTDAsync(uint16_t& rtd) {
enum t_state : byte {STATE1, STATE2, STATE3};
static t_state state = STATE1;
static uint32_t chrono = 0;
bool valueAvailable = false;
switch (state) {
case STATE1:
clearFault();
enableBias(true);
chrono = millis();
state = STATE2;
break;
case STATE2:
if (millis() - chrono >= 10) {
uint8_t t = readRegister8(MAX31856_CONFIG_REG);
t |= MAX31856_CONFIG_1SHOT;
writeRegister8(MAX31856_CONFIG_REG, t);
chrono = millis();
state = STATE3;
}
break;
case STATE3:
if (millis() - chrono >= 65) {
rtd = readRegister16(MAX31856_RTDMSB_REG);
rtd >>= 1; // remove fault
state = STATE1; // get ready for next time
valueAvailable = true; // signal computation is done
}
break;
}
return valueAvailable;
}
Hi, jackdavies, can You, please, publish Your modifications? Im just looking for async reading of MAX31865, standard Adafruit library reading is 150ms and thats too long for my code. Thanks.