3
votes

I'm trying to talk serial with an SDI-12 device, and it requires inverted seven data bits, even parity and one stop bit (7E1) serial at 1200 baud.

From the datasheet:

SDI-12 communication sends characters at 1200 bits per second. Each character has 1 start bit, 7 data bits (LSB first), 1 even parity bit, and 1 stop bit (Active low or inverted logic levels):

All SDI-12 commands and response must adhere to the following format on the data line. Both the command and response are preceded by an address and terminated by a carriage return line feed combination.

Is this possible with the Serial or SoftwareSerial libraries? I am trying to avoid additional hardware (beyond a levelshifter to 3.3 V), but I will do so if it is the only way.

I have seen that SoftwareSerial can do inverted, and Serial can do 7E1, but I can't find if either can do both.

I have access to a Arduino Mega (R2), and Arduino Uno (R3).

Here is the device I want to communicate with: http://www.decagon.com/products/sensors/soil-moisture-sensors/gs3-soil-moisture-temperature-and-ec/ and here, http://www.decagon.com/assets/Uploads/GS3-Integrators-Guide.pdf is the document explaining the protocol. Page 6 talks about its implementation of SDI.

2
This is standard for any serial communications. Have you tried it? en.wikipedia.org/wiki/RS-232#Voltage_levelsHans Passant
It is 0-3V, not +-3V, I think this means it is not RS232?Quatch
@HansPassant, From the Wikipedia entry for the SDI-12: "The data signal, using 5 V logic levels, bears no resemblance to RS-485 or RS-232 although the timing is similar."Brad
I posted the link to show that inverted logic levels is standard.Hans Passant
Thank you both for having a look at this. I've updated the main post with links to the specifics I'm dealing with. My question is then, are they talking about "inverted logic levels" as inverted SDI or inverted TTL? This may all just be me being confused (I'm new at using the serial interface, most everything else has been I2C or SPI for me) and the normal Serial library with the right parity and stop settings would be sufficient. I don't want to give it a go until I'm sure I can't harm the device by sending bad commands/voltages.Quatch

2 Answers

1
votes

I'm not familiar with Arduino, however the SDI-12 physical layer is inverted from the standard TTL levels - probably for two reasons:

  1. Since the idle voltage is 0V, this results in lower standby power (due to nominal pull-down resistors in a typical SDI-12 sensor.
  2. It facilitates simple bus 'sniffing' using a standard RS-232 serial port.

Short of bit-banging a 5V IO pin - yes, if using a standard microcontroller UART you will need an external inverter (or 2) and a 3-state buffer. Possibly requiring level shifting, depending on your hardware.

Thumbs down to the Wikipedia entry - SDI-12 uses entirely standard UART bit timings (very much like RS-232), just different signal levels (0 - 5V); see point #2. However, there are specific break sequences and strict timing requirements, which makes firmware development more difficult.

If you are serious about SDI-12 firmware development, you may want to invest in an SDI-12 Verifier. A thorough study of the specification is essential.

1
votes

A little late... but better late than never

I have actually just written a library for exactly that (actually exactly that including the sensors ... so it should work exactly with the included examples )

https://github.com/joranbeasley/SDISerial (Arduino Library)

#include <SDISerial.h> //https://github.com/joranbeasley/SDISerial (Arduino Library)
#include <string.h>
#define DATA_PIN 2
SDISerial connection(DATA_PIN);
char output_buffer[125]; // just for uart prints
char tmp_buffer[4];
char sensor_info[]
//initialize variables
void setup(){
      connection.begin();
      Serial.begin(9600);//so we can print to standard uart
      //small delay to let the sensor do its startup stuff
      delay(3000);//3 seconds should be more than enough
      char* sensor_info = connection.sdi_query("0I!",1000); // get sensor info for address 0
}

//main loop
void loop(){
    //print to uart
    Serial.println("Begin Command: ?M!");

    //send measurement query (M) to the first device on our bus
    char* resp = connection.service_request("0M!","0D0!");//Get Measurement from address 0


    sprintf(output_buffer,"RECV: %s",resp?resp:"No Response Recieved!!");
    Serial.println(output_buffer);
    delay(10000);//sleep for 10 seconds before the next read
}