EnviroDIY / Arduino-SDI-12

An Arduino library for SDI-12 communication with a wide variety of environmental sensors. This library provides a general software solution, without requiring any additional hardware.
https://github.com/EnviroDIY/Arduino-SDI-12/wiki
BSD 3-Clause "New" or "Revised" License
158 stars 100 forks source link

Adding a Direction Control Pin #87

Open rocketscream opened 2 years ago

rocketscream commented 2 years ago

It would be great if the SDI-12 object constructor can take in an optional pin argument to control the direction of the flow of data. This is useful if a 3.3V or lower voltage controller is used with the SDI-12 bus. This pin is used to put up conversion voltage translation IC like a SN74AHCT1G125 output pin into high impedance mode when the controller is not in transmitting mode but in receiving mode (use an IC like SN74LVC1G17 which is running at 3.3V but is a Schmitt-trigger and over voltage tolerant input to 5V but doesn't require any direction control). I believe this can be added in the SDI-12 mode control function using a compile time #ifdef?

neilh10 commented 2 years ago

Hi Rocketscream, great products you have. I've been thinking about the level shifting issue - and making SDI-12 standards complaint 5V on the output - and I wonder if you could sketchout what you think the hardware interface could look like. It seems to me it would need to go full duplex - which is also a data pin + direction pin. I have got part way there with a level shifter, and played around with it, some description here .. though its a software issue holding it up. https://github.com/neilh10/ModularSensors/issues/12#issuecomment-1094509928

rocketscream commented 2 years ago

I'm planning to tie the output of the SN74AHCT1G125 to the input of SN74LVC1G17. The OE pin on the SN74AHCT1G125 must be driven low during transmission to enable it and driven high during receiving to put it's output in high impedance mode. The output of SN74LVC1G17 is then tied with the input pin of SN74AHCT1G125. SN74AHCT1G125 runs on 5V VCC and SN74LVC1G17 runs on 3.3V VCC. SN74LVC1G17 can accept higher voltage on it's input pin than it's VCC voltage. That's all, nothing fancy. The only drawback is, both has a maximum of 10 uA of quiescent current. Will go through the entire TI catalogue to see whether there's lower current IC. Still waiting for a real SDI-12 sensor, until then I won't know it will work or not.

neilh10 commented 2 years ago

@rocketscream so if I have it right, and just add an R to prevent current glitches ~ its simple way of ensuring that the Tx is shifted to standard SDI-12 +5V - and I've added in the recommended voltage spike filtering (I reused a circuit from another cct to sketch this out) -and it buffers the processor port, for a cost of a directional line. image

I've purchased a https://vegetronix.com/Products/SDI-TRANS-SENSOR24/ to be able to do some testing.

SRGDamia1 commented 2 years ago

I don't think the SDI-12 protocol guidelines have anything about "best practices" for level shifting and direction control. I think, code wise, it should be pretty easy to add the pin, though I'm not able to do it now. I've had no success with enable pins and level shifting in RS485.

neilh10 commented 2 years ago

Hi @SRGDamia1 I see this discussion as about how to meet the SDI-12 +5V specification, which is hardware, but the suggestion here is it needs the half-duplex control pin in software to support the hardware when switching direction.

I agree I've also had a lot of issues with accurately doing direction switching with half-duplex RS485, and now using RS485 chips that do it automatically. The UARTS with SAMD51 (but not SAMD21) as well as Siemens/Freescale (Teensy3x) chips have the UART hardware options for direction control.

I wanted to see if there was a viable hardware circuit that could do the direction switching, and I think in principal the combination of the SN74AHCT1G125 / SN74LVC1G17 does it, and particularly the Schmitt trigger on the input would be valuable.

@rocketscream I wonder if you think this circuit with T1 would work as well for what you are thinking of ~ it doesn't require the half-duplex control, but doesn't clean up the incoming rx signal with the Schmitt. 87367152-12564580-c52f-11ea-935c-98d5f350ee36

For a long term perspective, the current module SDI12 line timing is quite fragile, and because of that difficult to debug. I've been thinking that the line timing could be implemented with DMA in SAMDx1 style Cortex-M series processors, and that would be quite a rework of the hardware interface of the software. So just a heads up. If you implemented SN74AHCT1G125 / SN74LVC1G17 then a directional control is still needed, and the processor would still need a hard deterministic response to the end of Tx, but its only at 1200baud.

background: The SDI12.org identifies the specifications https://sdi-12.org/current_specification/SDI-12_version-1_4-Jan-30-2021.pdf . The purpose of specifications is that if all players design to the specifications, understand the same language then any "SDI-12 data recorders" should be able to interface to any "SDI-12 sensors"

The way I see it is that the current Mayfly "SDI12 data recorder " doesn't implement the hardware specification of the Data line in 0-5V.
What has been called "level shifting" for the Mayfly mega1284 port.

image

I see this discussion about how to meet that hardware specification. Some sensor can specifically say they work with 3.3V, or have been shown to like 3.3V - which is great, but they are attempting a market capture. I have a group of returned from the field Insitu LT500's that I'm storing, and some of them don't respond on Mayfly SDI-12. Ouch!. I'm switching, at least in the short term to LT500/RS485 which as it turns out is so much more reliable. I have purchased a https://vegetronix.com/Products/SDI-TRANS-SENSOR24/ to be able to set up a reliable SDI-12 testing, which has been calibrated against the reference SDI-12 standard, and its not working with the Mayfly. It also implements the CRC, but it doesn't respond to commands. I have an older (2014) version of the vegetronix SDI-12 that does work, but doesn't support the CRC.

Appendix A shows a recommended interface that can also cope with external voltage transient protections. Some of it is low cost, and some like the 1KV is a lot higher cost, but its inline with layers of protections image

rocketscream commented 2 years ago

@SRGDamia1 let me get the hardware up and I will make the necessary code changes. If it works, I will do a pull request. Is that okay for you?

SRGDamia1 commented 2 years ago

@rocketscream - a PR would be great!

rocketscream commented 1 year ago

@SRGDamia1 I have the hardware ready and currently testing the board. I think the only section of the code I need to change are as follow:

// sets the state of the SDI-12 object.
void SDI12::setState(SDI12_STATES state) {
  switch (state) {
    case SDI12_HOLDING: {

      pinMode(_dataPin, INPUT);     // Turn off the pull-up resistor
      pinMode(_dataPin, OUTPUT);    // Pin mode = output
      digitalWrite(_dataPin, LOW);  // Pin state = low - marking
      digitalWrite(_txEnPin, LOW); // Enable transmit buffer
      setPinInterrupts(false);      // Interrupts disabled on data pin
      break;
    }
    case SDI12_TRANSMITTING: {

      pinMode(_dataPin, INPUT);   // Turn off the pull-up resistor
      pinMode(_dataPin, OUTPUT);  // Pin mode = output
      digitalWrite(_txEnPin, LOW); // Enable transmit buffer
      setPinInterrupts(false);    // Interrupts disabled on data pin
      break;
    }
    case SDI12_LISTENING: {
      digitalWrite(_txEnPin, HIGH); // Disable transmit buffer
      digitalWrite(_dataPin, LOW);  // Pin state = low (turns off pull-up)
      pinMode(_dataPin, INPUT);     // Pin mode = input, pull-up resistor off
      interrupts();                 // Enable general interrupts
      setPinInterrupts(true);       // Enable Rx interrupts on data pin
      rxState = WAITING_FOR_START_BIT;
      break;
    }
    default:  // SDI12_DISABLED or SDI12_ENABLED
    {
      digitalWrite(_txEnPin, HIGH); // Enable transmit buffer
      digitalWrite(_dataPin, LOW);  // Pin state = low (turns off pull-up)
      pinMode(_dataPin, INPUT);     // Pin mode = input, pull-up resistor off
      setPinInterrupts(false);      // Interrupts disabled on data pin
      break;
    }
  }
}

Is there anywhere else I should change?

rocketscream commented 1 year ago

@SRGDamia1 I have the hardware ready and currently testing the board. I think the only section of the code I need to change are as follow:

// sets the state of the SDI-12 object.
void SDI12::setState(SDI12_STATES state) {
  switch (state) {
    case SDI12_HOLDING: {

      pinMode(_dataPin, INPUT);     // Turn off the pull-up resistor
      pinMode(_dataPin, OUTPUT);    // Pin mode = output
      digitalWrite(_dataPin, LOW);  // Pin state = low - marking
      digitalWrite(_txEnPin, LOW); // Enable transmit buffer
      setPinInterrupts(false);      // Interrupts disabled on data pin
      break;
    }
    case SDI12_TRANSMITTING: {

      pinMode(_dataPin, INPUT);   // Turn off the pull-up resistor
      pinMode(_dataPin, OUTPUT);  // Pin mode = output
      digitalWrite(_txEnPin, LOW); // Enable transmit buffer
      setPinInterrupts(false);    // Interrupts disabled on data pin
      break;
    }
    case SDI12_LISTENING: {
      digitalWrite(_txEnPin, HIGH); // Disable transmit buffer
      digitalWrite(_dataPin, LOW);  // Pin state = low (turns off pull-up)
      pinMode(_dataPin, INPUT);     // Pin mode = input, pull-up resistor off
      interrupts();                 // Enable general interrupts
      setPinInterrupts(true);       // Enable Rx interrupts on data pin
      rxState = WAITING_FOR_START_BIT;
      break;
    }
    default:  // SDI12_DISABLED or SDI12_ENABLED
    {
      digitalWrite(_txEnPin, HIGH); // Enable transmit buffer
      digitalWrite(_dataPin, LOW);  // Pin state = low (turns off pull-up)
      pinMode(_dataPin, INPUT);     // Pin mode = input, pull-up resistor off
      setPinInterrupts(false);      // Interrupts disabled on data pin
      break;
    }
  }
}

Is there anywhere else I should change?

It works as expected but I'm going to add the necessary code and maybe a polarity setting for the transmit enable pin. The above is just the testing quick code. I will add a new class contructor with the extra transmit enable pin and polarity of it as the argument. Is that okay for you?

aparr001 commented 1 year ago

Did this ever get integrated? I so far have found no clean way to level shift from 5V to 3.3V without some sort of direction control pin.

nottorp commented 4 months ago

I'd like to ask the same thing, I'm keeping an archive of 1.3.6 because the contraption I'm working with also needs the direction pin.