SDI-12 protocol in the firmware app
I have created the library files required for the EXO3s communication - exo3s_sensor.cpp and exo3s_sensor.h along with user_code.cpp and user_code.h files in my branch based off of the serial_payload_example.
Setting the Baud rate to 1200
Within the init()
in the exo3s_sensor.cpp
, this is what I added to set the baud rate to 1200. BAUD_RATE
is defined to be 1200.
PLUART::setBaud(BAUD_RATE);
Data Frame configurations
By default, the settings for the LPUART are - 1 start bit, 8 data bits, no parity bit, 1 stop bit.
But we need to change this. The only successful way to configure it in this way is to set this in the Lower Level files - usart.c
(Note: This is not the right way to do it since I am editing a bsp
, board support package, file)
Within the MX_LPUART1_UART_Init()
, these are the configs I changed to set the LPUART to 1 start bit, 7 data bits, 1 even parity bit, 1 stop bit
LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B; // including the parity bit
LPUART_InitStruct.Parity = LL_LPUART_PARITY_EVEN;
Serial line is negative logic
The data line is negative logic and it has to be inverted. I added this in the same MX_LPUART1_UART_Init()
function.
LL_LPUART_SetTXPinLevel(LPUART1, LL_LPUART_TXPIN_LEVEL_INVERTED);
Break and Wake command
Before sending a command, the protocol expects a break
(data line held HIGH for 12 ms) and mark
(data line held LOW for 9 ms). This is a pretty crucial step and took me some time to figure out. After some debugging and using the Saleae logic analyzer, this was the way to achieve that.
For a short amount of time, the TX pin needs to multiplexed to be a GPIO output after disabling UART. Setting TX pin to HIGH for 12 ms break, followed by setting it to LOW for 9 ms and then multiplexing the TX pin back to GPIO ALTERNATE.
void SondeEXO3sSensor::sdi_break_mark(void) {
flush();
uint32_t timeStart;
PLUART::disable();
//Set TX pin to output
PLUART::configTxPinOutput();
// HIGH at TX pin
PLUART::setTxPinOutputLevel();
// Break - hold HIGH for 12 ms
timeStart = uptimeGetMs();
while(uptimeGetMs() - timeStart < breakTimeMin);
// LOW at TX pin
PLUART::resetTxPinOutputLevel();
// Mark - hold LOW for 9 ms
timeStart = uptimeGetMs();
while(uptimeGetMs() - timeStart < markTimeMin);
// Set TX pin back to TX (alternate) mode
PLUART::configTxPinAlternate();
// Re-enable UART
PLUART::enable();
}
This sdi_break_mark()
is used in the sdi_transmit()
funciton before sending each command.
void SondeEXO3sSensor::sdi_transmit(const char *ptr) {
// TX enable
PLUART::startTransaction();
sdi_break_mark();
PLUART::write((uint8_t *)ptr, strlen(ptr));
// TX disable
PLUART::endTransaction(100);
}
Here, startTransactions()
and endTransactions()
is to enable and disable the OE
line (output enable).
SDI-12 Commands and Responses
-
Inquiry Command - is sent to get the sensor address
sdi_transmit("?!");
-
Identification Command - is sent to get the identification string from the sensor
sdi_transmit("0I!");
-
Measure Command - is sent to ask the sensor to start measurements.
sdi_transmit("0M!");
Sending this command, we immediately receive the a response 00629\r\n
. This means it would take 62 seconds to send 9+1 values. After 62 seconds have elapsed, the sensor send a 0 to convey the measurement is done and new data can be inquired.
-
Data Command - is sent to retrieve the new data.
sdi_transmit("0D0!");
The maximum values the EXO3s sensor can send in a single frame is 4. Therefore, the next few values can be inquired by using the following commands.
sdi_transmit("0D1!");
sdi_transmit("0D2!");