Go Down

Topic: SDI-12 compatibility (Read 5352 times) previous topic - next topic

andywatson

I have some sensors that only have SDI-12 output.  Is there an easy way to interface them with an Arduino?

cr0sh

I assume you mean this:

http://www.sdi-12.org/

You should be able to communicate with such devices easily using the Arduino, as the serial data stream is just HIGH/LOW pulses of (approximately) 0 and 5 VDC, where a LOW logic level (0V) is equivalent to a binary state of "1" and a HIGH logic level (5V) is equivalent to a binary state of "0"; in other words, inverse logic.

I don't know of a library or anything that makes it simple to communicate using an Arduino, but you should be able to adapt one of the "2-wire" serial libraries with only a few changes (mainly inverting the logic if needed, and adjusting for 1200 baud).

I'm sure there are other methods (ie, using a level or protocol converter IC or similar), but in theory, based on my (admittedly) limited quick reading of the spec of the protocol, it should be something you can directly interface to with the Arduino using a single digital pin, and appropriate driver/interface software...

:)
I will not respond to Arduino help PM's from random forum users; if you have such a question, start a new topic thread.

andywatson

Yep, that's the protocol.  It's rather common in the environmental sensor world.  It looks like the Arduino could handle it, I just dont' have the experience or time to brute force my way through designing an interface, so I'm hoping someone else has done it, or has something that could be easily adapted to work with Arduino.

LAVco

I guess I am curious why you would want to interface an SDI-12 sensor to an Arduino. 

SDI-12 sensors have their own MCU, most pre-process measurements already (e.g. smp, avg, min, max, etc.) which can be called using the SDI protocol based on how the manufacturer has it configured and typically support other output types e.g. RS232/485.  SDI-12 is great for low powered applications and supports daisy chaining of sensors as the protocol supports sensor addressing.  It's a slick protocol developed by Eric Campbell of Campbell Scientific, worked for the company, and have used it frequently (sensor & data-logger development).

If your an intermediate programmer I would suggest polling the sensor via RS232.  Check the sensor because some support TTL, if thats the case, use the SoftSerial library and two IO ports.

Common sense is not so common...

dogleg67t2

Just wondering whether anyone had made progress with this topic: SDI-12 on Arduino?
There are plenty of SDI-12 compatible data loggers in the market place and on the other hand, plenty of good, simple sensor ideas around for Arduino.
What would be good would be to bring them together: that is using Arduino as a simple front end for some SDI-12 smart sensors.

terke

Hi all,

I don't suppose anybody has found a sdi12.h library? I have a very nice anemometer that uses SDI-12 communication.

Thx

terke

Here is a newbie thought. Please tell me if this is a dumb one:

If I tie a couple of Rx and Tx pins together, and wrap all Tx serial commands in toggling the interrupt register OFF then ON.

For example for pins 14 & 15:

PCICR |= (0<<PCIE2);                 // disables  interrupts on pins 8-15
serial.print("0");
serial.print(13, BYTE);
serial.print(10, BYTE);
PCICR |= (1<<PCIE2);                 // enables  interrupts on pins 8-15

Would this allow me to communicate over the same SDI-12 line using standard serial commands? If yes; here comes the next question:

How can I apply this to say pin 30 and 31 using the NewSoftSerial.h library. I can't even figure out what the pin-change-register is for pins 30 and 31. Besides that, it would be even better to figure out how to modify the NewSoftSerial library to pull this trick.

Cheers,
Terke

vogel1230

I've just received one of these devices. I've yet to hook it up and try it yet, but I thought I would provide the link. I work for the National Weather Service and we use quite a bit of SDI-12. I wanted to see if I could interface with our SDI-12 sensors effectively.

http://www.vegetronix.com/Products/SDI-12.phtml

terke

Thanks,

Had a look at that board. Nice, but the mega should simply be able to talk SDI-12. It is just a matter of finding the trick.

Cheers,
terke

4A_5E_33

I modified the software serial code to work for some sdi-12 sensor I was working with. The documentation for sdi-12 isn't the best but it wasn't to hard. Just invert the digitalRead part of the code and reduce it for a 7 bit string. Let me know and I'll post the modified softwareSerial.

robtillaart


started some work on a SDI-12 lib (anemometer) a few weeks ago, never finished it  :smiley-red:  no sensor => could not test

It contains several shortcuts, but it could get someone started
Code: [Select]

//
//    FILE: SDI-12.001.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-mar-18
//
// PUPROSE: talk to SDI-12 anemometer
//

void setup()
{
  // communication with PC
  Serial.begin(115200);
  Serial.println("\nSDI-12 0.01");

  // hardware serial1 for the sensor  [MEGA, optional replace with softSerial]
  Serial1.begin(1200);
}

void loop()
{
  char *answer;
 
  answer = addressQuery(); // char, no string
  Serial.print("\n addressQuery: ");
  Serial.println(answer);

  AcknowledgeActive('0'); // char, no string
  Serial.print("\n Address: ");
  Serial.println(answer);

  addressChange('0', 'Z');
  Serial.println(answer);
  addressChange('Z', '0');
  Serial.println(answer);

  sendIdentification('0'); // char, no string
  Serial.print("\n id: ");
  Serial.println(answer);

  int t = 0;
  int n = 0;

  startMeasurement('0', &t, &n);
  Serial.print("\n startMeasurement: ");
  Serial.println(answer); 
  Serial.println(t, DEC); 
  Serial.println(n, DEC);

  delay(t*1000UL); // wait for the measurement to be ready

  sendData('0');
  Serial.print("\n sendData: ");
  Serial.println(answer);

  delay(20000UL);
}

/////////////////////////////////////////////////////////
//
// Proto  SDI-12 commands
//
// names of functions, Table 5, page 8, SDI-12 1.3 specification
//


/////////////////////////////////////////////////////////
//
// Private members
//
char response[80];    // at least 35/75 according to 1.3 spec

/////////////////////////////////////////////////////////
//
// Public part I (admin commands)
//
char* AcknowledgeActive(char address)
{
  char command[] = "a!";
  command[0] = address;
  request(2, command, response);
  return response;
}

char* addressQuery()
{
  char command[] = "?!";
  request(2, command, response);
  return response;
}

char* addressChange(char oldAddress, char newAddress)
{
  char command[] = "aAb!";
  command[0] = oldAddress;
  command[2] = newAddress;
  request(4, command, response);
  return response;
}

char* sendIdentification(char address)
{
  char command[] = "aI!";
  command[0] = address;
  request(3, command, response);
  return response;
}

void printIdentification(char *idStr)
{
  // TODO
}

/////////////////////////////////////////////////////////
//
// Public part II (measurements)
//
char* startMeasurement(char address, int *t, int *n)
{
  char command[] = "aI!";
  command[0] = address;
  request(3, command, response);

  int len = strlen(response);
  *n = (int) (response[len-1] - '\0');
  *t = atoi(&response[1]) / 10;

  return response;
}

char* sendData(char address)
{
  char command[] = "aD0!";
  command[0] = address;
  request(4, command, response);
  return response;
}


/////////////////////////////////////////////////////////
//
// Private part I
//
void request(uint8_t length, char *command, char *response)
{
  // send command
  for (int idx=0; idx<length; idx++)
  {
    Serial.write(command[idx]);
  }

  // blocking until answer comes in
  char c = ' ';
  int idx = 0;
  while (c != 10)  // answer ends with LineFeed = char 10
  {
    if (Serial.available() > 0)
    {
      c = Serial.read();
      response[idx] = c & 0x7F;  // strip bit 8
      idx++;
      if (idx > 75) break; // prevent overflow answer;
    }
  }
  response[idx-2] = 0; // remove the CRLF and end string correctly
}

// --- END OF FILE ---
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

ding444

@4A_5E_33 Would you mind posting that modified library for some of us to take a look at?  I've been trying to get a good interface method for SDI-12 sensors for a while and would be very interested in seeing your method.

terke

#12
Jun 20, 2012, 08:30 am Last Edit: Mar 02, 2014, 08:23 am by robtillaart Reason: 1
Hello All;

I'm making a little progress in talking with my SDI-12 sensor. I'm using Rob Tillaart's code as a starting point with a few changes:
First off I switched to SoftwareSerial.h and then set the UART to inverted logic. Before I had tried with Rob's code but that kept the Tx (pin 10) port high all the time which won't work with SDI-12. I added a 13ms HIGH pulse, followed by 9ms of LOW on the Tx pin before sending the command. You can see the SDI-12 answer but at slightly lower voltage. The voltage is lower for the reply because the Tx (pin 10) and Rx (pin 11) pins are tied together. At first they were simply tied together, but then the reply voltage was only about 1.8V because of the open Tx pin. Now the Tx pin talks through a 2.2kOhm resistor and far more signal is available for the Rx pin. If I could figure out how to pull the internal resistor on the Tx pin high I would give that a try.

I have attached two pics. One of the scope showing the 13ms High pulse, 9ms Low, the request from the Arduino, and finally the reply from the sensor.

The second pic is a screen grab of the terminal output. I send the configuration commands to the sensor, and then try to ask for the sensor to send back what it has for configuration. For some reason, the I can only read a small part of the settings back when requested. I can't figure out why.

And here is the code:
Code: [Select]

//
//    FILE: SDI-12.001.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-mar-18
//
// PUPROSE: talk to SDI-12 anemometer
//

#include <SoftwareSerial.h>

#define txPin 10
#define rxPin 11
#define INVERTED true          //SDI-12 uses inverted logic

SoftwareSerial sdiAnem(rxPin, txPin, INVERTED);

void setup()
{
// communication with PC
Serial.begin(115200);
Serial.println("\nSDI-12 0.01");

// software serial for the sensor
sdiAnem.begin(1200);

delay(9);
digitalWrite(txPin, HIGH);
delay(13);
digitalWrite(txPin, LOW);
delay(9);
sdiAnem.write("0XXU,A=0,M=R,C=1,B=1200,D=7,P=E,S=1!");
Serial.println("0XXU,A=0,M=R,C=1,B=1200,D7,P=E,S=1!");

delay(9);
digitalWrite(txPin, HIGH);
delay(13);
digitalWrite(txPin, LOW);
delay(9);
sdiAnem.write("0XWU,R=01001100&01001100,I=10,A=60,G=3,U=N,D=0,N=W,F=4!");
Serial.println("0XWU,R=01001100&01001100,I=10,A=60,G=3,U=N,D=0,N=W,F=4!");

char incomingAnswer[80];
char a;
int idy = 0;
delay(9);
digitalWrite(txPin, HIGH);
delay(13);
digitalWrite(txPin, LOW);
delay(9);
sdiAnem.write("0SU!");
  if (sdiAnem.available() > 0)
  {
    a = sdiAnem.read();
    incomingAnswer[idy] = a & 0x7F;  // strip bit 8
    idy++;
  }
Serial.print("Reported settings are: ");  
Serial.println(incomingAnswer);
 

}

void loop()
{
char *answer;

answer = addressQuery(); // char, no string
Serial.print("\n addressQuery: ");
Serial.println(answer);

AcknowledgeActive('0'); // char, no string
Serial.print("\n Address: ");
Serial.println(answer);

sendIdentification('0'); // char, no string
Serial.print("\n id: ");
Serial.println(answer);

int t = 0;
int n = 0;

/* startMeasurement('0', &t, &n);
Serial.print("\n startMeasurement: ");
Serial.println(answer);
Serial.println(t, DEC);
Serial.println(n, DEC);

delay(t*1000UL); // wait for the measurement to be ready
*/
sendData('0');
Serial.print("\n sendData: ");
Serial.println(answer);

delay(20000UL);
}

/////////////////////////////////////////////////////////
//
// Proto  SDI-12 commands
//
// names of functions, Table 5, page 8, SDI-12 1.3 specification
//


/////////////////////////////////////////////////////////
//
// Private members
//
char response[80];    // at least 35/75 according to 1.3 spec

/////////////////////////////////////////////////////////
//
// Public part I (admin commands)
//
char* AcknowledgeActive(char address)
{
char command[] = "a!";
command[0] = address;
request(2, command, response);
return response;
}

char* addressQuery()
{
char command[] = "?!";
request(2, command, response);
return response;
}

char* addressChange(char oldAddress, char newAddress)
{
char command[] = "aAb!";
command[0] = oldAddress;
command[2] = newAddress;
request(4, command, response);
return response;
}

char* sendIdentification(char address)
{
char command[] = "aI!";
command[0] = address;
request(3, command, response);
return response;
}

void printIdentification(char *idStr)
{
// TODO
}

/////////////////////////////////////////////////////////
//
// Public part II (measurements)
//
char* startMeasurement(char address, int *t, int *n)
{
char command[] = "aI!";
command[0] = address;
request(3, command, response);

int len = strlen(response);
*n = (int) (response[len-1] - '\0');
*t = atoi(&response[1]) / 10;

return response;
}

char* sendData(char address)
{
char command[] = "0R1!";
request(4, command, response);
return response;
}


/////////////////////////////////////////////////////////
//
// Private part I
//
void request(uint8_t length, char *command, char *response)
{
// send command
delay(9);
digitalWrite(txPin, HIGH);
delay(13);
digitalWrite(txPin, LOW);
delay(9);
for (int idx=0; idx<length; idx++)
{
  sdiAnem.write(command[idx]);
}

// blocking until answer comes in
char c = ' ';
int idx = 0;
while (c != 10)  // answer ends with LineFeed = char 10
{
  if (sdiAnem.available() > 0)
  {
    c = sdiAnem.read();
    response[idx] = c & 0x7F;  // strip bit 8
    idx++;
  }
}
response[idx-2] = 0; // remove the CRLF and end string correctly
}

// --- END OF FILE ---


noderator added code tags

quatch

Hi, just letting you know I'm also interested in your work here. Thanks for taking the time to post your code, and I'd love to know if you've made more progress.

joranbeasley

I have just created this exact library :)

https://github.com/joranbeasley/SDISerial

Go Up