Multiple DS18b20 sensors & 'hardcoded' addresses

This isn't on an Arduino device, but it is using the Arduino bootloader, IDE & libraries, so hopefully someone here can give me some advice.

I'm working with some hardware from OpenEnergyMonitor.org, specifically, their emonTH at the moment.

I'm looking to have multiple DS18b20's connected to it. Probably as few as two and as many as possibly up to six. The problem is, just connecting the sensors, they will get placed in serial number order, so if I were to add a sensor or one went bad and I had to replace it, it would likely re-order the sensors and mess up the data.

I've got the TH reading the sensor data, the problem is, it seems that despite having code in the sketch to associate DS18b20 addresses with specific sensor IDs, it's putting them in the order it wants and that order dictates which one is 1, 2 or 3, not the order in which I have the serial numbers.

In the code below (The entire sketch is too long to post, I've posted the relevant portions, I don't think I missed anything), relative to the sensor 1,2,3 order I have those serial numbers in, they're being detected and transmitted as 2,1,3, which seems to be their serial number order.

It doesn't seem to be using the 'DeviceAddress' at all - if I change one or more to bogus addresses, it still detects three sensors and sends data for three sensors. It doesn't say 'Hey, I can't find sensor 0x28, 0x8C, 0xA3, 0x29, 0x07, 0xFE, 0xFE, 0xFE' and mark that one as not present. It marches on and still gives me three readings. it's basically like it's not even there.

Now, I could understand if the sensor addresses get stored in memory in numerical order, but when you have code saying 'EXT_SENSOR1 = { 0x28, 0x8C, 0xA3, 0x29, 0x07, 0x00, 0x00, 0x6D };', 'EXT_SENSOR2 = { 0x28, 0x48, 0x29, 0x2A, 0x07, 0x00, 0x00, 0xFE };' and so on, when the code calls for a temperature reading from 'EXT_SENSOR2', shouldn't it look to the previously defined address of '0x28, 0x48, 0x29, 0x2A, 0x07, 0x00, 0x00, 0xFE' and grab the reading from that specific address?

I am FAR from an expert on this, but from everything I've read around the internet, this should be working. Does anyone have any ideas why the hardcoded addresses are being ignored, and how to fix that?

Up before 'void setup':

/*
  External sensor addresses
  =========================
  
 Hardcoding these guarantees emonCMS inputs won't flip around if you replace or add sensors.
 Use one of the address finding sketches to determine your sensors' unique addresses.

 See 'emonTH temperature search' utility sketch in 'Simple emonTH Sensor Test' folder
 
 Extend this if you have more sensors.
 */
DeviceAddress EXT_SENSOR1 = { 0x28, 0x8C, 0xA3, 0x29, 0x07, 0x00, 0x00, 0x6D };
DeviceAddress EXT_SENSOR2 = { 0x28, 0x48, 0x29, 0x2A, 0x07, 0x00, 0x00, 0xFE };
DeviceAddress EXT_SENSOR3 = { 0x28, 0xAC, 0x04, 0x29, 0x07, 0x00, 0x00, 0xB9 };

boolean EXT_SENSOR1_PRESENT;  
boolean EXT_SENSOR2_PRESENT; 
boolean EXT_SENSOR3_PRESENT;

In 'void loop':

  // External temperature readings
  if (EXT_SENSOR1_PRESENT || EXT_SENSOR2_PRESENT || EXT_SENSOR3_PRESENT) 
    take_ds18b20_reading();

With the 'EXT_SENSOR?_PRESENT' lines, all six were originally un-commented, I've tried commenting out the forst three, second three, and all six; the only behavioral difference was when I commented them all out - no sensor data was gathered.

void initialise_DS18B20()
{
  // Switch on
  digitalWrite(DS18B20_PWR, HIGH); 
  dodelay(50); 

  sensors.begin();

  // Disable automatic temperature conversion to reduce time spent awake, instead we sleep for ASYNC_DELAY
  // see http://harizanov.com/2013/07/optimizing-ds18b20-code-for-low-power-applications/ 
  sensors.setWaitForConversion(false);                             

  // Note - the index (param 2) is dependant on the natural ordering of your sensor addresses.
  // We'll try both ways, just in case.
  EXT_SENSOR1_PRESENT = sensors.getAddress(EXT_SENSOR1, 0);
  EXT_SENSOR2_PRESENT = sensors.getAddress(EXT_SENSOR2, 1);
  EXT_SENSOR3_PRESENT = sensors.getAddress(EXT_SENSOR3, 2);

  //EXT_SENSOR1_PRESENT = EXT_SENSOR1_PRESENT ? EXT_SENSOR1_PRESENT : sensors.getAddress(EXT_SENSOR1, 0);
  //EXT_SENSOR2_PRESENT = EXT_SENSOR2_PRESENT ? EXT_SENSOR2_PRESENT : sensors.getAddress(EXT_SENSOR2, 1);
  //EXT_SENSOR3_PRESENT = EXT_SENSOR3_PRESENT ? EXT_SENSOR3_PRESENT : sensors.getAddress(EXT_SENSOR3, 2);
  // .. for 3 or more sensors you may want to establish the order by trial and error instead

After 'void loop'

void take_ds18b20_reading () 
{
  // Power up
  digitalWrite(DS18B20_PWR, HIGH); 
  dodelay(50); 

  // Set precision to desired value
  sensors.setResolution(EXT_SENSOR1, TEMPERATURE_PRECISION);
  sensors.setResolution(EXT_SENSOR2, TEMPERATURE_PRECISION);
  sensors.setResolution(EXT_SENSOR3, TEMPERATURE_PRECISION);

  // Get readings. We must wait for ASYNC_DELAY due to power-saving (waitForConversion = false)
  sensors.requestTemperatures();                                   
  dodelay(ASYNC_DELAY); 
  float temp1=(sensors.getTempC(EXT_SENSOR1));
  float temp2=(sensors.getTempC(EXT_SENSOR2)); 
  float temp3=(sensors.getTempC(EXT_SENSOR3));

  // Power down
  digitalWrite(DS18B20_PWR, LOW);

  // Payload will maintain previous reading unless the temperature is within range.
  if (temperature_in_range(temp1))
    rfPayload.externalTemp1= temp1 * 10;   

  if (temperature_in_range(temp2))
    rfPayload.externalTemp2= temp2 * 10;   
    
  if (temperature_in_range(temp3))
    rfPayload.externalTemp3= temp3 * 10;  
}

Cougar281:
I've got the TH reading the sensor data, the problem is, it seems that despite having code in the sketch to associate DS18b20 addresses with specific sensor IDs, it's putting them in the order it wants and that order dictates which one is 1, 2 or 3, not the order in which I have the serial numbers.

You assign a name to the address, say input, and put the sensor in that position.

If you add or replace a sensor, it means you have to adjust the programme accordingly. You can use a method where it simply reads in address order but I don't think it solves anything as you still ahve to know what the address is and where the sensor is going.

Nick, that's what's being done in the first block of code:

DeviceAddress EXT_SENSOR1 = { 0x28, 0x8C, 0xA3, 0x29, 0x07, 0x00, 0x00, 0x6D };
DeviceAddress EXT_SENSOR2 = { 0x28, 0x48, 0x29, 0x2A, 0x07, 0x00, 0x00, 0xFE };
DeviceAddress EXT_SENSOR3 = { 0x28, 0xAC, 0x04, 0x29, 0x07, 0x00, 0x00, 0xB9 };

But it isn't working - for whatever reason, the coding is ignoring the set addresses.

EXT_SENSOR1_PRESENT = sensors.getAddress(EXT_SENSOR1, 0);
EXT_SENSOR2_PRESENT = sensors.getAddress(EXT_SENSOR2, 1);
EXT_SENSOR3_PRESENT = sensors.getAddress(EXT_SENSOR3, 2);

That is the problem.
getAddress changes value of EXT_SENSORx.

Cougar281:
But it isn’t working

Which is why I gave you a link to some code that does.

@bricoleau has nailed the problem.
sensors.getAddress(EXT_SENSOR1, 0) does not check if the sensor whose address is in EXT_SENSOR1 is present. It finds the first sensor on the bus (i.e. at index 0) and stores its address in EXT_SENSOR1 thereby overwriting what you had initialized it to.

The usual way to get around the problem of identifying the physical location of sensors is to use one of the unused alarm locations in the DS18B20 EEPROM by writing a unique identifier into this location using a separate program. For example, you might label each of the original DS18B20 as A, B and C. You would have to modify your code (extensively) to use this information. Either add the label to each temperature when it is printed so that you know where it is from (you might print "A:20.3 C:14.9 B:11.3" where these are in order of the sensors' address) or write the code so that it always reports them in order of the label ("20.3,11.3,14.9" - i.e. A,B,C). Now, if sensor C fails, you get another DS18B20, label it as C and then install it. You will need to add some mechanism to alert your emonTH-based software that one or more of the sensors has changed which requires that the code enumerates them again. This is especially true if you want to be able to add another DS18B20, say D. The original emonTH code appears to enumerate the sensors once at setup so that if it finds 3 sensors on the bus, the only way to get it to use 4 is to reboot.

Pete

Thanks guys,

bricoleau:

EXT_SENSOR1_PRESENT = sensors.getAddress(EXT_SENSOR1, 0);

EXT_SENSOR2_PRESENT = sensors.getAddress(EXT_SENSOR2, 1);
EXT_SENSOR3_PRESENT = sensors.getAddress(EXT_SENSOR3, 2);




That is the problem.
getAddress changes value of EXT_SENSORx.

That's basically the part I was missing - I wasn't really sure exactly how it worked - didn't know if it probed the addresses and put them into memory, then the request for the temps pulled from that memory, or if it overwrote as you say it does - that makes sense given the behavior. I had skimmed the sketch on the referenced page and it didn't click because it was so similar to what is being used on the EmonTH - except for the lack of 'EXT_SENSORx_PRESENT = sensors.getAddress(EXT_SENSORx, 0);'. I loaded up the linked sketch onto an Arduino Pro Mini and tested it out with three sensors, and it worked as intended - replacing a valid address with a bogus one resulted in a read error (as expected).

el_supremo, that seems a bit overly complex for the situation - as shown by the simple script that was in the link provided by Nick, it is possible to reference the sensors by address without writing anything to them. That will require a restart when adding or changing sensors, wheras it sounds like the method you describe might not, but that's not really a big deal. Using the simper code, you'd have to write the sketch with the new address anyway, so it would be rebooted when adding the new sensor anyway.

el_supremo:
The usual way to get around the problem of identifying the physical location of sensors is to use one of the unused alarm locations in the DS18B20 EEPROM by writing a unique identifier into this location using a separate program.

Cute usual way

I didn’t fint it out, so I went my way to quite the same result :

The address of each DS18B20 is stored in the main eeprom.

And my program applies a few smart rules to auto-update the eeprom :

  1. If no device is found (very first run), the program keeps scanning the bus once per second.
    The first device plugged is supposed to be sensor A, the second sensor B, and so on

  2. If one device has disappeared, and an other one with an unknown address is detected, the program considers there was a hot swap of a DS18B20.
    The new address is stored in the eeprom, overwriting dead sensor address

  3. A detection mode can be manually activated
    In this mode, the arduino asks me to touch sensor A, and reads temperature from each sensor until a significant rise is detected.
    And so on for the other sensors, except the last one.

@Cougar281: If you have access to the code and understand how to find out what the new device's address is and how to edit the code with the new hexadecimal address, then the method you're using is fine, I suppose. When the code can't be changed (e.g. in a commercial product) or is being used by a hexadecimally-challenged person, then labelling them A,B,C or I(inside), O(outside) and D(doghouse) is probable a less error-prone method.

@bricoleau: that's a nice solution too :slight_smile:

Pete

I'm pretty impressed by these commercially-oriented approaches using the eeprom, even though I have no need for them. By way of encouragement for the OP, these sensors are very robust. I have abused them with no harm done, and I have never actually heard of one going bad.

I use cheap waterproof DS18B20 sensors (those commonly found on eBay) for aquarium purpose, and found a few feedbacks from other users, about lifetime of these sensors when immersed permanently.

I don’t want to get back to my computer to replace a sensor.
Neither use one pin per sensor (oneWire bus on each pin, with only one device <= simple solution in many cases).

So hot swap ability was the solution to cover my needs.
Detection mode is in case of several sensors failure.