DS18B20 + TLC5940: Invalid Sensor Readings

Hello,
Long time reader. First time poster. Great forum and thanks for all your help so far!!

I have a project where I am bringing several chips/sensors together to automate a system (brewing). I everything was working great until I added 4x TLC5940’s. However, the problems are not with the TLC5940’s. I am now having far too many invalid sensor readings (-196 degF) and am struggling to understand the root cause so I can begin working on a solution.
I plan to use the 2x TLC5940 to drive LEDs showing hardware status and 2x TLC5940 to control solid state relays which will energize the higher voltage/current valves & pumps.
I have stripped the hardware/software down to the Arduino Mega 2560, 2x LCD’s (I2C / Ext powered), and custom PCB which houses only LEDs and 2x TLC5940 (SPI / Ext powered).

I am not waiting for conversion with the Dallas RTD’s but instead read the values 1 second after requesting them. Per the data sheet I should only need 750 mS. I have also tried increasing the time between reading/calling sensors with no improvement.

At first I thought that the problem was the time it was taking to update the TLC5940 since the following things reduced the number of invalid temp sensor readings:

  • Commenting out all of the lines in ‘ValvePump_Loop’ code eliminates the problem
  • Commenting out >=10 of the lines in ‘ValvePump_Loop’ code eliminates the problem
  • Commenting out <10 of the TLC code and the problem gets worse with every used line of code
  • Dropping the grey scale value to the TLC reduces invalid sensor readings

There is no appreciable difference when I increase the update frequency (UpdateHz) of the TLC’s (even to 10 seconds / I really need 1 Hz). Also, it is only taking ~400 uS to perform all functions associated with the TLC (see variable “duration”).
That said, it does appear to be a timing problem of sorts because the % of invalid readings are related to their address and not by the order which I “getTempF()”. Therefore I am assuming that the “requestTemperatures()” command requests them by address and either the earlier or later the sensor is called, possibly the more likely it is to return an error when I request its value through “getTempF()”.

The Request
Does anyone have advice on something I can measure/investigate to find the root cause as to what is causing the invalid temp sensor readings? I feel like I am overlooking something but have run out of ideas…

I cut out all of the code I could without affecting the issue I am having. Please let me know if something isn’t clear.

Thanks in advance is anyone posts any ideas. Please let me know if you have any questions.

The code… :slight_smile:

//Libraries
  #include <OneWire.h>            //Enables use of one wire temperature sensors
  #include <DallasTemperature.h>  //Enables support of specific Dallas temperature sensors
  #include <Wire.h>               //Needed to communicate with data logger
  #include <LiquidCrystal_I2C.h>  //I2C LCD Librarry
  #include <Tlc5940.h>            //Library for TLC5940

// Constructed Objects for LCD
  LiquidCrystal_I2C lcd1(0x27,20,4);  
  LiquidCrystal_I2C lcd2(0x26,20,4); 

//Temperature Sensors all connected through a single pin
  #define ONE_WIRE_BUS 19
//Sets the pin to read the bus
  OneWire RTD_oneWire(ONE_WIRE_BUS);           // Setup a oneWire instance to communicate with any OneWire devices
  DallasTemperature RTDsensors(&RTD_oneWire);  // Pass our oneWire reference to Dallas Temperature. 
//Define the address of each RTD on the bus
  DeviceAddress HLT_T1 = { 0x28, 0x5D, 0x08, 0x70, 0x06, 0x00, 0x00, 0x3D };  //Green: Bus address for the specific sensor
  DeviceAddress MashT1 = { 0x28, 0xB4, 0x28, 0x6F, 0x06, 0x00, 0x00, 0xD8 };  //Blue: Bus address for the specific sensor
  DeviceAddress MashT2 = { 0x28, 0xF3, 0x94, 0x6E, 0x06, 0x00, 0x00, 0xC9 };  //Orange: Bus address for the specific sensor
  DeviceAddress MashT3 = { 0x28, 0xAC, 0x50, 0x70, 0x06, 0x00, 0x00, 0xD3 };  //Pink: Bus address for the specific sensor
  DeviceAddress BK_T1 = { 0x28, 0xC0, 0x1A, 0x6F, 0x06, 0x00, 0x00, 0x9B };   //Black: Bus address for the specific sensor
//Limit the update frequency of the RTD's
  int RTD_MillisDelay = 1000;
  unsigned long RTD_lastRequested = 0;
//Define the variables used to store temperatures
  double HLT_TempF = 0;        //HLT temperature
  double MashTempF_T1 = 0;     //Mash temperature - 1
  double MashTempF_T2 = 0;     //Mash temperature - 2
  double MashTempF_T3 = 0;     //Mash temperature - 3
  double BK_TempF = 0;         //BK temperature

//TLC5940 Varaibles
  int LED_Brightness = 600;      //PWM signal for LED brightness (4095 = 100%)
  int UpdateHz = 10000;          //Millis - Update frequency
  unsigned long TLCmillis = 0;   //Millis - Time tracking variable for TLC update frequency
//Machine State variables. 
  byte ValveState = 0;          // 0 = Off or Closed / 1 = On or Open
  
//Variables specifically for debug investigation
  int ErrorH = 0;
  int ErrorM1 = 0;
  int ErrorM2 = 0;
  int ErrorM3 = 0;
  int ErrorB = 0;
  double attempts = 0;
  double duration = 0;
  
//=====================================================================================================
void setup(){
// put your setup code here, to run once:
  lcd1.init(); 
  lcd2.init(); 
  lcd1.backlight();
  lcd2.backlight();
// Call Tlc.init() to setup the tlc. You can optionally pass an initial PWM value (0 - 4095) for all channels
  Tlc.init();  
// other setup routines
  RTD_Setup();             //Defines the RTD bus addresses & output data resolution
}
//=====================================================================================================
void loop(){
//Read sensors
  TempSensors();
//Control Valves/Pumps
  ValvePump_Loop();
}
//=====================================================================================================
void RTD_Setup(){
// Start up the library
  RTDsensors.begin();
//Define Sensor resolutions
  RTDsensors.setResolution(HLT_T1, 12); //Green: Bus address for the specific sensor
  RTDsensors.setResolution(MashT1, 12); //Blue: Bus address for the specific sensor
  RTDsensors.setResolution(MashT2, 12); //Orange: Bus address for the specific sensor
  RTDsensors.setResolution(MashT3, 12); //Pink: Bus address for the specific sensor
  RTDsensors.setResolution(BK_T1, 12);  //Black: Bus address for the specific sensor
//True = Library will use delay() functions to prevent from reading sensors too often. This ties up the processor and prevent further activities.
//False= This allows other processes to occur while waiting for the sensors to read. User must take precautions not to read too often.
  RTDsensors.setWaitForConversion(false); 
//Initiates sensor request for first reading
  RTDsensors.requestTemperatures();
}
//=====================================================================================================
void ValvePump_Loop(){
//Control update frequency to avoid errors. TLC.update() takes a loooong time to update
  if(millis() > TLCmillis){
    TLCmillis = millis() + UpdateHz;
  //Set the initial state of duration
    duration = micros();
  //Blink the LEDs to signal when routine is called
    ValveState = abs(ValveState - 1);
  //Clear GS values
    Tlc.clear();
  //Set the TLC5940 machine state values for the system machine state values to operate the hardware and LEDs
    TLC_Update(ValveState, 58, 26);
    TLC_Update(ValveState, 54, 22);
    TLC_Update(ValveState, 48, 16);
    TLC_Update(ValveState, 46, 14);
    TLC_Update(ValveState, 42, 10);
    TLC_Update(ValveState, 38, 6);  // if you comment out this line and below, the issue goes away
    TLC_Update(ValveState, 32, 0);  // if you comment out everything below here, the issue begins to appear
    TLC_Update(ValveState, 56, 24); // the invalid sensor reading just gets worse as more of these lines are included
    TLC_Update(ValveState, 52, 20);
    TLC_Update(ValveState, 50, 18);
    TLC_Update(ValveState, 44, 12);
    TLC_Update(ValveState, 40, 8);
    TLC_Update(ValveState, 36, 4);
    TLC_Update(ValveState, 62, 30);
    TLC_Update(ValveState, 60, 28);
  //Tlc.update() sends the data to the TLCs.  This is when the LEDs will actually change
    Tlc.update();
  //Capture the duration to run the TLC code. Printed on the LCDs later
    duration = micros() - duration;
  }
}
//=====================================================================================================
void TLC_Update(byte MachineState, byte Hardware_Pin, byte LED_Pin){
//Set the TLC5940 machine state values for the system machine state values to operate the hardware and LEDs
  if(MachineState == 0){
    Tlc.set(Hardware_Pin, LED_Brightness);
    Tlc.set(LED_Pin, LED_Brightness);
  }
  else{
    Tlc.set(Hardware_Pin + 1, LED_Brightness);
    Tlc.set(LED_Pin + 1, LED_Brightness);
  }
}
//=====================================================================================================
void TempSensors(){
//Limit the update frequency of the RTD's
  if(millis() > RTD_lastRequested){
    RTD_lastRequested = millis() + RTD_MillisDelay;
  //Retrieves temperature from RTD's
    HLT_TempF = RTDsensors.getTempF(HLT_T1);     //ZERO invalid readings over a 1000 second cycle
    MashTempF_T1 = RTDsensors.getTempF(MashT1);  //Third worst
    MashTempF_T2 = RTDsensors.getTempF(MashT2);  //Second worst
    MashTempF_T3 = RTDsensors.getTempF(MashT3);  //Worst one for invalid readings
    BK_TempF = RTDsensors.getTempF(BK_T1);       //Hardly any invalid readings over a 1000 second cycle
  //Initiates sensor request for next reading
    RTDsensors.requestTemperatures();

//Everything below this line is only for debugging------------------------------------------------------
  attempts ++;
  if(HLT_TempF < 0){
    ErrorH++;
  }
  if(MashTempF_T1 < 0){
    ErrorM1++;
  }
  if(MashTempF_T2 < 0){
    ErrorM2++;
  }
  if(MashTempF_T3 < 0){
    ErrorM3++;
  }
  if(BK_TempF < 0){
    ErrorB++;
  }  
//LCD1
  //Line 1
    lcd1.setCursor(0,0);
    lcd1.print("HLT= ");
    lcd1.print(HLT_TempF,1);
    lcd1.print(" BK= ");
    lcd1.print(BK_TempF,1);
    lcd1.print("  ");
  //Line 2
    lcd1.setCursor(0,1);
    lcd1.print("Mash1= ");
    lcd1.print(MashTempF_T1,1);
    lcd1.print("   ");
  //Line 3
    lcd1.setCursor(0,2);
    lcd1.print("Mash2= ");
    lcd1.print(MashTempF_T2,1);
    lcd1.print("   ");
  //Line 4
    lcd1.setCursor(0,3);
    lcd1.print("Mash3= ");
    lcd1.print(MashTempF_T3,1);
    lcd1.print("   ");
//LCD2
  //Line 1
    lcd2.setCursor(0,0);
    lcd2.print("#= ");
    lcd2.print(attempts, 0);
    lcd2.print(" uS= ");
    lcd2.print(duration, 0);
    lcd2.print("  ");
  //Line 2
    lcd2.setCursor(0,1);
    lcd2.print("H= ");
    lcd2.print(100.0 * ErrorH / attempts, 1);
    lcd2.print(" B= ");
    lcd2.print(100.0 * ErrorB/attempts, 1);
  //Line 3
    lcd2.setCursor(0,2);
    lcd2.print("M1= ");
    lcd2.print(100.0 * ErrorM1/attempts, 1);
    lcd2.print(" ");
    lcd2.print("M2= ");
    lcd2.print(100.0 * ErrorM2/attempts, 1);
    lcd2.print(" ");
  //Line 4
    lcd2.setCursor(0,3);
    lcd2.print("M3= ");
    lcd2.print(100.0 * ErrorM3/attempts, 1);
  }
}

bheinz: I feel like I am overlooking something but have run out of ideas....

I feel you are just making things too complicated.

Your entire "RTD setup" is redundant. DS18B20s run 12bit by default, and are not RTDs anyway.

A simple Delay(1000); Should suffice for all your timing needs.

Your "ValvePump_Loop()" is probably not a loop, just a subroutine.

Your reading -190 probably indicates a simple bad connection, which is a lot easier to fix than an "invalid sensor connection".

You might look here http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html for using the DS18B20. It isn't much different from what you are doing, but is a lot easier to read.

Thanks for clarifying that these are not RTD’s. Not sure where I got that acronym from. I’ll update and re-label the “Valvepump” routine to something more accurate later.

I do tend to put things into my setup routines which are redundant. I am still learning and it helps me remember what levers I can pull if I keep them readily available. Typically I tuck the setup routines away on their own tab to keep the clutter down.

Whether the temp sensor code is complicated is a matter of opinion. The code which calls/passed temperatures to their respective variables is ~10 lines of code for 5 sensors. You are correct that it could be simpler if I used a delay. However, I purposely prevented the library from using its built in delay by “setWaitForConversion(false)”. In the bigger picture of my program I cannot wait the 750 ms for the sensors to return a value (all code not shared b/c ~4 thousand lines of code atm).

I did find a promising lead today. Even though everything except the temp sensors connected to my arduino are powered by an external wall wart, the problem goes away if I simply do not power the LEDs. If I keep power to the TLC’s but unplug the serial-in wire (effectively turning off LED’s) the problem goes away again.
20mA * 15 LEDs = 300mA pulling from a 2600mA power supply. Should be plenty of power so I have some investigation to do.

Summary
95% sure have a hardware issue… There is an interaction between the components powered externally and the temperature sensors powered via Arduino 5v. My arduino 5v is more like 4.8v and my external supply is closer to 5.1v. Time to dust off the multimeter and see if I can figure anything out.

Probably not helpful at this point but I did attach a picture of this project. The black mass of wires coming out of the bottom are the temp sensors.

I am assuming that the "requestTemperatures()" command requests them by address

No. In effect, it sends a broadcast command to all the sensors on the bus to do a temperature conversion so that they all do their conversion simultaneously. You then request the temperatures (with RTDsensors.getTempF) in a specific order. The way that you are timing the delay between the request and the readings looks good.

Are you using parasite mode? If so, do you have all of the sensors wired for parasite mode? If any one of them is parasite, all of them must be.

Your entire "RTD setup" is redundant.

There's nothing wrong with making sure that the sensors are initialized to a known state. It only takes on the order of microseconds and is only run once anyway.

But I agree that the reading of -196F "probably indicates a simple bad connection".

Pete

bheinz: My arduino 5v is more like 4.8v and my external supply is closer to 5.1v. Time to dust off the multimeter and see if I can figure anything out......... Probably not helpful at this point but I did attach a picture of this project.

You're right, it isn't. Far more useful would be a view of the back showing the power connection, and the wiring of all those LEDs.

It sounds like you are providing power through the barrel jack, so I submit that dusting off the data sheet will be a lot more useful. The minimum supply to the input is 7v but a kosher 5v may be applied to the 5v pin.

That LCD installation looks very good............

First, thanks for responding :slight_smile:

@el_supremo:
I am not using parasite mode. Vdd of sensor to +5v (well ~4.8v) of the arduino with 4.7k resistor to the data line. Thanks for clarifying how the “requestTemperatures()” and “getTempF()” commands.

To that point, I will pull out a 9v wall wart and use it to power the arduino this weekend to see if that brings the arduino Vcc closer to 5v.

@Nick_Pyner:
Currently powering Arduino via USB & TLC5940’s via external 5v.

I have attached another picture which may or may not help. Things are pretty tight under the hood (for my liking). I labelled all of the hardware in the pic BUT I disconnected the SD & servo when this issue popped up to cut down on variables. Everything (including ext power supply) are connected to a common ground.

The LEDs are all on the white Fzing PCB. I also designed/ordered a shield clean up the rest of it but there was some confusion and it shipped to my billing address instead of the shipped address… So… it is on the other side of the Atlantic and I won’t have it until June 20th. :confused:

I am going to investigate some more this weekend. If I find anything I will report back. Otherwise I will shelf this project until the shield arrives. Then I will report a shining success shortly after fingers crossed

4.8V shouldn't be a problem.

Pete

I forgot to include that I did do some testing without touching the hardware.

If I set the grey scale value of all LEDs to anything less than 60 (4095 is 100% on / 12-bit PWM?), I get zero invalid sensor readings over 1000 seconds.
As I increase the frequency/ontime of the LEDs, the invalid sensor readings being to increase. Somewhere before 4095, 2x of the sensors report 100% invalid readings and the other 2x report somewhere between 50% and 75% invalid readings and the last one reports only good readings over 1000s.

I am still in the camp of a bad connection somewhere. Just including this for completeness.

How much current is the 5V supply rated to produce? It might be underpowered. Alternatively, add a capacitor from +5V to ground on the 5V supply. I'm not sure of the value (I'm not a hardware guy) but try 10uF to 100uF.

Pete

bheinz:
To that point, I will pull out a 9v wall wart

@Nick_Pyner:
Currently powering Arduino via USB

The wall wart sounds like a good idea. The 4.8v you have measured may well confirm what the problem has been all along - inadequate power from the USB - and the hogs may be the LCD backlights. The DS18B20 get by on very little, but I guess anything can happen if the supply isn’t up to the job and that might include the “not connected” error. If the external 5v is up to it, you might use that for everything.

Drum roll..... The problem was a bad connection somewhere. Don't know if it was my poorly made board or the actual connection between the sensors and the board.

It now operates with zero invalid readings on all sensors.

Thanks again and sorry for any wasted effort!!

[u]Post Mortem future readers[/u]

Basic HW: 5x DS18B20 sensors in system reporting through a single pin on MEGA2560. Basic SW: Code based upon "WaitforConversion2" example in library with setWaitForConversion(false) Symptom: Some would return invalid readings. Some sensors worse than others. Solution: Went to great lengths to make sure that each sensor has a good connection to Data/5v/GND

Thanks for the update.

Pete