-
Notifications
You must be signed in to change notification settings - Fork 9
Home
This library implements the SDI-12 v1.3 protocol, natively using Teensy's hardware serial one-wire protocol.
SDI12 is a single wire serial protocol that uses inverted 5V logic levels, specifically (1200 baud, 7E1) for bi-directional data flow with one Master and many Slaves. This library sets up the Freescale Cortex one-wire protocol for each of its 3 Hardware Serial ports TX pin along with critical timing - Break and Mark signals to wake the sensor bus. This makes effectively 3 separate SDI12 buses that can be used or not. Since SDI12 is Master-Slave, many different types of sensor can share the same bus through the use of unique address for each sensor.
Level Shifting:
Since Teensy are 3.3V micrcontrollers it is actually out of SDI12 specification:
- Spacing (3.5V to 5V)
- Marking (-0.5V to 1V)
For proper level shifting I found that you can use the Adafruit's Bi-directional Logic Level Converter TXB0104. I2C level shifters do not work because of the strong pullups which TXB0104 does not have.
Sensor Tested:
Hookup - No Level Shifter:
- Teensy 3.0 are not 5V tolerant, put a resistor inlined with the data line.
- Teensy 3.1 is 5V tolerant so direct connection can be done.
- While SDI12 specification states 12V is used for power, many sensors use a range of values (5-17V).
Connect the sensor to Teensy using Teensy's Vin (5V) for sensor power.
Teensy 3.0 Sensor
---------------- -------------
| Serial Port TX |<------------[10K Resistor]------------->| Data |
| GND |<----------------------------------------| GND |
| Vin |---------------------------------------->| Power |
---------------- -------------
Teensy 3.1 Sensor
---------------- -------------
| Serial Port TX |<--------------------------------------->| Data |
| GND |<----------------------------------------| GND |
| Vin |---------------------------------------->| Power |
---------------- -------------
Connect the sensor to Teensy using external Vin for sensor power.
Teensy 3.0 Sensor
---------------- -------------
| Serial Port TX |<------------[10K Resistor]------------->| Data |
| GND |--------| |-----------| GND |
| Vin |X | | |-------->| Power |
---------------- | External Power | | -------------
| ------- | |
|---->| GND |<-----| |
| Power |--------|
-------
Teensy 3.1 Sensor
---------------- -------------
| Serial Port TX |<--------------------------------------->| Data |
| GND |--------| |-----------| GND |
| Vin |X | | |-------->| Power |
---------------- | External Power | | -------------
| ------- | |
|---->| GND |<-----| |
| Power |--------|
-------
Hookup - Level Shifter:
Connect a sensor using level shifter and Teensy's Vin for sensor power.
_TXB0104__
|--------->|A1 B1|<-----|
| |------->|GND GND|<---| |
| | |----->|3.3V 5V|<-| | |
| | | |--->|OE OE|X | | |
| | | | ---------- | | |
Teensy 3.x | | | | | | | Sensor
---------------- | | | | | | | -------------
| Serial Port TX |<-| | | | | | |--------->| Data |
| GND |<---| | | | |------------| GND |
| 3.3V |------| | | |->| Power |
| DIG PIN |--------| | | -------------
| Vin |---------------------------*-----------|
----------------
Connect a sensor using level shifter using Ext Vin for sensor power.
_TXB0104__
|--------->|A1 B1|<---|
| |------->|GND GND|X |
| | |----->|3.3V 5V|<-| |
| | | |--->|OE |X | |
| | | | ---------- | |
Teensy 3.x | | | | | | Sensor
---------------- | | | | | | -------------
| Serial Port TX |<-| | | | | |--------->| Data |
| GND |<---| | | | |----| GND |
| 3.3V |------| | | | |--| Power |
| DIG PIN |--------| | | | -------------
| Vin |---------------------------| | |
| GND |-----------| | |
---------------- | | |
| External Power | |
| ------- | |
|->| GND |<-----------| |
| PWR |<-------------|
-------
Constructor: --- ```c SDI12( Stream *port, char address, bool crc = false ) ; ``` >1. ```Stream *port``` = One of the Hardware Serial Ports. >2. ```char address``` = Sensor Address, must be preprogramed. >3. ```bool crc```(optional) = Set 'true' to append CRC to sensor data.
Example:
// Define a constructor for each sensor you plan to use.
// Serial Port can either be Serial1, Serial2, Serial3.
// Address have to be ascii values. (0-10),(a-z),(A-Z).
// CRC will be appended to returned data packet for each sensor.
SDI12 DECAGON_5TE_10CM( &Serial3, '1', true );
SDI12 DECAGON_5TE_20CM( &Serial3, '2', true );
SDI12 DECAGON_5TE_30CM( &Serial3, '3', true );
SDI12 DECAGON_5TE_40CM( &Serial3, '4', true );
//SDI12 (Acknowledge Active) "a!" command.
int isActive( int address = -1 );
int address
(optional) = can use other address also.
Example:
int error;
// Check if sensor is active using address defined in constructor.
error = DECAGON_5TE_10CM.isActive( );
if( error ) Serial.println( "Sensor at address 1 Not Active" );
// Check if sensor is active using address defined in constructor.
error = DECAGON_5TE_20CM.isActive( );
if( error ) Serial.println( "Sensor at address 2 Not Active" );
// Check if sensor is active using address defined in constructor.
error = DECAGON_5TE_30CM.isActive( );
if( error ) Serial.println( "Sensor at address 3 Not Active" );
// Check if sensor is active using address defined in constructor.
error = DECAGON_5TE_40CM.isActive( );
if( error ) Serial.println( "Sensor at address 4 Not Active" );
// Check if sensor is active using different address than defined
// in constructor. This allows us to see if it is actually a
// different address.
error = DECAGON_5TE_40CM.isActive( '5' );
if( error ) Serial.println( "Sensor at address 5 Not Active" );
// SDI12 (Send Identification) "aI!" command.
int identification( const char *src ) { identification( (const uint8_t *)src ); }
int identification( const uint8_t *src );
const (char or uint8_t) *src
= buffer array you supply to hold returned string.
Example:
int error;
// Max size of return string is 35 character.
char buf[35]
// If no error then print id string.
error = DECAGON_5TE_10CM.identification( buf );
if( !error ) Serial.println( buf );
// If no error then print id string.
error = DECAGON_5TE_20CM.identification( buf );
if( !error ) Serial.println( buf );
// If no error then print id string.
error = DECAGON_5TE_30CM.identification( buf );
if( !error ) Serial.println( buf );
// If no error then print id string.
error = DECAGON_5TE_40CM.identification( buf );
if( !error ) Serial.println( buf );
// SDI12 (Address Query) "?!" command.
/*** Only ONE sensor can on the bus when using this command! ***/
int queryAddress( void );
Example:
int address;
// Used to see what your sensor address actually is.
// Only one sensor can be connected to bus at a time.
address = DECAGON_5TE_10CM.queryAddress( );
if(address != -1) {
Serial.print( "Sensor Address is " );
Serial.println( (char)address )
}
// SDI12 (Change Address) "aAb!" command.
int changeAddress( uint8_t new_address );
uint8_t new_address
= address you want to change to.
Example:
int address;
// Change address defined in the constructor '1' to '5'.
// This address will be updated for any future use of this function.
address = DECAGON_5TE_10CM.changeAddress( '5' );
if( address != -1 ) {
Serial.print( "New Address is" );
Serial.println( (char)address );
} else {
Serial.println( "Address out of range or command failed" );
}
// SDI12 (Start Verification) "aV!" command.
int verification( const char *src ) { verification( (const uint8_t *)src ); }
int verification( const uint8_t *src );
const (char or uint8_t) *src
= buffer array you supply to hold returned string.
Example:
int error;
// Buffer to hold returned string.
char buf[35];
// Optionally can get debug info on command return
char debug[10];
error = DECAGON_5TE_10CM.verification( debug );
if ( !error ) Serial.print( debug );
// Verification needs a return measurement command to get data
// 'returnMeasurement' function is explained below.
error = DECAGON_5TE_10CM.returnMeasurement( buf, 0 );
if ( !error ) Serial.print( buf );
// SDI12 (Start Measurement) command.
// "aM!", "aMC!" or "aM0...aM9" or "aMC0...aMC9"
int measurement( int num = -1 ) { uint8_t s[75]; measurement( s, num ); }
int measurement( const char *src, int num = -1 ) { measurement( (const uint8_t *)src, num ); }
int measurement( const uint8_t *src, int num = -1 );
const (char or uint8_t) *src
= buffer array you supply to hold acknowledgement string.int num = -1
(optional) = Additional measurements.
Example:
int error;
// buffer to hold sensor measurement acknowledgment string.
char debug[10];
// Max return sensor string size is 81 characters.
char data[81];
memset( data, 0, 81 );
memset( debug, 0, 10 );
// Function to tell sensor to make a measurement.
// optional additional measurements command.
/***error = DECAGON_5TE_10CM.measurement( debug, 0 );***/
error = DECAGON_5TE_10CM.measurement( debug );
if ( !error ) Serial.print( debug );
// 'measurement' needs a return measurement command to get data.
// 'returnMeasurement' function is explained below.
error = DECAGON_5TE_10CM.returnMeasurement( data, 0 );
if ( !error ) Serial.print( data );
memset( data, 0, 81 );
memset( debug, 0, 10 );
// Function to tell sensor to make a measurement.
// optional additional measurements command.
/***error = DECAGON_5TE_20CM.measurement( debug, 0 );***/
error = DECAGON_5TE_20CM.measurement( debug );
if ( !error ) Serial.print( debug );
error = DECAGON_5TE_20CM.returnMeasurement( data, 0 );
if (!error) Serial.print( data );
memset( data, 0, 81 );
memset( debug, 0, 10 );
// Function to tell sensor to make a measurement.
// optional additional measurements command.
/***error = DECAGON_5TE_30CM.measurement( debug, 0 );***/
error = DECAGON_5TE_30CM.measurement( debug );
if ( !error ) Serial.print( debug );
error = DECAGON_5TE_30CM.returnMeasurement( data, 0 );
if ( !error ) Serial.print( data );
memset( data, 0, 81 );
memset( debug, 0, 10 );
// Function to tell sensor to make a measurement.
// optional additional measurements command.
/***error = DECAGON_5TE_40CM.measurement( debug, 0 );***/
error = DECAGON_5TE_40CM.measurement( debug );
if ( !error ) Serial.print( debug );
error = DECAGON_5TE_40CM.returnMeasurement( data, 0 );
if ( !error ) Serial.print( data );
// SDI12 (Start Concurrent Measurement) command.
// "aC!","aCC!" or "aC0...aC9" or "aCC0...aCC9"
int concurrent( int num = -1 ) { uint8_t s[75]; concurrent( s, num ); }
int concurrent( const char *src, int num = -1 ) { concurrent( (const uint8_t *)src, num ); }
int concurrent( const uint8_t *src, int num );
const (char or uint8_t) *src
= buffer array you supply to hold acknowledgement string.int num = -1
(optional) = Additional measurements.
Example:
// Not implemented yet...
/*
* This function will provide a non blocking way to read sensors.
* This will be the function used in "Background Mode" where
* sensors can be logged automatically.
*/
// SDI12 (Start Continuous Measurement) command.
// "aR0!...aR9!" or "aRC0!...aRC9!"
int continuous( const char *src, int num = -1 ) { continuous( (const uint8_t *)src, num ); }
int continuous( const uint8_t *src, int num = -1 );
const (char or uint8_t) *src
= buffer array you supply to hold returned string.int num = -1
(optional) = Additional measurements.
Example:
int error;
// buffer to hold sensor measurement string.
// Max return sensor string size is 81 characters.
char data[81];
memset( data, 0, 81 );
// Function to tell sensor to get a continuous measurement
// if the sensor supports it.
// optional additional measurements command.
/***error = DECAGON_5TE_10CM.continuous( data, 0 );***/
error = DECAGON_5TE_10CM.continuous( data );
if ( !error ) Serial.print( data );
// SDI12 (Return Measurement) command.
// "aD!" or "aD0!...aD9!"
int returnMeasurement( const char *src, int num = -1 ) { returnMeasurement( (const uint8_t *)src, num ); }
int returnMeasurement( const uint8_t *src, int num = -1 );
const (char or uint8_t) *src
= buffer array you supply to hold returned string.int num = -1
(optional) = Additional measurements.
Example:
// This function will send the send data command to get the data string.
// Example is provided with 'measurement' function above.
// SDI12 (Transparent) command. Allows extended SDI12 commands.
int transparent( const char *command, const uint8_t *src ) { transparent( (uint8_t*)command, src ); }
int transparent( const uint8_t *command, const char *src ) { transparent( command, (uint8_t*)src ); }
int transparent( const char *command, const char *src ) { transparent( (uint8_t*)command, (uint8_t*)src ); }
int transparent( const uint8_t *command, const uint8_t *src );
const (char or uint8_t) *command
= SDI12 command to send.const (char or uint8_t) *src
= buffer array you supply to hold returned string.
Example:
int error;
// Command to send.
char cmd[3] = "2I!";
// buffer to hold sensor measurement string.
// Max return sensor string size is 81 characters.
char data[81];
memset( data, 0, 81 );
// Function to tell sensor to send a transparent command.
// If using 'M' command it will handle sensor acknowledgement also.
error = DECAGON_5TE_10CM.transparent( cmd, data );
if ( !error ) Serial.print( data );