Issues reading information from sensor using SDI-12 protocole

Hello everyone,

I’m using the example from the SDI12 library: d_simple_logger

I’ve made some corrections of my own, because the sensor’s address=0 and trying to reduce RAM consumption.

#include <SDI12.h>

SDI12 mySDI12(9); 

// keeps track of active addresses
// each bit represents an address:
// 1 is active (taken), 0 is inactive (available)
// setTaken('A') will set the proper bit for sensor 'A'
// set 
byte addressRegister[2] = { 
  0B00000000, 
  0B00000000
}; 


void setup(){
  Serial.begin(9600); 
  mySDI12.begin(); 
  delay(500); // allow things to settle

  Serial.println("Scanning all addresses, please wait..."); 
  /*
      Quickly Scan the Address Space
   */

  for(byte i = '0'; i <= '9'; i++) if(checkActive(i)) setTaken(i);   // scan address space 0-9
  /*
      See if there are any active sensors. 
   */
  boolean found = false; 

  for(byte i = 0; i < 62; i++){
    if(isTaken(i)){
      found = true;
      break;
    }
  }

  if(!found) {
    Serial.println("No sensors found, please check connections and restart the Arduino."); 
    while(true);
  } // stop here
  
  Serial.println(); 
  Serial.println("Time Elapsed (s), Sensor Address and ID, Measurement 1, Measurement 2, ... etc."); 
  Serial.println("-------------------------------------------------------------------------------");
}

void loop(){

  // scan address space 0-9
  for(char i = '0'; i <= '9'; i++) if(isTaken(i)){
    Serial.print(millis()/1000);
    Serial.print(",");
    printInfo(i);
    mySDI12.flush();   
    takeMeasurement(i);
  }
  delay(10000); // wait ten seconds between measurement attempts. 

}

void takeMeasurement(char i){
  String command = ""; 
  command += i;
  command += "M!"; // SDI-12 measurement command format  [address]['M'][!]
  mySDI12.sendCommand(command); 
  while(!mySDI12.available()>5); // wait for acknowlegement with format [address][ttt (3 char, seconds)][number of measurments available, 0-9]
  delay(100); 
  
  mySDI12.read(); //consume address
  
  // find out how long we have to wait (in seconds).
  int wait = 0; 
  wait += 100 * mySDI12.read()-'0';
  wait += 10 * mySDI12.read()-'0';
  wait += 1 * mySDI12.read()-'0';
  Serial.println(wait);
  Serial.println(mySDI12.read()); // ignore # measurements, for this simple examlpe
  Serial.println(mySDI12.read()); // ignore carriage return
  Serial.println(mySDI12.read()); // ignore line feed
  
  long timerStart = millis(); 
  while((millis() - timerStart) > (1000 * wait)){
    if(mySDI12.available()) break;                //sensor can interrupt us to let us know it is done early
  }
  
  // in this example we will only take the 'DO' measurement  
  mySDI12.flush(); 
  command = "";
  command += i;
  command += "D0!"; // SDI-12 command to get data [address][D][dataOption][!]
  mySDI12.sendCommand(command);
  while(!mySDI12.available()>1); // wait for acknowlegement  
  delay(300); // let the data transfer
  printBufferToScreen(); 
  mySDI12.flush(); 
}

void printBufferToScreen(){
  String buffer = "";
  mySDI12.read(); // consume address
  while(mySDI12.available()){
    char c = mySDI12.read();
    if(c == '+' || c == '-'){
      buffer += ',';   
      if(c == '-') buffer += '-'; 
    } 
    else {
      buffer += c;  
    }
    delay(100); 
  }
 Serial.print(buffer);
}


// this checks for activity at a particular address     
// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z'
boolean checkActive(char i){              

  String myCommand = "";
  myCommand = "";
  myCommand += (char) i;                 // sends basic 'acknowledge' command [address][!]
  myCommand += "!";

  for(int j = 0; j < 3; j++){            // goes through three rapid contact attempts
    mySDI12.sendCommand(myCommand);
    if(mySDI12.available()>1) break;
    delay(30); 
  }
  if(mySDI12.available()>2){       // if it hears anything it assumes the address is occupied
    mySDI12.flush(); 
    return true;
  } 
  else {   // otherwise it is vacant. 
    mySDI12.flush(); 
  }
  return false; 
}


// this sets the bit in the proper location within the addressRegister
// to record that the sensor is active and the address is taken. 
boolean setTaken(byte i){          
  boolean initStatus = isTaken(i);
  i = charToDec(i); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. 
  byte j = i / 8;   // byte #
  byte k = i % 8;   // bit #
  addressRegister[j] |= (1 << k); 
  return !initStatus; // return false if already taken
}

// THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL. 
// this unsets the bit in the proper location within the addressRegister
// to record that the sensor is active and the address is taken. 
boolean setVacant(byte i){
  boolean initStatus = isTaken(i);
  i = charToDec(i); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. 
  byte j = i / 8;   // byte #
  byte k = i % 8;   // bit #
  addressRegister[j] &= ~(1 << k); 
  return initStatus; // return false if already vacant
}


// this quickly checks if the address has already been taken by an active sensor           
boolean isTaken(byte i){         
  i = charToDec(i); // e.g. convert '0' to 0, 'a' to 10, 'Z' to 61. 
  byte j = i / 8;   // byte #
  byte k = i % 8;   // bit #
  return addressRegister[j] & (1<<k); // return bit status
}

// gets identification information from a sensor, and prints it to the serial port
// expects a character between '0'-'9', 'a'-'z', or 'A'-'Z'. 
char printInfo(char i){
  int j; 
  String command = "";
  command += (char) i; 
  command += "I!";
  for(j = 0; j < 1; j++){
    mySDI12.sendCommand(command);
    delay(30); 
    if(mySDI12.available()>1) break;
    if(mySDI12.available()) mySDI12.read(); 
  }

  while(mySDI12.available()){
    char c = mySDI12.read();
    if((c!='\n') && (c!='\r')) Serial.write(c);
    delay(5); 
  } 
}

// converts allowable address characters '0'-'9', 'a'-'z', 'A'-'Z',
// to a decimal number between 0 and 61 (inclusive) to cover the 62 possible addresses
byte charToDec(char i){
  if((i >= '0') && (i <= '9')) return i - '0';
}

// THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL. 
// maps a decimal number between 0 and 61 (inclusive) to 
// allowable address characters '0'-'9', 'a'-'z', 'A'-'Z',
char decToChar(byte i){
  if((i >= 0) && (i <= 9)) return i + '0';
}

The code shows the time, address, SDI-12 version, company, sensor’s version and serial number, after that there’s nothing in the serial port, don’t know if it’s because the buffer gets full or it doesn’t break the for loop in this part

long timerStart = millis(); 
  while((millis() - timerStart) > (1000 * wait)){
    if(mySDI12.available()) break;                //sensor can interrupt us to let us know it is done early
  }

Any suggestions?

  long timerStart = millis();
  while ((millis() - timerStart) > (1000 * wait)) {
    if (mySDI12.available()) break;               //sensor can interrupt us to let us know it is done early
  }

Have you got the comparison the wrong way round in the while ?

UKHeliBob:
Have you got the comparison the wrong way round in the while ?

I left that part untouched since the code was developped by programmers, but I’ll try to invert it.
And also there’s another trouble, the sensor is not here, so I have to wait until my supervisor tells me we’re going to test anything.

What sort of value do you get for 1000 * wait ?

UKHeliBob:
What sort of value do you get for 1000 * wait ?

The variable "wait" is in seconds, so I guess it's 1000*(seconds), you've made me realize that I should know how that part works before going on.

I really don't know what's happening inside the while loop.

  long timerStart = millis();
  while ((millis() - timerStart) > (1000 * wait)) {
    if (mySDI12.available()) break;               //sensor can interrupt us to let us know it is done early
  }

The variable "wait" is in seconds

millis() - timerStart is going to be a very small value.

How likely is it that it will be greater than 1000 * wait when first tested in the while loop ?

UKHeliBob:
millis() - timerStart is going to be a very small value.

How likely is it that it will be greater than 1000 * wait when first tested in the while loop ?

There's a really small chance, the sensor has to wait (most of the times) at least 5 seconds before measuring. So I guess the comparison inside the while should be something like:
while(timerStart > wait)

What do you think?

What do you think?

That won't work. timerStart is a copy of the millis() value at a particular time so is going to get larger and larger for 49 and a bit days until it rolls over to zero.

the sensor has to wait (most of the times) at least 5 seconds before measuring.

So, what are you trying to achieve ? Is it a 5 second delay before taking a reading ? Even if the while loop worked it would block operation of the code so you might just as well use delay(5000);

If you want other parts of the code to keep running during the 5 second waiting period then millis() is the way to go but not used as it currently is.

Have a look at the BlinkWithoutDelay example in the IDE and Several things at the same time

I found out how it works and why is it there, it keeps the process inside the while loop until there’s information coming from the sensor. When there’s something it goes out from the while loop. So I think that’s not the trouble.

it keeps the process inside the while loop until there's information coming from the sensor.

I am sure that is what it is supposed to do, but does it ?

Read what I said in reply #5

millis() - timerStart is going to be a very small value.

How likely is it that it will be greater than 1000 * wait when first tested in the while loop ?

Yes it does, it waits there, it's probable that the code goes on, beacause it starts all again.

I'm not convinced.
Suppose millis() is 2000 when you assign its value to timerStart.
Immediately afterwards you calculate millis() - timerStart. What magnitude of number will you get ? Ten, a hundred, a thousand

I did a test out of the whole program, step by step, it seems to work just fine.

When the program goes back to that point it does everything as it should.