[SOLVED] Reading DS18B20 in the correct sequence

Let me explain the state of the art of my device:

I have multiple (14) DS18B20 temperature sensors wired in the same bus in a non-parasite mode. Everything works great, and i am able to read all of them by the use of any of the examples in the DallasTemperature library.

However, when i use the "tester" example from this library, the sequence is wich each sensor is asked is not the same than the real sequence in which they are soldered in the bus. For me is so important to store the data from each sensor in a selected order, but also to same as much as posible of the arduino memory (beause in really, i do not have only one bus, but 4 of them, each one with a different number of sensors).

So, i think that a possible solution is to know the address of each sensor, and to call one by one in the order i could decide... but i think (i didn´t tested) it will result in a long code and huge memory usage... Am i right?

Any idea about how could i obtain the temperature of the sensors in the same sequency they are soldered, or any possible solution to this issue? Thanks so much!

Interesting question. Perhaps scan the ID's on startup and remember them in flash.

The Dallas lib does not support the detection of the order of DS18B20's ( have had a similar problem with a project)

My solution was to make an array of the addresses in EEPROM in the order I wanted to access them. I had an array of structs something like

struct TempSensors
{
  uint8_t ID;
  int  nameAddr; // pointer to descriptive name in EEPROM
  int  DSAddr;  // pointer to address of the DS18B20
  uint8_t pin;  // pin the DS18B20 was connected to
} TS[20];

2 functions getName(uint8_t idx) and getDSAddr(uint8_t idx); made it complete.

hope this helps

Okay, thanks RobvdVeer. Sounds reasonable.

But, the problem i see is that when i read the address of eahc sensors, they are not in the correct order in which they appear in the bus. So, may be beter than to read the values at the setup, may be it is better if i store the IDs of each sensor into an array at the correct order in which i want the program will read them in the loop. Then, in order to read the sensors in the correct order, i could say to the program to read the content of the first position of the array, and use this information to call a selected sensor and read its temperature. And then, the same with the next position until the end of the array. Does it have sense for you?

In fact, since i have more than one bus, i could create an array for each bus containing the IDs of the senors soldered on it.

So, in pseudocode, could it be something like this?

Read the array and count the number of positions. For i = 0 to numberofpositions in the array read the i position of the array TempC = ensors.getTempC(address_stored_at_position_i) Check the correct value (-127 and 85 values correspond to errors reading ds18B20 sensors). savethedata into an SD.

Does it have sense? If yes, I do not have so much experience, so, could you help me to code this? Thanks!

Edited: Sorry, robtillaart . We were writing at the same time. I see i was not so far from the solution. In fact, i also have a 24LC256 eeprom in my device, so i could use it for that purpose too. In fact, i had on mind to use a solution like this to use the eeprom: http://apexlogic.net/code-bank/arduino/eeprom-i2c-write-anything/

No problemo :)

Okay, leaving the eeprom issue (first i would like to know how it works in a simple program before to move the array to the eeprom):

Should i define the arrays in this way?

uint8_t busSensors_1[1] = {28963820050000E2};
uint8_t busSensors_2[14] = {28728705050000C6,28728E04050000BA,2822F805050000EA, etc};
uint8_t busSensors_3[1] = {284A8620050000B6};
uint8_t busSensors_4[10] = {281CC4040500003E,28D42B050500003A,287EFD050500006B, etc};

Edited:
BTW, may be it could be much more easy to declare and array of simple numbers corresponding to the devide to be readed, such as:
int bus_1[10]={2,5,3,8,1,6,4,0,7,9}

Then, to ask the code to read the devide which number is stored in a selected position of the array in the array sequence… Does it have sense?

The addresses are arrays of 8 bytes, so the array becomes more like

OneWire oneWire4(4);
OneWire oneWire5(5);
OneWire oneWire6(6);
OneWire oneWire7(7);

DallasTemperature sensors4(&oneWire4);
DallasTemperature sensors5(&oneWire5);
DallasTemperature sensors6(&oneWire6);
DallasTemperature sensors7(&oneWire7);

DallasTemperature sensors[4] = { sensors4, sensors5, sensors6, sensors7 };;

uint8_t addr[14][8]  = { 
        { 0x28, 0x96, 0x38, 0x20, 0x05, 0x00, 0x00, 0xE2 },
        { 0x28, 0x72, 0x87, 0x05, 0x05, 0x00, 0x00, 0xC6 },
  ..
};

uint8_t bus[14] = { 1,1,1,2,2,2,3,3,3,0,0,0,0 };

...

for (int i=0; i<14; i++)
{
   float tempC = sensors[bus[i]].getTempC(addr[i]);
   // print tempC 
}

Thanks robtillaart for the code.

I see that i was declaring the ID in a incorrect way.

I see what you are doing... it is really interesting. Thanks!

I edited my previous post to ask something. Why not to store the order of the sensor to be readed better than their address? May be is much more simple... Something like this:

const int Bus_1[1] = {0};
const int Bus_2[14] = {9,8,5,3,13,12,6,1,0,7,2,11,10,4};
const int Bus_3[1] = {0};
const int Bus_4[10] = {2,0,4,5,8,7,6,3,9,1};

Thanks for you patience!

Disclaimer: code was not tested

madepablo: Thanks robtillaart for the code.

I see that i was declaring the ID in a incorrect way. However, there are some sections of the code that i do not understand.

DallasTemperature sensors[4] = { sensors4, sensors5, sensors6, sensors7 };

What is this for? One bus containing 4 sensors? there are 4 pins on which DS18's are connected I placed them in an array

uint8_t addr[14][8] = { { 0x28, 0x96, 0x38, 0x20, 0x05, 0x00, 0x00, 0xE2 }, { 0x28, 0x72, 0x87, 0x05, 0x05, 0x00, 0x00, 0xC6 }, .. };

If i am correct, you declared the first 2 ID of 14 sensors [14] in 8 Bits "[8]" each one? Am i right? it is a 2dimensional array, in which I have 14 x 8 bytes. and 8 bytes is an DS18 address. you may have noticed that the first nr is always 0x28, it is the type identifier and we can leave it out the table to make it smaller

uint8_t bus[14] = { 1,1,1,2,2,2,3,3,3,0,0,0,0 };

What is this for? this array has corresponding indices with the previous one, it defines which sensor[] you need to use

Sorry for all those question! welcome, it is a good question and I need to work out a working sketch with the above one day ...

I edited my previous post to ask something. Why not to store the order of the sensor to be readed better than their address? May be is much more simple... Something like this:

const int Bus_1[1] = {0};
const int Bus_2[14] = {9,8,5,3,13,12,6,1,0,7,2,11,10,4};
const int Bus_3[1] = {0};
const int Bus_4[10] = {2,0,4,5,8,7,6,3,9,1};

may work very well Thanks for you patience!

Following with the idea of to use the order sequence bettern than the sensor ID, but following the code previous posted by robtillaart, here is where i am now:

In the declaration section of the code i have this:

//Library configuration
OneWire oneWire1(AirTemp); 
DallasTemperature sensors1(&oneWire1);
OneWire oneWire2(SnowTemp);
DallasTemperature sensors2(&oneWire2);
OneWire oneWire3(SurfaceTemp); 
DallasTemperature sensors3(&oneWire3);
OneWire oneWire4(GroundTemp); 
DallasTemperature sensors4(&oneWire4);

DallasTemperature sensors[4] = {sensors1,sensors2,sensors3, sensors4};

const int airsensors[1] = {0};
const int snowsensors[14] = {9,8,5,3,13,12,6,1,0,7,2,11,10,4};
const int surfacesensors[1] = {0};
const int groundsensors[10] = {2,0,4,5,8,7,6,3,9,1};

//char positions[4] = {"airsensors","snowsensors","surfacesensors","groundsensors"};

/i commented the last line because it returns an error:

And this is what i have in the loop section of the code:

for(int i = 0; i<14; i++){     
Serial.print(sensors[i].getTemCByIndex(positions[i])
Serial.print(";");
 }

However, the positions array returns and error in the char array:

error: too many initializers for ‘char [4]’
error: initializer-string for array of chars is too long

However, it seems to me that in the loop, not all the sensors will be readed… just only the sensor 0 of 1st bus, sensor 1 of 2nd bus,… and so on…

I submit the simplest way is to get the addresses of the sensors and call them up by that in the order you require. This is likely to include the use of sticky paper labels and a pencil, but at least you are never left in any doubt about which DS18B20 is where, and what to call it in your code.

http://www.hacktronics.com/Tutorials/arduino-1-wire-tutorial.html

Thanks for your ideas.

In the mean time, i implemented the next code for the definitions:

//Library configuration
OneWire oneWire1(AirTemp);  
DallasTemperature sensors1(&oneWire1);
OneWire oneWire2(SnowTemp);       
DallasTemperature sensors2(&oneWire2);
OneWire oneWire3(SurfaceTemp);        
DallasTemperature sensors3(&oneWire3);
OneWire oneWire4(GroundTemp);          
DallasTemperature sensors4(&oneWire4);
#define TEMPERATURE_PRECISION 12         
//const int airsensors[1] = {0};  //Not used because i only have one sensor in this bus
const int snowsensors[14] = {9,8,5,3,13,12,6,1,0,7,2,11,10,4};
//const int surfacesensors[1] = {0};   //Not used because i only have one sensor in this bus
const int groundsensors[10] = {2,0,4,5,8,7,6,3,9,1};

and then, this code in the loop section:

    sensors1.requestTemperatures();
    Serial.print(sensors1.getTempCByIndex(0)); // Since only one sensor in this bus, i just only need to call the first one.
    Serial.print(";");
    sensors2.requestTemperatures();
    for(int i = 0; i<14; i++){     // Here i call to all the sensors in the bus by the sequence stored in the array
      Serial.print(sensors2.getTempCByIndex(snowsensors[i]));
      datafile.print(";");
    }
    sensors3.requestTemperatures(); // Since only one sensor in this bus, i just only need to call the first one.
    Serial.print(sensors3.getTempCByIndex(0));
    Serial.print(";");
    sensors4.requestTemperatures();
    for(int i = 0; i<10; i++){          // Here i call to all the sensors in the bus by the sequence stored in the array
      Serial.print(sensors4.getTempCByIndex(snowsensors[i]));
      Serial.print(";");
    }

I tested and it works.

The number of iterations in each case (14 and 10), could be changed by sensors2.getDeviceCount() and sensors4.getDeviceCount(), respectively in my case.

However, i will follow exploring the idea of the IDs and to store the data into the eeprom… I think it is more clean and precise (?), such as Nick_Pyner propose.

Thanks to both of you guys. I will post here future improvements of the code.

madepablo: However, i will follow exploring the idea of the IDs and to store the data into the eeprom... I think it is more clean and precise (?), such as Nick_Pyner propose.

Well, I have no idea how you would use the EEPROM. It might be cleaner - no sticky labels involved, it won't be any more precise, but one thing you can be sure about is that , by taking that road, you will very quickly learn that one DS18B20 looks very much like another.

Hi Nick_Pyner,

I will use an external eeprom (24LC256) because i am near the limit of memory. My code is about 27K and i still need to add more procedures (such as errors control). So, by defining all the sensors ID (26 in my devic in 4 different busses), it could take (i don´t know jet) some free space. Then, in the code, i could call to ech sensor by its ID better than its position, by reading the arrays from the eeprom. So, not mandatory or necessary to solve the here discussed problem, just onñy in my case to save memory. Moreover, i wil save other things in the eeprom, so, since it is a huge one, i could spend some blocks of memory to store the 26 IDs i will require.

And i will explore the idea of the ID bettern than the position because... what happend if one of the sensors fail? So, we will assume that (for example) the sensor number 5 in a bus fails. The next time the program try to read all the sensors, how could i be sure that the sensor readed in the fifth position is the sensor number 5 and not the next one? I don´t know what could be the implications of a sensor fail... (i don´t have a broken one to check, and i neither want to burn one of them to test). So, may be by calling the sensors by their name (ID), if you do not receive reply for this name, you could be sure which sensor failed. Am i right?

Cheers

I can't comment on EEPROMs. I have never seen the need for them and certainly never had the temptation to learn how to use them. I t looks like you are using a Uno. Now I don't know what you are doing but, if your memory is around 27k, you are very likely to be amazed quite soon at how fast that last 5k gets used up, and the time to start thinking about a Mega was probably yesterday. My datalogging setup is comprehensive but probably quite ordinary and is currently up to 42k. The memory got used up at ever increasing speed, it seemed. I don't think the address storage for the DS18B20s would cost much memory, and it wouldn't surprise me if it cost more to drive the EEPROM.

DS18B20s can take a lot of abuse and I have never had one fail. I believe they return -127, or something self-evident when they die. I guess the index system works in the event of failure but what I would do is clear and obvious - take a pre-labelled spare out of the box, install it, copy paste the pre-written address from Notepad into the code, load that, and we are off to the races. I would trust my thirteen-year-old granddaughter with the job.

Those 27.6Kb includes mostly everything (also the eeprom library and management) except the error control, what is pretty simple since, such as you already pointed, ds18b20 sensors return -127 if there is nothing connected, and 85 if there is a communication problem but the devide is detected... So, it will not take so many bytes to code this section. But here is the source of my previous question about what happend if one of the sensors is dead (or accidentally disconnected) and not detected... then, i think that by the use of the ID you are sure about which sensor are you reading in each moment.

My main headpain and memory consumer is the time. I added a ds1307 rtc board (and its library) to monitor time, but i also added Time and TimeAlarms libraries in order to trigger alarms to measure the temperature once per hour (each hour o'clock).

Yes, i use a duemilanove board in this device. I also have other datalogger based on a mega board (plenty of pins and memory!). However, this datalogger (What will be installed in antarctica for two years at least) have some costs and space limitations, and for that reason i didn´t use a mega board. On the other hand, this is also a reaon, why i added an eeprom, to store some configuration parameters, and string chains for debugging purposes without to waste space in the main memory. For example, the eeprom will store the header to be writed into each datafile saved SD card installed in the device to save the data. So, this is the first time i use eeprom.... so, we will see if it is a solution or a headpain.

Thanks robtillaart for your clear explanations! they were really useful!

Very interesting.

If you are using an SD card, I don't see why you would bother with an EEPROM. Maybe you have something special about the headers, but I don't understand that. I just use the DS1307 to create new filenames and the content is just data for Excel.

Yes, i need to keep in different places the information from the configuration and the data. The idea is to go back to the place in Antarctica where the device will be installed to replace the SD card. In the better case, every year, but in the worse case... So, any byte of space ar the SD card should be important. But, this is not the main reason, because using a bigger SD card is enough, right? No, the second issue is to made possible to everybody to replace the card... using any SD card... without taking care about its content. Just only, to take one full of data and installing a new one... Then , reseting the devide (who initilize the SD card, create a new file, save the header on it, and that is). If the card produce an error, the operator could change it by a new one (for example, in case of extreme emergency, from its own digital camera if no other cards are available at that moment). Also, if the card fail, the devide trigger an alarm, so the operator know inmediatly if there is a problem for start to work or not. If not, the user could leave the site with the security that everything works perfect (at least apparently). To save the configuration information in the SD card, require that, in case of problems, to use a laptop in the field (what is neither easy under that harsh weather conditions) to save again the information required by the device to work. Then, with or without the SD card, the devide will work, and if the sd card is full or fail, you could replace easily without take care about anything.

So, in other cases or conditions, the design could be better and more friendly, or whatever, but taking into account the objectives, emplacement and conditions, i think this is the better option... but, of course, disputable, since my experience in electronics is so limited.

madepablo: . So, any byte of space ar the SD card should be important. But, this is not the main reason, because using a bigger SD card is enough, right?

Yes, think years of CSV data. The DS1307 battery will probably die before the card fills.

No, the second issue is to made possible to everybody to replace the card... using any SD card... without taking care about its content. Just only, to take one full of data and installing a new one... Then , reseting the devide (who initilize the SD card, create a new file, save the header on it, and that is).

So what is this header? It sound like something you don't need. Also, what is the configuration that needs to be recorded?

My stuff is not much different, just less dramatic. One of them has to be a able sit underneath a ski lodge unattended for about ninety days. The most attention it will get is somebody else changing the card for the spare in the box and bringing the data to Sydney. This simply involves turning the power off, swapping the card, restarting it, and looking. Advice of a bad start is included in the standard card initialisation code. There isn't a need for any other alarm. I have a local display too but the same initialisation procedure could turn a LED on if you don't, so the user operation would be the same.

In my case, the SD is essentially just local backup. There is no header and no configuration, just daily data files, but there would be no change to the way it works if it was the prime or only source of information.

It seems we are doing the same thing in different places...

Yes, the rtc battery is an issue i will try to solve in the future. For the moment this is only a prototype to see how it works. But i have on mind to use an RTC capable to trigger alarms in order to send the arduino board to sleep reducing its power consume. In the same way, i have on mind to add rechargeable battery for the RTC also which will (at least ideally) result on a pretty long life for the devide.

About the header, It is not fundamental for me, but it is a requirement. The header contains all the metadata of the device, the project, and the data (measurement, units, and so on). For me it is not necessary, because i know what is what and i only have this device. But it is a requirement. In the future, multiple of them should be installed at different places, with different number of sensors and measurements... Moreover, in case somebody found broken the decive and could rescue the sd alive, to found where to send the data... And the header contains information required for the software we use to analyze the data. Yes, i use an csv file, so, it could be perfectly opened by notpad or excell, but it is a general requirement. So, again, other design could be possible with/whithout some of the elements it already has or not right now.

Yes, the working process i have on mid is the same that you already explain. I also added a led to show, to the guy changing the SD card, if everything worked correctly or not during the start up (SD inicilization, datafile writing, sensors identifications, etc.), but also a swich to turn off the led when not needed to same as much battery as possible.

However, it is interesting to know that others have a similar device working perfectly! So, any advice will be wellcome!