Recently I bought XIAO RP2040 boards to play around with it. I am trying to use DS18B20 temperature sensor, but the board just hangs when it tries to read the sensor sensors.begin() and I cannot even open Serial monitor when that happens.
I am using one of the standard examples from Arduino library. My wiring is also correct as the same setup works perfectly on ESP8266. Also I tried to use different pins, but again same behaviour. I have 2 boards and they both seems to have same issue.
I am completely lost at this point. Tried to look into Wire library do some modifications, but that way out of my knowledge.
Does anyone else such issue. maybe someone manage to resolve it?
Thanks in advance
// Include the libraries we need
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS D0
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
/*
* The setup function. We only start the sensors here
*/
void setup(void)
{
// start serial port
Serial.begin(9600);
Serial.println("Dallas Temperature IC Control Library Demo");
// Start up the library
sensors.begin();
}
/*
* Main function, get and show the temperature
*/
void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");
// After we got the temperatures, we can print them here.
// We use the function ByIndex, and as an example get the temperature from the first sensor only.
float tempC = sensors.getTempCByIndex(0);
// Check if reading was successful
if(tempC != DEVICE_DISCONNECTED_C)
{
Serial.print("Temperature for the device 1 (index 0) is: ");
Serial.println(tempC);
}
else
{
Serial.println("Error: Could not read temperature data");
}
}
Right after writing that post I manage to get it working by changing OneWire and DallasTemperature libraries. I replaced all delayMicroseconds and delay functions with busy_wait_us and busy_wait_ms.
I cannot really tell the reasoning behind this as I said its beyond my expertise. Also what worked is enabling interrupts before calling delayMicroseconds.
Maybe someone more clever can figure out why its behaving in such way, maybe that should globally be changed in delay.cpp
I was spinning my wheels trying to decide if the direct register access register routines used by this library were creating some hardware problem specific to these boards. (Time to get out the data sheet, or so I thought.)
Well, now I get it (thanks to you):
The idea is that they (usually) disable interrupts before the direct access functions and re-enable interrupts afterward. The problems are, mostly, in places where they disable interrupts, write a bit, delay and then read the bit before re-enabling the interrupts. Or in cases where they disable interrupts in a loop and then exit before re-enabling the interrupts.
Why is this a problem for this board’s software and not for the literally hundreds of other boards that have used Arduino’s OneWire library over the years?
Well…
It turns out that the delay() and delayMicroseconds() functions for this board’s software wait for a timer callback function (invoked by the timer Interrupt Service Routine) to proceed, so if the interrupts are disabled before calling these functions, everything comes to a screeching halt.
On the other hand, the busy_wait_xxx routines just sit in a loop reading the timer register until the designated number of counts has elapsed (i.e. they do not depend on interrupts).
Apparently other boards (at least the ones that use this library successfully) use something like the second approach, and weren’t bothered by interrupts being disabled when calling delay() or delayMicroseconds().
Thank you very much for cluing us in!
Regards,
Dave
Footnote: After making changes to ensure that interrupts were always enabled before calling the delay functions, I tested with the DS18x20 example in the OneWire library. Badda-bing, Badda-boom.
(I think your plan for replacing the calls to the delay functions is preferable.)
I read a bit about busy_wait and sleep differences as I wanted to globally replace them in delay.cpp in rp2040 board libs. That seems to work, but I suppose it might have some further implications like killing all the power saving features as it wont give other resources to their job while delay is happening.
I also would like to thank you as your issue with i2c helped me to get my display working.
Instead of changing the board file, I found that the following works for me:
Reinstall the OneWire library (Delete the directory tree and use LIbrary Manager to re-install it.)
Somewhere around line 19 of OneWire.h (just after all of the #include stuff), insert the following line
#define delayMicroseconds busy_wait_us
Now, all of the libraries that use OneWire will use that non-interrupt-dependent delay.
If you want to use OneWire with a board for which the software does not have busy_wait_us() function, (many, many other boards), you will get a very informative compile error, and will have to comment out that line. Not too scary, and it only changes one line in one file.
This is the minimum that I could figure out that would make OneWire work with the XIAO RP2040. It only affects OneWire functions (and any application files that #include OneWire.h).
I definitely do NOT want to replace delay() in any of the board software because I found, in a number of cases that long delays with interrupts disabled can wreak havoc (watchdog timeout, for example). A few microseconds here and there won’t screw the pooch.
@davekw7x and @sasio thank you so much! You saved me from trying to figure out why I couldn’t get the temp sensor running on the Xiao RP2040. After a couple hours of troubleshooting, it was getting frustrating!