OneWire library: Problem supporting "n" wires - parametricly change pin #

Esteemed Forum Participants and Lurkers:

Warning: I'm NOT a C++ programmer ... I barely get along in C

I'm trying to write a general application for multiple DS18x20 thermometers on multiple pins. I have loaded the OneWire library and demo program from the PJRC website OneWire Library and it runs a single wire just fine.

I then started on my program and first wrote a loop to discover all devices on the specified pins, and that runs fine - it loops finding devices on 2 pins (1 device on pin 9, and 6 devices on pin 10). Note that it iterates through the "OneWire" definition for each pin:

// DS18x20 Library
#include <OneWire.h>

/****************    USER  DEFINITIONS    ****************/
// Define the wired pins 
uint8_t PINS[] = {9, 10};  // I AM USING ARDUINO MEGA 2650
// Define the maximum number of Thermo devices
uint8_t DEVS = 8;

/*************************************************************
****   setup:  Initialization section                     ****
*************************************************************/
void setup(void) {
  // Initialize and start the default serial port
  Serial.begin(9600);  // 9600 baud
}

/*************************************************************
****           FUNCTIONS:              ****
*************************************************************/
// Function to convert a byte into a 2 character string.
//   pdec2:  prefix a leading space for a decimal output
//           0 - 99 only
void pdec2(uint8_t dnum) {
  if (dnum < 10) { Serial.print(" "); }
  Serial.print(dnum);
}
//   phex2:  prefix a leading 0 for a hex output
void phex2(uint8_t hnum) {
  if (hnum < 16) { Serial.print("0"); }
  Serial.print(hnum, HEX);
}

/*************************************************************
****   loop:  RUN section VARIABLES                       ****
*************************************************************/
// loop section variables:
// Device parameter storage:  Pin #, Address[8 bytes]
// Since structs are not fully supported, just use arrays
// This setup will support up to 8 devices
uint8_t pin[8];  // Pin # driving each device
uint8_t par[8];  // Whether or not this device is on a Parasitic Power pin
uint8_t addr[8][8]; // Array of address for the devices
byte data[12];      // Raw data from read of Scratch Pad

byte i;  // loop iterator - pin index
byte j;  // device iterator - index
byte k;  // byte iterator - index
int  n;  // repeat counter
// "Continue" flag
boolean flag;

/*************************************************************
****   loop:  RUN section CODE                            ****
*************************************************************/

void loop(void) {
  // Create a list of devices
  j = 0;         // Initialize the Device counter
  // Initialize the device table to "NO DEVICES" by writing "0" into "device type" bytes
  for (i = 0; i < DEVS; i++) { addr[i][0] = 0; }
  // Iterate through the pins creating a list of devices
  for (i = 0; i < sizeof(PINS); i++) {
    flag = TRUE;  // Initialize the "Continue" flag
    // DS18x20 Temperature chip i/o assignment to pin
    OneWire ds(PINS[i]);

    // find all of the devices on this pin
    // If search is successful, the address is loaded into the array
    while ( flag ) {
      // Print the pin number as a line prefix
      Serial.print("Pin "); pdec2(PINS[i]);
      // Look for any devices on this wire - NOTE:  addr[j] = *addr[j][0]
      if ( ds.search(addr[j])) {
        // Log the pin number for this device address
        pin[j] = PINS[i];
        // Display the device ROM information
        Serial.print("  ID ");  phex2(addr[j][0]);

        // Display the 6 Byte address:
        for (k = 6; k > 0; k--) { phex2(addr[j][k]); }
        // Display the CRC just for luck ???
        Serial.print("  CRC "); phex2(addr[j][7]);
        Serial.print("\n");

        // Increment the device counter
        j += 1;
        if (j >= DEVS) { flag = FALSE; }
      }
      else {  // no more devices were found ... end of search on this pin
        Serial.print("  No more devices on this pin.\n\n");
        ds.reset_search();
        // Force the next pin to be selected
        flag = FALSE;
        delay(250); // ????
      }
    }
  }

                // READ CODE GOES HERE ...

}

If I insert the following code at the end of the previous code:

  // Take 100 Temp Readings from all attached thermometers
  for (n = 0; n < 100; n++) {
   // Reset the pin selector
   i = 0;
   // Scan the entire address table for devices to read
   for (j = 0; j < DEVS; j++) {
      // Check for a change in the driver pin number
      if (i != pin[j]) {
        // The pin has changed ... set up to use the new pin
        i = pin[j];
        // DS18x20 Temperature chip i/o assignment to pin
        OneWire ds(PINS[i]);  // Set the pin at each change
        // Start a convert operation on ALL devices on this pin/wire
        // Do a reset to clear out all devices
        ds.reset();
        // Issue SKIP ROM to direct next command to ALL devices on the pin
        ds.write(0xCC,0);         // SKIP ROM, with parasite power OFF
        ds.write(0x44,0);      // ALL DEVICES: CONVERT T, with parasite power OFF
        delay(775);  // Wait at least 750 milliseconds for the conversions to complete
      }
      // All devices are done ... Read each device 

      // ds has dropped out of the scope of OneWire here ???
      ds.reset();

      ds.select(addr[j]);    
      ds.write(0xBE);         // Read Scratchpad
      // Read 9 bytes of Scratch Pad Data
      for ( k = 0; k < 9; k++) {           // we need 9 bytes
        data[k] = ds.read();

 // This is as far as I got before getting the compile error

      }
    } 
  }

I get an error:
MultiTherm.ino: In function ‘void loop()’:
MultiTherm.ino:211:7: error: ‘ds’ was not declared in this scope

I have beaten my head against a brick wall trying to figure out how to properly change the pin assignment WITHOUT having to have ds1, ds2, ds3, etc. The "OneWire ds(PINS*);"[/b] does change the pin and it does work in the first part of the program, but it has this strange "scope" problem in the bottom part.*
I even tried to insert a "ds.apin" function into the library, but I don't really know how to do that. I put it in OneWire.h, OneWire.cpp, and in keywords.txt, but it was never recognized in the program as a function of OneWire. It is frustrating, because all the ds(PINS);[/b] does is to set up the pin mode, mask, and basereg and then do a reset_search. Nothing at all exotic. I just can not figure out how to access or implement the function to allow the pin number to be changed reliably.
HELP!!! Please. Thank you for any and all comments, suggestions, and assistance in this perplexing C++ puzzle.
Blessings in abundance,
Art in Carlisle, PA USA

Here is what I did to try to add a "change pin" function "apin" to the OneWire library. It didn't work because it is never recognized as an element of OneWire ... the ds.apin function is not highlighted as all the other ds functions are, and the compiler complains that apin is not a function in ds. All the other functions are still recognized.

OneWire.h
=========

  public:
    OneWire( uint8_t pin);

    // ADR 151115.1256  Function to change pin assignment
    void apin( uint8_t pin);


OneWire.cpp
===========

OneWire::OneWire(uint8_t pin)
{
 pinMode(pin, INPUT);
 bitmask = PIN_TO_BITMASK(pin);
 baseReg = PIN_TO_BASEREG(pin);
#if ONEWIRE_SEARCH
 reset_search();
#endif
}

// ADR 151115.1259  Add regular function to change pin
void OneWire::apin(uint8_t pin)
{
 pinMode(pin, INPUT);
 bitmask = PIN_TO_BITMASK(pin);
 baseReg = PIN_TO_BASEREG(pin);
#if ONEWIRE_SEARCH
 reset_search();
#endif
}


keywords.txt
============

#######################################
# Datatypes (KEYWORD1)
#######################################

OneWire KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

apin KEYWORD2
reset KEYWORD2

Any guesses as to what I am overlooking??? Thank you for any and all comments, suggestions, and assistance with this problem.

Blessings,
Art

Dumb, dumb, dumb ... on the Library mod to add apin, I was editing my code on my 'save' disk, which of course does not change the code in /usr/share/arduino/libraries/OneWire. When I copy the files over, the apin is indeed a function in OneWire ... now I just need to see if it works or not.

I'm trying to write a general application for multiple DS18x20 thermometers on multiple pins. I have loaded the OneWire library

Maybe you have good reason, but this seems an utterly pointless exercise that probably incurs unwarranted software overheads. An end result has been done before - without the overheads.

I got it working. The key to the solution was the added "apin" function in the library which will dynamically change the pin for subsequent operations. I showed the library changes in my 2nd post. You have to make sure the changed files get loaded into "/usr/share/arduino/libraries/OneWire". Then, you can call:

ds.apin(pinnum);

and all subsequent operations will occur on pin "pinnum".

Here is some sample code that runs:

// Art Du Rea    imadept AT bigfoot DOT com

// DS18x20 Library
#include <OneWire.h>

/****************    USER  DEFINITIONS    ****************/
// Define the wired pins 
uint8_t PINS[] = {9, 10};  // I AM USING ARDUINO MEGA 2650
// Define the maximum number of Thermo devices
uint8_t DEVS = 8;


/************************************************************/
void setup(void) {
  // Initialize and start the default serial port
  Serial.begin(9600);  // 9600 baud
  // Delay to allow time to turn on the serial monitor
  delay(2000);         // Delay 2 seconds
}


/*********    FUNCTIONS:     ****************************/
// Function to convert a byte into a 2 character string.
//   pdec2:  prefix a leading space for a decimal output
//           0 - 99 only
void pdec2(uint8_t dnum) {
  if (dnum < 10) { Serial.print(" "); }
  Serial.print(dnum);
}
//   phex2:  prefix a leading 0 for a hex output
void phex2(uint8_t hnum) {
  if (hnum < 16) { Serial.print("0"); }
  Serial.print(hnum, HEX);
}


/**********  RUN section VARIABLES     *************/
// loop section variables:
// Device parameter storage:  Pin #, Address[8 bytes]
// Since structs are not fully supported, just use arrays
// This setup will support up to 8 devices
uint8_t pin[DEVS];     // Pin # driving each device - initialized to 0 (not a valid pin)
uint8_t par[DEVS];     // Whether or not this device is on a Parasitic Power pin
uint8_t addr[DEVS][8]; // Array of address for the devices
uint8_t anyPar;     // Set if any device on the wire reports back as "Parasitic"
byte present;       // Set if any device on wire responds to Reset. Initialized on every pass
byte data[12];      // Raw data from read of Scratch Pad

byte i;  // loop iterator - pin index
byte j;  // device iterator - index
char k;  // byte iterator - index  SIGNED
int  n;  // repeat counter

//byte parPwr;   // Parasitic power status for the selected wire/pin
boolean flag;  // "Continue" flag

// Instatniate OneWire to create "ds" name
OneWire ds(9);

/***********  RUN section CODE     ********************/

void loop(void) {
  Serial.print("\n\n");
  // Create a list of devices
  // Initialize the device table to "NO DEVICES" by writing "0" into pin assignment bytes
  for (i = 0; i < DEVS; i++) { pin[i] = 0; }
  j = 0;         // Initialize the Device counter
  // Iterate through the pins creating a list of devices
  for (i = 0; i < sizeof(PINS); i++) {
    flag = TRUE;  // Initialize the "Continue" flag
    // DS18x20 Temperature chip i/o assignment to pin
    ds.apin(PINS[i]);

    // Determine if this pin has any "Parasitic" devices attached to it
    // THIS WILL NOT WORK FOR "PAR" labeled devices which are ONLY Parasitic
    //   They do NOT have "Read Power Supply" capability ???
    present = ds.reset();
    if ( present ) {
      ds.write(0xCC,0);            // Skip ROM, with parasite power OFF at the end
      ds.write(0xB4,0);            // Read Power Supply, with parasite power OFF at the end
      anyPar = ds.read_bit() ^ 1;  // Read and invert the resultant bit

      // find all of the devices on this pin
      // If search is successful, tha address is loaded into the array
      while ( flag ) {
        // Print the pin number as a line prefix
        Serial.print("Pin "); pdec2(PINS[i]);
        // Look for any devices on this wire - NOTE:  addr[j] = *addr[j][0]
        if ( ds.search(addr[j])) {
          // Log the pin number for this device address
          pin[j] = PINS[i];
          // Display the device ROM information
          Serial.print("  ID ");  phex2(addr[j][0]);

          // Print the device type name
          switch (addr[j][0]) {
            case 0x28:
              Serial.print(" DS18B20  ");
              break;
            case 0x10:
              Serial.print(" DS18S20  ");
              break;
            default:  // The device type is unknown ...
              Serial.print(" UNKNOWN  ");
              break;
          }

          // Display the 6 Byte address:
          for (k = 6; k > 0; k--) { phex2(addr[j][k]); }
          // Display the CRC just for luck ???
          Serial.print("  CRC "); phex2(addr[j][7]);

          // Print the parasitic status of this wire
          Serial.print("  ParPwr: ");
          // Load the device parameter array with the anyPar value
          par[j] = anyPar;
          // Print the status
          if (anyPar == 0) { Serial.print("NO\n");  }
          else             { Serial.print("YES\n"); }

          // Increment the device counter
          j += 1;
          if (j >= DEVS) { flag = FALSE; }
        }
        else {  // no more devices were found ... end of search on this pin
          Serial.print("  No more devices on this pin.\n\n");
          ds.reset_search();
          // Force the next pin to be selected
          flag = FALSE;
          delay(250); // ????
        }
      }
    }
    else {  // No Devices on this pin
      Serial.print("No Devices on Pin ");
      Serial.print(PINS[i]);
      Serial.print("\n\n");
    }
  }

  Serial.print("\n\n");

  // Take a bunch of Temp Readings from all attached thermometers
  for (n = 0; n < 13; n++) {

    // Reset the pin selector
    i = 0;
    // Scan the entire address table for devices to read
    for (j = 0; j < DEVS; j++) {
      // Check to make sure this device got loaded
      if ( pin[j] > 0) {
        // Check for a change in the driver pin number
        if (i != pin[j]) {
          // The pin has changed ... set up to use the new pin
          i = pin[j];
          // DS18x20 Temperature chip i/o assignment to pin
          ds.apin(i);  // Set the pin at each change

          // Start a convert operation on ALL devices on this pin/wire
          // Do a reset to clear out all devices
          ds.reset();
          // Issue SKIP ROM to direct next command to ALL devices on the pin
          ds.write(0xCC,0);      // SKIP ROM, with parasite power OFF
          ds.write(0x44,0);      // ALL DEVICES: CONVERT T, with parasite power OFF
          delay(775);  // Wait at least 750 milliseconds for the conversions to complete
        }

        // All devices are done ... Read all devices on the pin 
        ds.reset();          // Reset the bus to shut down any active devices
        ds.select(addr[j]);  // Issue the address of the current device    
        ds.write(0xBE);      // Read the device Scratchpad
        // Read 9 bytes of Scratch Pad Data
        for ( k = 0; k < 9; k++) {           // we need 9 bytes
          data[k] = ds.read();
        }

// TEST TEST TEST TEST
        Serial.print("Dev ");
        pdec2(j);
        Serial.print("  Pin ");
        pdec2(pin[j]);
        Serial.print("  Data  ");
        for (k = 8; k >= 0; k--) {
          phex2(data[k]);
        }
        Serial.print("  ");
        Serial.print(n);
        Serial.print("\n");
// EOT

      }
    } // next device
  }  
}
/****************    EOF    ****************/

That's a big relief ...

TO: Nick_Pyner

Thanks for pointing out the reference ... I haven't come across it in my searches. That code will only allow 1 thermometer chip per pin, and you have to set up the definitions for any changes. My code, once you have set up the "apin" function in the OneWire library, is totally configured by 2 definitions:

// Define the wired pins
uint8_t PINS[] = {9, 10}; // I AM USING ARDUINO MEGA 2650
// Define the maximum number of Thermo devices
uint8_t DEVS = 8;

You can wire up however many devices on however many pins as you want. It will even handle pins with NO devices wired up, and will add new devices automatically (after a reset) when they are wired up. You only change these lines if you use different pins or change the maximum number of pins. The program takes care of the rest. My final program will detect "Parasitic" wired devices and handle the pin "Strong Pullup" automatically. I know there are several people who have tried to do this, but have only partially succeeded.

I hope this might be helpful to others ... the "apin" function is a solution to a long-standing problem.

webtest:
You can wire up however many devices on however many pins as you want.

I hope this might be helpful to others ... the "apin" function is a solution to a long-standing problem.

OK. I have never seen this as a problem, but maybe it could be.........