SDI-12 on WisBlock

I want to use the WisBlock to read out an SDI-12 sensor. Does anyone know if it is possible using the RS485 interface (Transmit data efficiently with the LoRa®-powered RAK5802 module – RAKwireless Store)? I saw somewhere on the internet that someone used only the one connection from the RS485 differential pair, and connected that to the SDI-12 sensor. The other connection from the differential pair is then either left floating, grounded, or pulled to Vcc.

Or does RAK maybe plan to make an SDI-12 interface card for the WisBlock?

Hello JP,
I doubt that it will work with the RAK5802 module, but could be worth a try.

We are collecting customer feedback about interfacs/sensors that they would like to see supported by WisBlock and I will add your request to our list. But I cannot promise you anything.

I tried to speak SDI-12 using the RS485 connector board (RAK5802) today. So far I’m not successful. I’m not sure if I should use A or B, and if it’s fine to pull the other terminal to 3.3V or to GND. And I can’t find the Arduino forum post that described the setup.

Next step would be to try Arduino-SDI-12 but I’m not sure if the WisBlock has a free GPIO I can use for this. And then I’m also not sure if the library needs interrupts or not.

I’ll update here if I find anything. If you have any ideas, please let me know.

Following your need of an SDI interface module I put it on the list of new WisBlock modules for this year. But I cannot promise any availability date. We are planning at least 3 events this year where we will present new WisBlock modules.

At this point I need to find a way to test the viability of using the WisBlock to read out SDI-12 soil moisture probes. It needs to happen asap, as it influences business decisions.

We can’t wait for RAK to release a new comms module every time we want to do something new/custom. In this case the best is to find a way to directly interface with the WisBlock without a connectivity module. I’ll search for the docs and see if there is a GPIO which can be used.

I understand your problem.

If I might recommend something, look at

  • the RAK1920, which has several GPIO’s, SPI and I2C exposed on the Click! connector.
    image
  • or the 5804 which has all available GPIO’s, I2C, Serial and SPI exposed on a small connector. It comes with a matching connector and cable to connect external devices.
    image
1 Like

Yes, that is a good idea. What do you think about the J10 and J11 headers on the base board? I see J11 has a free GPIO on pin 2. That might work. I’m still looking through the documentation of the WisBlock to figure out exactly how to use these pin from Arduino.

IO1 on J11 would be available to use.

We know about the confusing different naming of GPIO’s on the RAK5005-O and the RAK4631.
In the next release of the Arduino BSP we will have all RAK5005-O GPIO’s predefined and linked to the correct GPIO numbers of the RAK4631.

For now you can use this list as a start:

	/*
 * WisBlock Base GPIO definitions
 */
	static const uint8_t WB_IO1 = 17;	   // SLOT_A SLOT_B
	static const uint8_t WB_IO2 = 34;	   // SLOT_A SLOT_B
	static const uint8_t WB_IO3 = 21;	   // SLOT_C
	static const uint8_t WB_IO4 = 4;	   // SLOT_C
	static const uint8_t WB_IO5 = 9;	   // SLOT_D
	static const uint8_t WB_IO6 = 10;	   // SLOT_D
	static const uint8_t WB_SW1 = 33;	   // IO_SLOT
	static const uint8_t WB_A0 = 5;		   // IO_SLOT
	static const uint8_t WB_A1 = 31;	   // IO_SLOT
	static const uint8_t WB_I2C1_SDA = 13; // SENSOR_SLOT IO_SLOT
	static const uint8_t WB_I2C1_SCL = 14; // SENSOR_SLOT IO_SLOT
	static const uint8_t WB_I2C2_SDA = 24; // IO_SLOT
	static const uint8_t WB_I2C2_SCL = 25; // IO_SLOT
	static const uint8_t WB_SPI_CS = 26;   // IO_SLOT
	static const uint8_t WB_SPI_CLK = 3;   // IO_SLOT
	static const uint8_t WB_SPI_MISO = 29; // IO_SLOT
	static const uint8_t WB_SPI_MOSI = 30; // IO_SLOT

WB_IOx refers to the IO name used on the RAK5005-O and on the modules.

I was able to get SDI-12 working on the Wisblock using SoftwareSerial. The ArduinoSDI12 library didn’t work, even after I hacked it to use the micros() timer for timing.

SoftwareSerial does not support any other serial mode than SERIAL_8N1. But SDI-12 uses SERIAL_7E1. I therefore manually add a parity bit to every byte before I transmit, and I ignore the parity bit on rx.

Example code below:

#include <SoftwareSerial.h>

SoftwareSerial sdi12rx(17, -1, true);
SoftwareSerial sdi12tx(-1, 17, true);

uint8_t command[] = "?I!";

uint8_t rxBuffer[64];

void setup() {
  Serial.begin(57600);
  while(!Serial);
  Serial.println("Startup");

  addParityBits(command, sizeof(command)-1);
}

void loop() {
  
  Serial.println("loop");

  Serial.print("Request: ");
  for(int i=0; i<sizeof(command); i++) {
    if(command[i] < 0x10) Serial.print("0");
    Serial.print(command[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
  
  pinMode(17, OUTPUT);
  digitalWrite(17, HIGH);
  delay(12);
  digitalWrite(17, LOW);
  delay(9);
  
  sdi12tx.begin(1200);
  sdi12tx.write(command, sizeof(command)-1);

  delay(6);
  sdi12tx.end();
  pinMode(17, INPUT_PULLDOWN);
  
  sdi12rx.begin(1200);
  sdi12rx.setTimeout(2000);

  Serial.print("Response: ");
  unsigned long startTime = millis();
  int bytesRead = 0;
  while(millis()-startTime < 2000) {
    if(sdi12rx.available()) {
      uint8_t response = sdi12rx.read() & 0b01111111; // ignore parity bit
      rxBuffer[bytesRead++] = response;
      if(response == 0x0A) {
        break;
      }
    }
  }

  delay(6);
  sdi12rx.end();
  while(sdi12rx.available()) sdi12rx.read();
  
  pinMode(17, INPUT_PULLDOWN);
  Serial.println("RX closed");

  Serial.write(rxBuffer, bytesRead);

  delay(2000);
}

void addParityBits(uint8_t* dataBuffer, size_t bufferLength) {
  for(int i=0; i<bufferLength; i++) {
    uint8_t parity = calcParityBit(dataBuffer[i]);
    if(parity) {
      dataBuffer[i] = dataBuffer[i] | 0b10000000;
    } else {
      dataBuffer[i] = dataBuffer[i] & 0b01111111;
    }
  }
}

char calcParityBit (unsigned char v)
{
    return (0x6996u >> ((v ^ (v >> 4)) & 0xf)) & 1;
}

@jpmeijers
Congratulations.
If we ever make a SDI-12 module, might we use your patched SoftwareSerial library?

I’m using the standard SoftwareSerial library without any changed. All the required changes to do SERIAL_7E1 are done in the sketch posted above.

If you ever make an SDI-12 module, I would suggest you stick to HardwareSerial where one has native Serial Modes.

I know this is pretty old, but I would like to add a vote to having an SDI-12 module - would open up and simplify a lot of possibilities with sensors

Under development. Will come this year.

1 Like

Figured it may be appropriate to post this here.
I recently developed libsdi12, a SDI-12 protocol engine in C.
It uses tiny send/recv/set_direction/delay callbacks so you can plug it into WisBlock IO1 or a UART with a ~20–50 line adapter. Repo: GitHub - phillipweinstock/libsdi12: The most complete, portable SDI-12 v1.4 protocol library. Pure C, sensor + master, 98 tests, zero dependencies. · GitHub
I’d love some feedback.
As far as I am aware it is the only open source implementation covering the full SDI-12 standard.

Welcome to the forum @Phillip
Thank you for sharing. Did you test your library with our RAK13010 module?
The RAK13010 is using two GPIO’s for RX/TX.

Thank you for the welcome @beegee
I haven’t tested it on the RAL13010 hardware yet, but it should integrate quite cleanly because the library was designed around making small hardware adapters.
It is architecturally very different from other libraries like Arduino-SDI-12
libsdi12 does not implement any GPIO or UART logic itself, rather it exposes a few callbacks.

  • send() transmit bytes
  • recv() recieve bytes
  • set_direction() which switches the bus between TX and RX
  • delay_us() timing

Because of the callback design HAL is 20-50 LOC
RAK13010 uses three GPIO(TX/RX), direction control.
This maps very well to the library.
The actual SDI-12 protocol is handled inside of the library, i.e command parsing (M, C, D,I), timing rules, CRC commands, sensor addressing, measurement state and behavior.

So the wisblock sketch ends up only implementing the hardware adapter, registering the sensor parameters and providing a callback that returns measurement values.

One reason I wrote it this way is SDI-12 implementations are usually coupled to a specific MCU or UART configuration like Arduino-SDI-12 using software serial, which makes certain use cases hard if outright impossible i.e interrupts .
This version is pure c with zero dependencies, so it will work on anything from a small ATmega328 to a SBC running embedded Linux independent of actual UART implementation, to the best of my knowledge it is the only library to practically implement the entire SDI-12 standard everything else only complies to a subset of the standard.
I am also in the middle of developing a SDI-12 Verifier tool, which uses the lib; So far I have only had the chance to test it via loop-back. GitHub - phillipweinstock/libsdi12-verifier: Open-source SDI-12 v1.4 compliance tester with 21 automated sensor tests, microsecond timing measurement, and text/JSON reporting. · GitHub I need to refactor the libsdi12 build system in order to make shared libraries, so software relying on it is easier to package.

If there is some interest, I am more than happy to add a Wisblock/RAK13010 example. If I had access to a Wisblock & RAK13010 I’d test it directly on the module & upstream an example into WisBlock/examples/common/IO/RAK13010_SDI_12_BUS at master · RAKWireless/WisBlock · GitHub