Skip to content

4. Waking up, and talking to, the sensors.

Kevin M. Smith edited this page Apr 8, 2014 · 5 revisions

4.1 wakeSensors()

wakeSensors() literally wakes up all the sensors on the bus. The SDI-12 protocol requires a pulse of HIGH voltage for at least 12 milliseconds followed immediately by a pulse of LOW voltage for at least 8.3 milliseconds. The values here are close to those values, but provide 100 extra microseconds of wiggle room. Setting the SDI-12 object into the TRANSMITTING allows us to assert control of the line without triggering any interrupts.

void SDI12::wakeSensors(){
  setState(TRANSMITTING); 
  digitalWrite(_dataPin, HIGH); 
  delayMicroseconds(12100); 
  digitalWrite(_dataPin, LOW); 
  delayMicroseconds(8400);
}

4.2 writeChar(uint8_t out)

This function writes a character out to the data line. SDI-12 specifies the general transmission format of a single character as:

                  10 bits per data frame 		
                            1 start bit
                            7 data bits (least significant bit first)
                            1 even parity bit
                            1 stop bit	

We also recall that we are using inverse logic, so HIGH represents 0, and LOW represents a 1. If you are unclear on any of these terms, I would recommend that you look them up before proceeding. They will be better explained elsewhere.

The transmission takes several steps. The variable name for the outgoing character is "out".

4.2.1 - Determine the proper even parity bit (will an additional 1 or 0 make the final number of 1's even?)

First we grab the bit using an optimized macro from parity.h parity_even_bit(out)

Then we bit shift it into the proper place, which is the most significant bit position, since the characters we are using are only 7-bits. (parity_even_bit(out)<<7);

Then we use the '|=' operator to set the bit if necessary.

4.2.2 - Send the start bit. The start bit is always a '0', so we simply write the dataPin HIGH for SPACING microseconds.

4.2.3- Send the payload (the 7 character bits and the parity bit) least significant bit first. This is accomplished bitwise AND operations on a moving mask (00000001) --> (00000010) --> (00000100)... and so on. This functionality makes use of the '<<=' operator which stores the result of the bit-shift back into the left hand side.

If the result of (out & mask) determines whether a 1 or 0 should be sent. Again, here inverse logic may lead to easy confusion.

if(out & mask){
      digitalWrite(_dataPin, LOW);
    }
    else{
      digitalWrite(_dataPin, HIGH);
	} 

4.2.4- Send the stop bit. The stop bit is always a '1', so we simply write the dataPin LOW for SPACING microseconds. All together:

void SDI12::writeChar(uint8_t out)
{

  out |= (parity_even_bit(out)<<7);            // 4.2.1 - parity bit

  digitalWrite(_dataPin, HIGH);                // 4.2.2 - start bit
  delayMicroseconds(SPACING);

  for (byte mask = 0x01; mask; mask<<=1){      // 4.2.3 - send payload
    if(out & mask){
      digitalWrite(_dataPin, LOW);
    }
    else{
      digitalWrite(_dataPin, HIGH);
    } 
    delayMicroseconds(SPACING);
  }
  
  digitalWrite(_dataPin, LOW);                 // 4.2.4 - stop bit
  delayMicroseconds(SPACING); 
}

4.3 sendCommand(String cmd)

sendCommand(String cmd) is a publicly accessible function that sends out a String byte by byte the command line.

void SDI12::sendCommand(String cmd){
  wakeSensors();						// wake up sensors
  for (int i = 0; i < cmd.length(); i++){
	writeChar(cmd[i]); 					// write each characters
  }	
  setState(LISTENING); 						// listen for reply
}