-
-
Notifications
You must be signed in to change notification settings - Fork 100
4. Waking up, and talking to, the sensors.
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);
}
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);
}
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
}