SDI-12 Soil Moisture Probe Help
-
@jimpower Code and exact sensor product name would be helpful. I found a data sheet telling that the longest response string is 80 bytes. That should not be a problem with the UART. The Rx buffer length is set to 512 bytes.
-
I'm using an Enviropro EP100G-12 which has 12 rows of sensors at 10cm intervals, each row contains a temp, moisture and ec sensor.
http://enviroprosoilprobes.com/Files/EnviroPro_SDI-12_CommandsV2.pdf
http://enviroprosoilprobes.com/Files/EnviroPro_EPCU_User_ManualV3.pdf
import network import time import pycom import machine import gc import socket import utime from machine import RTC from machine import ADC from machine import Pin from machine import UART from network import LoRa execfile('/flash/Network/LoRa.py') f_device = open('/flash/Config/Device_Details.txt', 'r') device_setting = f_device.readline() device_strings = [i.strip() for i in device_setting.split(',')] f_device.close() pycom.heartbeat(False) rtc = RTC() adc = ADC() lora = LoRa() uart = UART(1, baudrate=1200) p_tx = Pin('P3', mode=Pin.OUT) p_dir = Pin('P11', mode=Pin.OUT) def SDI12(cmd): uart.deinit() p_tx.value(0) p_dir.value(1) # set output dir utime.sleep_us(14000) # send BREAK p_tx.value(1) utime.sleep_us(8333) # send MARK uart.init(1200, bits=7, parity=UART.EVEN, stop=1, timeout_chars=75) # init with given parameters uart.write(cmd) # send command utime.sleep_us(8333 * len(cmd)) # wait to send command (byte time * command lenght) p_dir.value(0) # output set to read line = uart.read(75) # read data from UART return line; time.sleep(2) while True: batt = adc.channel(attn=1, pin='P16') #s = socket.socket(socket.AF_LORA, socket.SOCK_RAW) #s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) #s.setblocking(True) #s.send("Battery Status : " + str(batt.value()) + " mV") #s.setblocking(False) print('--------------------------------') print("Battery Status : " + str(batt.value()) + " mV") print('--------------------------------') ########################################################## # Measure Temperature # ########################################################## utime.sleep(2) line = SDI12('?!') #Wake Up Sensor print(line) #Print Sensor Response utime.sleep(3) #Wait before Next Cmd line = SDI12('0C2!') #Probe Temp Measure print(line) #Print Time/Sensors utime.sleep(5) #Wait before Next Cmd data = SDI12('0D0!') #Probe Temp Readings print(data) #Print Temp Readings #utime.sleep(10) #data = SDI12('0D1!') #Probe Temp Readings #print(data) #Print Temp Readings ########################################################## # Garbage Collect # ########################################################## gc.enable() gc.collect() print("Sensor Memory Free : " + str(gc.mem_free())) time.sleep(30)``` -
@jimpower Thanks. The code looks principly OK. What happens if you increase the receive timeout?
The documentation of the product is weird. Why cannot they include a proper protocol definition? Did you see b.t.w. CR/LF transmitted at the end of response messages?
-
yes it says technical document "coming soon"
here is a snippet of my uart.readline()

Do you think this timeout_chars is correct? I wasn't sure what to put in here.
uart.init(1200, bits=7, parity=UART.EVEN, stop=1, timeout_chars=75)``` -
@jimpower SInce the length of the response is not known, you need astrategy to tell when the respons eis complete:
- either the response has a known terminal symbol, like ! in the command, or \r\n, then you can read until you see that symbol, and wait as second condition for a long timeout. Waitung for \r\n is done by uart.readline().
- or you have to wait for a timeout.
The 75 char timeout seems reasonable, but to sort out implementation errors in Micropython, you could increase that for testing and see, if you receive a longer message. Thre reason I ask is, taht I faintly remember some discussion around the rx timeout of uart.read.
-
@robert-hh I'm not sure what happened but I changed my code back to the way it was before playing around with break times, uart timeout etc and it works.
A note for anyone wanting to interface with SDI-12 with a LoPy you need to perform a gc.collect() after every read and then wait for 30secs before you can read from any other sensors.
@robert-hh thanks for your help with this it was pivotal to getting a working SDI-12 interface.
@AntonioValente thanks for your initial concept which lead to this solution. -
@jimpower I know this is an old thread but where are you getting the 1.8V ref from?
-
@Bryan-Hom Yuo can use a small voltage regulator and derive it from 3.3V. If
-
@robert-hh Thank you. 1 other question, am i looking at the diagram correctly that the 1.8V + should also be tied in with the 3.3v line? Wouldn't that mean the voltage going into A is 3.3V?
-
@Bryan-Hom That's looks wrong. You can consider the 1.8V source as a function block with 3.3V/GND as input and 1.8v/GND as output.
I tried also some other circuits for a SDI-12 connection, but never connected them to real sensors. https://forum.pycom.io/topic/4827/teros-12-and-sdi-12?_=1640181173389
-
@robert-hh Thanks, so it should actually look more like this?

-
@Bryan-Hom Yes.
-
This post is deleted! -
Hi Robert, you seem to be the right person to be asking these questions to, but im using @jimpower's code and i followed the drawing exactly, but i cannot seem to get any reply from the sensor. Probing around my setup it looks like i have all the grounds properly connected, 5v going into to A. I made sure to put the 10k resistor between the 3.3v and the pin 4 (rx) of the pycom. Any thoughts or suggestions? I'm trying to read SDI-12 from a Campbell Scientific AVW200 https://s.campbellsci.com/documents/es/manuals/avw200 - 772.pdf Thank you
import network import time import pycom import machine import gc import socket import utime from machine import RTC from machine import ADC from machine import Pin from machine import UART pycom.heartbeat(False) rtc = RTC() adc = ADC() #lora = LoRa() uart = UART(1, baudrate=1200) p_tx = Pin('P3', mode=Pin.OUT) p_dir = Pin('P11', mode=Pin.OUT) def SDI12(cmd): uart.deinit() p_tx.value(0) p_dir.value(1) # set output dir utime.sleep_us(12500) # send BREAK p_tx.value(1) utime.sleep_us(8333) # send MARK uart.init(1200, bits=7, parity=UART.EVEN, stop=1, timeout_chars=75) # init with given parameters uart.write(cmd) # send command utime.sleep_us(8333 * len(cmd)) # wait to send command (byte time * command lenght) p_dir.value(0) # output set to read line = uart.readline() # read data from UART return line; time.sleep(2) while True: ########################################################## # Measure Temperature # ########################################################## utime.sleep(2) command = SDI12('V!') utime.sleep(10) print(command) #Print Sensor Response utime.sleep(2) command2 = SDI12('0V!') utime.sleep(10) print(command2) ########################################################## # Garbage Collect # ########################################################## gc.enable() gc.collect() print("Sensor Memory Free : " + str(gc.mem_free())) time.sleep(15)``` -
@Bryan-Hom Sorry, I misread you diagram. Pin A has to be at a level between 0 and 3.3 V, like 1.8 V in the first diagram. You asked from where to get it, I recommended an 1.8V regulator which in turn gets it power from 5V or 3.3V. For a fist test, you should also be able to use a 1.5V battery.
-
@robert-hh Thank you! i rigged up the battery and got the response. I also just tried used a DAC output on Pin22 set at 55% which is roughly 1.77V and it seems to work as well without any special power supply
dac = machine.DAC('P22') # create a DAC object
dac.write(0.55) # set output to 55% ~1.77V -
@Bryan-Hom @robert-hh Hello, I've tried the program and do the wiring diagram like this

but it still can't seem to get any reply from the sensor.
,here is my program
import utime from machine import Pin, UART, DAC # from machine import UART uart = UART(1, baudrate=1200) p_tx = Pin('P10', mode=Pin.OUT) p_dir = Pin('P11', mode=Pin.OUT) pin12 = Pin('P3', mode=Pin.OUT) dac = DAC('P21') # create a DAC object def SDI12(cmd): uart.deinit() p_tx.value(0) p_dir.value(1) # set output dir utime.sleep_us(12800) # send BREAK p_tx.value(1) utime.sleep_us(8333) # send MARK uart.init(1200, bits=7, parity=UART.EVEN, stop=1, timeout_chars=75) # init with given parameters uart.write(cmd) # send command utime.sleep_us(8333 * len(cmd)) # wait to send command (byte time * command lenght) p_dir.value(0) # output set to read line = uart.readline() # read data from UART return line; while True: print("reading") pin12.value(1) dac.write(0.55) # set output to 55% ~1.77V utime.sleep(2) line = SDI12("?!") utime.sleep(3) print(line) # pin12.value(0) utime.sleep(10)I need your help to fix it, Thank you
-
@Gusti-Made-Arya-Wijaya Compqring with the port of the previous poster, you use different pins for p_tx. In the previous post, p_tx is the same a UART 1 TX, and that is by intention.
-
@robert-hh @Bryan-Hom Hello, I've updated the program like this
import utime from machine import Pin, UART, DAC # from machine import UART # uart = UART(1, baudrate=1200, pins=('P10','P11')) uart = UART(1, baudrate=1200) p_tx = Pin('P3', mode=Pin.OUT) # p_tx = Pin('P10', mode=Pin.OUT) p_dir = Pin('P11', mode=Pin.OUT) # pin12 = Pin('P3', mode=Pin.OUT) dac = DAC('P21') # create a DAC object def SDI12(cmd): uart.deinit() p_tx.value(0) p_dir.value(1) # set output dir # utime.sleep_us(12800) # send BREAK utime.sleep_us(14000) p_tx.value(1) utime.sleep_us(8333) # send MARK uart.init(1200, bits=7, parity=UART.EVEN, stop=1, timeout_chars=75) # init with given parameters uart.write(cmd) # send command utime.sleep_us(8333 * len(cmd)) # wait to send command (byte time * command lenght) # p_dir.value(0) # output set to read line = uart.readline() # read data from UART return line; while True: print("reading") # pin12.value(1) dac.write(0.55) # set output to 55% ~1.77V utime.sleep(2) line = SDI12('?!') utime.sleep(3) print(line) # line = SDI12('0M!') # utime.sleep(3) # print(line) # pin12.value(0) utime.sleep(10)but the response is still "none", and I use rs485 module like this (max485)


-
@Gusti-Made-Arya-Wijaya As far I see it, the chip has both transmit enable DE and receive enable /RE. For Transmit, the DE has to be high, for receive, the /RE has to be low. Unless you have an inverter connected between DE and /RE, you have to control both in software.
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login