Multiple (dynamic #) DallasTemperature sensors

Hi all,
hoping someone has the magic idea here - I got stuck with this one.

I’m trying to use a (currently unknown) DallasTemperature sensors. Got the latest Arduino 1.0.1 compatible libraries for DT sensors and for oneWire, all set.

I’m trying to setup an array of these sensors, such as

...
#include <DallasTemperature.h>
#include <OneWire.h>

#define ONE_WIRE_BUS 7
OneWire oneWire(ONE_WIRE_BUS);  // start OneWire interface on digital pin 7

#define SensorCount 5
// typedef uint8_t DeviceAddress[8];
DeviceAddress tmp;  // a device address
DeviceAddress DevAddress[SensorCount]; // an array of 5 device addresses
DallasTemperature Sensors(&oneWire); // hookup sensors via oneWire interface
...
void setup(){
  // setup serial speed for serial monitor
  Serial.begin(9600);
...
  // setup DallasTemperature Sensors
  // start sensor library
  Sensors.begin();
  // find sensors by searching oneWire bus
  oneWire.reset_search();
  for (int i=0; i<SensorCount-1; i++) 
  {
    // assigns the next address found to tmp
    if (!oneWire.search(tmp)) // store whatever found in tmp
    {
      // oops ... fault
    }
    else
    {
      // success
      DevAddress[i] = tmp;
    }
  }
  ...
  Sensors.setResolution(12); // 12 bit, 0.06 deg C
  ...
};

Alas, it doesnt compile… with

sketch_mar01a.cpp: In function ‘void setup()’:
sketch_mar01a.cpp:61:23: error: invalid array assignment

I think part of the cause of the problem is that the DallasTemperature class (DallasTemperature.h) defined the type DeviceAddress as an array, and I am somehow pointing to the wrong parts of it:

typedef uint8_t DeviceAddress[8];

So that means my

DeviceAddress DevAddress[SensorCount]; // an array of 5 device addresses

is actually a

uint8_t DevAddress[8][SensorCount];

or a

uint8_t DevAddress[SensorCount][8];

which it doesn’t like.

So my main question is - any ideas on how to define an array of addresses, each address pointing to a DeviceAddress which really is an array of [8] uint8_t’s ? I’ve tried defining and array of pointers, such as

DeviceAddress* devAddress[SensorCount]
...
tmp = new DeviceAddress;
devAddress[i] = tmp;

but that only produces garbage as well …

Any clever ideas?

Thanks in advance,

Otte

ottehoman: I'm trying to use a (currently unknown) DallasTemperature sensors.

What's the objective? You don't know the addresses, so why don't you simply run the standard address finder?

Objective - a slightly larger array of sensors (5..25 or so, as I said, unknown). I will be adding and removing sensors 'on the fly', but do not want to re-hardcode and upload the code each time. Simple power cycle of the Duino should suffice to see that there are now a few sensors more, or less.

OK. If you don't know the address, you can't assign a name. In that event how do you know which data is coming from which sensor?

Having said that, there is a programme around on this forum which could read the addresses on the fly. I believe it tested each sensor on the bus in the loop. It was about three months ago.

I’ve had a look at the examples, there is a multiple.pde one. It basically does what I want, just with hardcoded addresses:

#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
#define TEMPERATURE_PRECISION 9
// 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);
// arrays to hold device addresses
DeviceAddress insideThermometer, outsideThermometer;  // hardcoded addresses for inside and outside
void setup(void)
{
  // start serial port
  Serial.begin(9600);
  Serial.println("Dallas Temperature IC Control Library Demo");
  // Start up the library
  sensors.begin();
...
  // search for devices on the bus and assign based on an index.  ideally,
  // you would do this to initially discover addresses on the bus and then 
  // use those addresses and manually assign them (see above) once you know 
  // the devices on your bus (and assuming they don't change).
  // 
...
  // method 2: search()
  // search() looks for the next device. Returns 1 if a new address has been
  // returned. A zero might mean that the bus is shorted, there are no devices, 
  // or you have already retrieved all of them.  It might be a good idea to 
  // check the CRC to make sure you didn't get garbage.  The order is 
  // deterministic. You will always get the same devices in the same order
  //
  // Must be called before search()
  oneWire.reset_search();
  // assigns the first address found to insideThermometer
  if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  // assigns the seconds address found to outsideThermometer
  if (!oneWire.search(outsideThermometer)) Serial.println("Unable to find address for outsideThermometer");
  // show the addresses we found on the bus
  Serial.print("Device 0 Address: ");
  printAddress(insideThermometer);
  Serial.println();
  Serial.print("Device 1 Address: ");
  printAddress(outsideThermometer);
  Serial.println();
...

Now, instead of

...
DeviceAddress insideThermometer, outsideThermometer;  // hardcoded addresses for inside and outside
...
void setup(void)
{
  Serial.begin(9600);
  sensors.begin();
...
  oneWire.reset_search();
  if (!oneWire.search(insideThermometer)) Serial.println("Unable to find address for insideThermometer");
  if (!oneWire.search(outsideThermometer)) Serial.println("Unable to find address for outsideThermometer");
...
)

how would I do it like this:

...
DeviceAddress devAddr[15];  // array of (up to) 15 addresses
...
void setup(void)
{
  Serial.begin(9600);
  sensors.begin();
...
  SensorCount = sensors.getDeviceCount();
  oneWire.reset_search();
  for (int i=0; i<SensorCount-1; i++)
  {
    if (!oneWire.search(devAddr[i])) Serial.print("Unable to find address "); Serial.println(i);
  }
...
)

The compiler somehow always complains about the fact that a DeviceAddress is an array of 8 uint_t’s, and so something such as devAddress[i] refers to something of type uint8_t instead of to something of type DeviceAddress.

Can’t get it to work, really. Frustrating, because it’s not something hugely weird, what I ask for, is it? Basically, how to declare and address a two-dimensional array?

Otte

ottehoman: it's not something hugely weird, what I ask for, is it? Basically, how to declare and address a two-dimensional array?

I think it is extremely weird, but only because I have no idea why you would want to do this, rather than the matter of arrays. I have had nothing to do with arrays for about forty years, and don't remember anything about them.

The programme I alluded to had no hard coded addresses. I believe it ran the standard address finder routine. It might have been something from adafruit.

I think I understand what you want. Here is some junk I put together when playing that scans the bus and loads all the found devices into a two-dimensional array. I have the 1.03 stuff and I think you should upgrade if you can. Things have changed some from the way your coding shows. I cut and pasted this from a much larger program, so it may be missing a brace or two. You shouldn’t have much trouble getting it to work though.

EDIT: It’s my understanding that the older library has bugs that may cause it to lock up when scanning for devices. I really suggest upgrading to latest library. I’ve been using it with 3 sensors on a bus and it works perfectly.

#include <OneWire.h>
//
// 1-wire stuff
//

OneWire  ds(7);     // pin 7

byte i;
byte present = 0;
byte type_s;
byte data[12];
byte addr[4][8];     // enough room for 4 sensors
byte numFound;
byte addrSub;
float celsius, fahrenheit;

void setup() {
// Search for 1-wire devices

  numFound = 0;

  for(addrSub = 0; addrSub < 4; addrSub++){
    Serial.println(millis());
    if ( ds.search(addr[addrSub])) {
      Serial.println(millis());
      numFound++;
      Serial.print("Found: ");
      Serial.println(addrSub);
      Serial.print(" ROM =");
      for( i = 0; i < 8; i++) {
        Serial.write(' ');
        Serial.print(addr[addrSub][i], HEX);
      }
      if (OneWire::crc8(addr[addrSub], 7) != addr[addrSub][7]) {
        Serial.println("CRC is not valid!");
        return;
      }
      Serial.println();
 
    } else {
      Serial.println("No more found");
      break;
    }
  }
}

void loop() {
    dumpTemps();
}

void dumpTemps(){

  if(!numFound)
    return;
  ds.reset();
    
  ds.write(0xCC);             // Skip ROM
  ds.write(0x44, 0);          // start conversion, with parasite power off at the end
  
  delay(750);     // maybe 750ms is enough, maybe not

  
  for(addrSub = 0; addrSub < numFound; addrSub++) {

    // the first ROM byte indicates which chip
    Serial.print("\n  Sensor: ");
    Serial.println( addrSub );

    switch (addr[addrSub][0]) {
      case 0x10:
        Serial.println("  Chip = DS18S20");  // or old DS1820
        type_s = 1;
        break;

      case 0x28:
        Serial.println("  Chip = DS18B20");
        type_s = 0;
        break;

      case 0x22:
        Serial.println("  Chip = DS1822");
        type_s = 0;
        break;

      default:
        Serial.println("Device is not a DS18x20 family device.");
        return;
    } 
    
   // we might do a ds.depower() here, but the reset will take care of it.
  
    present = ds.reset();          // Reset the bus

    ds.select(addr[addrSub]);      // do a match ROM
 
    ds.write(0xBE);                // Read Scratchpad

    Serial.print("  Data = ");
    Serial.print(present, HEX);

    Serial.print(" ");
    for ( i = 0; i < 9; i++) {           // we need 9 bytes
      data[i] = ds.read();
      Serial.print(data[i], HEX);
      Serial.print(" ");
    }
    
    Serial.print(" CRC=");
    Serial.print(OneWire::crc8(data, 8), HEX);
    Serial.println();

    // convert the data to actual temperature

    unsigned int raw = (data[1] << 8) | data[0];
    if (type_s) {
      raw = raw << 3; // 9 bit resolution default
      if (data[7] == 0x10) {
        // count remain gives full 12 bit resolution
        raw = (raw & 0xFFF0) + 12 - data[6];
      }
    } else {
      byte cfg = (data[4] & 0x60);
      if (cfg == 0x00) raw = raw << 3;  // 9 bit resolution, 93.75 ms
      else if (cfg == 0x20) raw = raw << 2; // 10 bit res, 187.5 ms
      else if (cfg == 0x40) raw = raw << 1; // 11 bit res, 375 ms
      // default is 12 bit resolution, 750 ms conversion time
    }

    celsius = (float)raw / 16.0;
    fahrenheit = celsius * 1.8 + 32.0;

    Serial.print("  Temperature = ");

    Serial.print(celsius);
    Serial.print(" Celsius, ");

    Serial.print(fahrenheit);
    Serial.println(" Fahrenheit");
  }
}

Thank you very much. I'll try using 'my own' address[25][8] array (for up to 25 8-byte addresses) as you do (with 4), rather than relying on the uint8_t DeviceAddress[8] type defined in the DallasTemperature library I use. My Arduino development enviro is 1.0.1, slightly outdated I admit. But I thought I got the current latest libraries for OneWire and DallasTemperature. I got them from the http://playground.arduino.cc/Learning/OneWire as well as from the http://milesburton.com/Dallas_Temperature_Control_Library pages, so I think I'm 'up-to-date' with these. Again, thanks and kudos to you.

I think you are confused about arrays and pointers. Thats what your first post suggests to me.

Specifically, in your very first post, you are getting the value of a particular device
address returned to you in ‘tmp’, and then you are trying to assign that value into another device address.

You are a Java programmer ?

In Arduino C++, there is no overloaded assignment operator = which is just going to copy that array for you.
You actually need to copy the bytes in it, by yourself. Something like

for (int j=0 ; j<8 ; j++ )
{
   DevAddress[i][j] = tmp[j] ;
}

You are a Java programmer ?

Nah… wouldn’s say so. But I do like coffee.

I write code (or what looks like it) in nearly any language (apart from probably assembler, although I did do some M6509 stuff way back in 1988 or so), java and js, C shell, python, Matlab, F77 and F90, Basic, Pascal, C++, Qt, etc., whatever suits the application best, or whatever is available. Not particularly good at it (as one previous contributor said) but I get around. Typical physicist …

The DevAddress = tmp trick was more sort-of-like meta-code, not real. Although one could possibly do something like it with the use of pointers, malloc(), memcp() and sizeof(), I guess. Apologize for the confusion caused.

Reporting success here - hints from above seem to work and are successful - thank you for your support and help and ideas and comfort.

In a nutshell - how to connect multiple Maxim DS18B20 sensors via Dallas OneWire bus:

/* test */

#include <Arduino.h> // assumes Arduino 1.0.0 or higher
...
#include <DallasTemperature.h>
#include <OneWire.h>
...
#define ONE_WIRE_BUS 7
OneWire oneWire(ONE_WIRE_BUS);  // start OneWire interface on digital pin 7

#define MaxSensorCount 10  // max num of sensors to be defined
byte SensorCount;  // actual num o' sensors read from oneWire bus
byte Address[MaxSensorCount][8];  // 5 addresses of 8 bytes each  <--- Trick 77 for multiple 8-byte addresses
DallasTemperature Sensors(&oneWire); // pointer to Sensors
...
byte NextSensor = 0;  // next Sensor to be read. if nextSensor>SensorCount-1, nextSensor=0;
...
float temp;
char* buffer;
...
void setup(){
  // setup serial speed for software serial (LCD) and serial monitor
  Serial.begin(115200);
...
  // setup DallasTemperature Sensors
  // start sensor library
  Sensors.begin();
  SensorCount = Sensors.getDeviceCount();
  // get sensor address by searching the OneWire bus
  oneWire.reset_search();
  for (byte i=0; i<SensorCount; i++) 
  {
    // assigns the next address found to Address[i][0..7]
    if (!oneWire.search(Address[i]))
    {
      Serial.print("Unable to find address");
      Serial.print("\n");
    }
  };

...
    for (byte i=0; i<SensorCount; i++)
    {
      Serial.print("Address[");
      Serial.print(i);
      Serial.print("] = ");
      for (byte n=0; n<8; n++)
      {
         Serial.print(Address[i][n], HEX);
         Serial.print(" ");
      }
      Serial.print("\n");
    }
  }

  Sensors.setResolution(10); // 10 bit, 0.25 deg C, ALL sensors
  byte NextSensor=0;
  buffer = "-88.8888";
  Sensors.requestTemperatures();
  temp = Sensors.getTempC(Address[NextSensor]);
  ...
};

void loop() {
...
    //grab a new measurement
    Sensors.requestTemperatures();
    temp = Sensors.getTempC(Address[NextSensor]);
...
    // make a 'string' out of it
    dtostrf(temp, -5, 1, buffer);
...
    NextSensor++;
    if (NextSensor>SensorCount-1) { NextSensor=0; }
...
  }
...
}

The above (stripped) code works fine for one sensor hooked up. Note how the code does not a priori know how many sensors there are. Serial monitoring the thing shows it knows only about 1 and then keeps on reading that. I can trick it into thinking it has 5 (all with the same address) and then it will read 5.

1 Note on the side: on my Arduino Uno R3 a DS18B20 measurement (Sensors.requestTemperatures(); temp = Sensors.getTempC(Address[NextSensor]);) takes about 770 ms. I would currently assume most of it goes into the Sensors->requestTemperatures() routine, but I haven't tested it yet.
Note 2 on the side: I'm using breakout boards from DFROBOT with DS18B20 on them. These are fancy as they have the 4k7 pull-up/down resistors already mounted. That probably limits them to a max number of around 9 or 10 on the bus, before the Arduino would have to source too much current on the bus. For more sensors, I'd have to resort to e.g. barebones DS18B20 from Sparkfun or Adafruit (well, frmo Maxim, really) and hook up many all with one collective 4k7 pull-up/down on the bus.

Very interesting. When you get it to run with severral sensors, how do you tell which sensor is giving what information?

Simple, I think: I'll have a String SensorNames[] = {"One", "Two", etc.} array. I'll do a Serial dump of the names and the measured temperatures of each of them, hold one of them in my hand so it warms up a bit, and I'll see which one it is. From there on, either reshuffle the names array, or label the sensors accordingly, whatever is easier.

ottehoman: Simple, I think:

Really.....? It seems an utterly pointless exercise. If you are going to go through all that with the sensors, you might as well use the names they have already got. And one thing you can be pretty sure of is: the more sensors you've got, the dumber the idea.

O hail, the oracle has spoken. Thank you for your extremely valuable feedback.

I think he’s trying to say that you should enumerate them by their internal serial number since it is unique to each device. But, I can certainly see the challenge of trying to remember which 64-bit number was assigned to the attic. I think he might also be thinking that you might want to determine their serial numbers in a small circuit that just gives you the serial number of one device at a time, so that know what they are before they ever connect to your real network. Then you can have your software all ready when the device comes online. I’m not telepathic, so I could be way off on my thinking. :slight_smile:

Thanks for thinking along, you’re right.

Oh no… it must be so “utterly useful” to write code that fills the the entire top line of a 2x16 LCD display with an 8byte address, and have the associated temperature on the second line, and then have a piece of post-it stuck to it saying which device is located where … :wink:

On the side … why is it that dudes who reply 5 times to a fairly simple and reasonably well documented question, who miss the point of the question, who give zero input (but loads of criticism), who ‘allude to a program’ that ‘was around on the forum around 3 months ago’ which ‘he believed’ to do something, who think that naming of devices ‘is extremely weird’ and who think that identifying hooked up sensors by physically stimulating them and looking for a response is ‘utterly pointless’ and ‘dumb’ - why is that dudes like that are a Sr. Member after 3 months on the forum, and get 3 1/2 stars and 5 Karmas ? 42, Don’t PANIC, Keep Calm and Carry On …

LCD_img.png

afremont: I'm not telepathic,

Not much telepathy needed there, the address-finder that comes with the DS18B20 library does exactly what you describe. The sensor with that address is assigned to the attic exactly as you describe, and that sensor is henceforth called "attic". Then, and only then, Arduino can tell you "it is 27.23 deg. in the attic". What could be simpler than that?

One thing you can be sure of is that the Arduino is not telepathetic either. I therefore submit that, if you don't tell it which sensor is where, it is not going to tell you which temperature is where.

I write code (or what looks like it) in nearly any language (apart from probably assembler, although I did do some M6509 stuff way back in 1988 or so), java and js, C shell, python, Matlab, F77 and F90, Basic, Pascal, C++, Qt, etc., whatever suits the application best, or whatever is available. Not particularly good at it (as one previous contributor said) but I get around. Typical physicist ...

The DevAddress[] = tmp trick was more sort-of-like meta-code, not real. Although one could possibly do something like it with the use of pointers, malloc(), memcp() and sizeof(), I guess. Apologize for the confusion caused.

Well it doesn't matter how many languages you know. If you want to copy the contents of one 8-char array to another, you don't get that outcome by attempting to assign the name of one array to the other. Not in C.

The code of yours will assign to DevAddress*, the value of the location of tmp. It's a pointer assignment. Which is actually ok, except when you read the next value of tmp for the next device. That approach will not give you the array of device ID codes which you actually want.* You can copy the elements of tmp[] char by char, as I suggested. Or you could get fancy and use something like memcpy().