JolonB / SDI12-UART

Microcontroller independent implementation of SDI-12 over UART
MIT License
19 stars 5 forks source link

KiCad schematics #2

Open binomaiheu opened 2 months ago

binomaiheu commented 2 months ago

Dear Jolon,

Many thanks for this repository and for your elaborate bachelor thesis on developing an SDI-12 enabled IoT system. I'm currently on a similar quest actually ;). I am aware of the Arduino SDI12 library, which I'm currently testing, but I'm a little worried that it will interfere with the interrupts I'm using in my logger firmware, so i wanted to explore the hardware option for interfacing the UART with the SDI-12 bus as well. I did find the schematic on https://www.daycounter.com/Circuits/SDI-12/SDI-12-Interface.phtml which is also mentioned in your thesis, so i ordered some parts to test that, but I really like the way you are using the SN74LVC2G241 dual buffer to switch between TX & RX and wasn't aware of the need for the additional NAND gate on the TX line.

My electronics/KiCad skills are not the best, so I was wondering whether you would consider sharing the KiCad files for your SDI-12 interface ? I noticed the files under hardware/config_1 are fairly empty if I'm not mistaken. In any case a big thanks for your work !

Regards, Bino (from Belgium)

JolonB commented 2 months ago

Hi Bino,

Thanks for getting in touch. It's great to hear that you had a look at my thesis and are interested in working with SDI-12. I'm more than happy to help!

I did probably get a bit ambitious with this repository and ended up not actually doing much work outside of the README, meaning config_1 (and all the other non-existent configurations) are pretty empty... whoops :/ This will just have to be motivation to update this repo.


Anyway, you mentioned the additional NAND gate on the Tx line. Are you referring to the one I've circled here?

image

This was added to implement the break condition (12ms high voltage (logic 0)) in the SDI-12 specification. Some UART buses might be able to do this, however, we were working with an ESP32 running MicroPython, which seemed to prevent us from doing this. This NAND gate exists as a workaround for the inability to implement a break. The FOut pin allows us to send the break by setting that pin for 12ms instead.

If you are using a microcontroller that can send a 12ms break on the Tx pin, you won't need to include this NAND gate; you'll be able to simplify it down to use just 3 pins (Tx, Rx, and direction).
If you don't know if your microcontroller's UART bus can send a 12ms break, it may even be possible to manually set the output register for the Tx pin to 5V for 12ms.

I believe the design at https://www.daycounter.com/Circuits/SDI-12/SDI-12-Interface.phtml should work. The only issue is that all of your Tx data will also be received by Rx, so you need to filter that out. That's why I used the dual buffer


I'll try to update some schematics tomorrow and I'll let you know when I've done it. If you want to, you could let me know what microcontroller you are using and I can give you some advice on how I would design the SDI-12 circuit or—if I have the time—I could upload a new design to this repo that will work for your microcontroller :) The designs I show in my thesis and on this repo have been specifically made for ESP32 devices. I want to make designs for other devices, but haven't had the motivation to do so (until now!)

JolonB commented 2 months ago

Hey @binomaiheu

Since you mentioned Arduino, I've made a design that should work for an Arduino with 5V GPIO. This probably won't work with 3.3V GPIO, at least not reliably. You can find it here. Bear in mind, I haven't tested this

This design uses two components:

There is also a large pull-down resistor. I'm not sure if it is needed. I never used one there; maybe some sensors have one built in?? I'll leave that up to you to test because I no longer have any SDI-12 sensors.

output.pdf

As you can see, this design does not need the NAND gate with the FOut input. As I mentioned in my previous comment, this is only needed if your UART bus cannot send the break signal. There are two ways this can be achieved: hardware support for break signals, or the ability to close and reopen a UART bus. It seems that Arduinos support the latter.

I'm basing the following code on this code from the RS485 library. You should be able to implement a break like so:

void sendSDI12Break() {
  Serial.flush();
  Serial.end();
  pinMode(txPin, OUTPUT);
  digitalWrite(txPin, LOW);  // remember, this will be inverted
  delay(12);  // minimum 12, could do slightly longer
  Serial.begin(1200, 7E1);  // 1200 baud; 1 start, 7 data, 1 even parity, and 1 stop bits
}

You'd also need to manage switching the direction yourself. I believe my code spent most of the time in the receive state and only switched to the transmit state when it was needed. (This is why I think the pull-down resistor (R1) could be needed. It just means Rx has a defined input)

binomaiheu commented 2 months ago

Hi @JolonB !

Thanks a lot for taking the time for such an the elaborate response! We had the first proper spring weekend here in Belgium so haven't had the change to test much further, but as usual nice weather doesn't last very long here, so I'll get back to it tomorrow :). Normally the components i ordered to try the schematic on that daycounter website should arrive then as well.

I'm currently using both the Atmel1284p (as found on the Sodaq Mbili boards) and the SAMD21G (as on the Arduino MKR boards). Both using 3.3V level logic, so the original schematics should be ok. The final logger will be using a SAMD.

Good tip about the UART being able to send the break signal itself, I wouldn't know by heart, but I'll try that tomorrow, that would indeed save one GPIO line, thanks ! And thanks for uploading the schematic, great to get me going (have only done small things in kicad, but pretty sure i'll get the hang of it).

I'll keep you updated on my progress !

Cheers, bino

JolonB commented 2 months ago

Hey @binomaiheu

There is something to be aware of when using the original designs with a 3.3V microcontroller. When I did the environmental monitoring project, I used NAND gates with TTL logic levels, which meant that 3.3V signals would be detected as a HIGH signal.

image

If you use a CMOS chip instead of TTL, the 3.3V signals will be undefined, so you have to be careful not to do that.

However, instead of using a TTL logic gate, I recommend using the new circuit I made but with a logic level shifter connected to the microcontroller to convert the 3.3V signal to 5V and vice versa. With the logic level shifter, it doesn't matter if you use a TTL or CMOS chip—they'll both work.

Keep me updated. I'm keen to hear how it goes.

Jolon

binomaiheu commented 2 months ago

Hi Jolon,

I did some more testing with sending a break via the UART line. On the Arduino MKR (SAMD21 MCU), it didn't seem to work with the standard UART pins for Serial1 (13 & 14). For some reason the bus stopped working and the entire device froze after a few iterations (i setup a small firmware to print a character every 2 seconds, alternately preceeded by the 12 ms break and not. So I dug a little deeper and created an own hardware UART following https://docs.arduino.cc/tutorials/communication/SamdSercom/. That seemed to work, also defining the correct number of bits (7) and parity etc..

Source is below :

#include <Arduino.h>
#include <wiring_private.h>

int32_t n = 0;

Uart mySerial(&sercom3, 1, 0, SERCOM_RX_PAD_1, UART_TX_PAD_0); // Create the new UART instance assigning it to pin 1 and 0

// Attach the interrupt handler to the SERCOM
void SERCOM3_Handler()
{
  mySerial.IrqHandler();
}

void setup()
{
  Serial.begin(9600);
//  Serial1.begin(1200,  SERIAL_7E1);

  mySerial.begin(1200, SERIAL_7E1);

  pinPeripheral(1, PIO_SERCOM); //Assign RX function to pin 1
  pinPeripheral(0, PIO_SERCOM); //Assign TX function to pin 0
}

void sendSDI1Break()
{
  mySerial.flush();
  mySerial.end();

  pinPeripheral(0, PIO_OUTPUT);
  pinPeripheral(1, PIO_OUTPUT);

  digitalWrite(0, LOW);  // remember, this will be inverted
  delay(12);  // minimum 12, could do slightly longer

  pinPeripheral(1, PIO_SERCOM); //Assign RX function to pin 1
  pinPeripheral(0, PIO_SERCOM); //Assign TX function to pin 0

  mySerial.begin(1200, SERIAL_7E1);  // 1200 baud; 1 start, 7 data, 1 even parity, and 1 stop bits
}

void loop()
{

  Serial.println("--");
  if ( n % 2 ) sendSDI1Break();

  mySerial.print("g");

  n++;
  delay(2000);
}

So not entirely trivial, but at least this seems to work in adding the 12 ms break in software. However, on the scope I did notice that when I re-enable the UART in the sendSDI12break() routine, the TX line briefly goes back to it's (default) HIGH state for about 52 µs, which I suppose makes sense from a UART initialisation point of view, but I'm a little worried that it will mess up the SDI-12 communication. See traces below :

TEK00008

and with cursor :

TEK00009

So perhaps having the NAND gate around isn't such a bad idea for safety anyway.

My test-parts have arrived, but I was stupid enough to order them in packages which are completely beyond my soldering capabilities. So I'll have to revisit my component search. I'm thinking of just trying to layout the schematic on a pcb first (with the optional NAND gate via a jumper or so).

Regarding the logic level shifter, thanks for the tip. For the software implementation using the official arduino SDI-12 library, I was actually trying to use the TXB0108 (https://www.ti.com/product/TXB0108), but it did not seem to work, I assume for the reasons mentioned on https://e2e.ti.com/support/logic-group/logic/f/logic-forum/887817/txb0104-trying-to-adapt-voltage-levels-for-an-sdi-12-bus-with-a-3-3v-mcu

Cheers, Bino

binomaiheu commented 2 months ago

Hi Jolon,

I spent an afternoon & evening working my way through KicCad and component footprints. This is what I have made so far. Both the Electrical Rules and Design checkers are happy with the design. I'll re-check the schematic tomorrow thoroughly, as this was a first attempt of me getting to know KiCad a bit better.

Screenshot from 2024-04-10 21-00-34

and the PCB:

sdi12-breakout

binomaiheu commented 2 months ago

Few minor updates:

Cheers, b.

JolonB commented 2 months ago

Hi @binomaiheu

I won't say whether I think that sending the break with the UART pins or with the NAND gate is the better option, because I don't want to give you incorrect advice on something I haven't tested. The downsides of the UART solution is there could be issues that aren't obvious now (for example, the interrupt might affect something else). The downsides of the NAND gate is that there could be higher current draw, and there is the cost of an extra component.


I was stupid enough to order them in packages which are completely beyond my soldering capabilities

I did the same thing when working on SDI-12 4 years ago. I think it happens to everyone at least once 😅️

binomaiheu commented 2 months ago

Hi Jolon,

To keep you updated, I checked the schematics & PCB with a colleague who has more experience with electronics than I do and integrated the 12V/5V and 3.3V onto the PCB as well along with the stuff we discussed above. I believe indeed you are right concerning the 8.33 ms marking, so it should be possible to use the UART and leave out the "FOut" via the NAND gate. But I included both options for testing.

I just ordered the PCB ... with the correct footprints ! (I hope), so I'm looking forward to the result. Will get back here when I receive my toys & have tested them !

Cheers, b.