i_SDI-12_interface.ino example

Example I: SDI-12 PC Interface.

Example I: SDI-12 PC Interface

Arduino-based USB dongle translates serial comm from PC to SDI-12 (electrical and timing)

  1. Allows user to communicate to SDI-12 devices from a serial terminal emulator (e.g. PuTTY).
  2. Able to spy on an SDI-12 bus for troubleshooting comm between data logger and sensors.
  3. Can also be used as a hardware middleman for interfacing software to an SDI-12 sensor. For example, implementing an SDI-12 data logger in Python on a PC. Use verbatim mode with feedback off in this case.

Note: "translation" means timing and electrical interface. It does not ensure SDI-12 compliance of commands sent via it.

D. Wasielewski, 2016 Builds upon work started by: https://github.com/jrzondagh/AgriApps-SDI-12-Arduino-Sensor https://github.com/Jorge-Mendes/Agro-Shield/tree/master/SDI-12ArduinoSensor

Known issues:

  • Backspace adds a "backspace character" into the serialMsgStr (which gets sent out on the SDI-12 interface) instead of removing the previous char from it
  • Suceptible to noise on the SDI-12 data line; consider hardware filtering or software error-checking
1/**
2 * @example{lineno} i_SDI-12_interface.ino
3 * @copyright Stroud Water Research Center
4 * @license This example is published under the BSD-3 license.
5 * @date 2016
6 * @author D. Wasielewski
7 *
8 * @brief Example I: SDI-12 PC Interface
9 *
10 * Arduino-based USB dongle translates serial comm from PC to SDI-12 (electrical and
11 * timing)
12 * 1. Allows user to communicate to SDI-12 devices from a serial terminal emulator
13 * (e.g. PuTTY).
14 * 2. Able to spy on an SDI-12 bus for troubleshooting comm between data logger and
15 * sensors.
16 * 3. Can also be used as a hardware middleman for interfacing software to an SDI-12
17 * sensor. For example, implementing an SDI-12 data logger in Python on a PC. Use
18 * verbatim mode with feedback off in this case.
19 *
20 * Note: "translation" means timing and electrical interface. It does not ensure
21 * SDI-12 compliance of commands sent via it.
22 *
23 * D. Wasielewski, 2016
24 * Builds upon work started by:
25 * https://github.com/jrzondagh/AgriApps-SDI-12-Arduino-Sensor
26 * https://github.com/Jorge-Mendes/Agro-Shield/tree/master/SDI-12ArduinoSensor
27 *
28 * Known issues:
29 * - Backspace adds a "backspace character" into the serialMsgStr (which gets sent
30 * out on the SDI-12 interface) instead of removing the previous char from it
31 * - Suceptible to noise on the SDI-12 data line; consider hardware filtering or
32 * software error-checking
33 */
34
35#define HELPTEXT \
36 "OPTIONS:\r\n" \
37 "help : Print this message\r\n" \
38 "mode s : SDI-12 command mode (uppercase and ! automatically corrected) " \
39 "[default]\r\n" \
40 "mode v : verbatim mode (text will be sent verbatim)\r\n" \
41 "fb on : Enable feedback (characters visible while typing) [default]\r\n" \
42 "fb off : Disable feedback (characters not visible while typing; may be desired " \
43 "for developers)\r\n" \
44 "(else) : send command to SDI-12 bus"
45
46#include <SDI12.h>
47
48#ifndef SDI12_DATA_PIN
49#define SDI12_DATA_PIN 7
50#endif
51#ifndef SDI12_POWER_PIN
52#define SDI12_POWER_PIN 22
53#endif
54
55/* connection information */
56uint32_t serialBaud = 115200; /*!< The baud rate for the output serial port */
57int8_t dataPin = SDI12_DATA_PIN; /*!< The pin of the SDI-12 data bus */
58int8_t powerPin = SDI12_POWER_PIN; /*!< The sensor power pin (or -1) */
59char sensorAddress = '1'; /*!< The address of the SDI-12 sensor */
60
61/** Define the SDI-12 bus */
62SDI12 mySDI12(dataPin);
63
64void setup() {
65 Serial.begin(serialBaud);
66 while (!Serial && millis() < 10000L);
67
68 // Power the sensors;
69 if (powerPin >= 0) {
70 Serial.println("Powering up sensors...");
71 pinMode(powerPin, OUTPUT);
72 digitalWrite(powerPin, HIGH);
73 delay(200);
74 }
75
76 // Initiate serial connection to SDI-12 bus
77 mySDI12.begin();
78 delay(500);
79 mySDI12.forceListen();
80
81 // Print help text (may wish to comment out if used for communicating to software)
82 Serial.println(HELPTEXT);
83}
84
85void loop() {
86 static String serialMsgStr;
87 static boolean serialMsgReady = false;
88
89 static String sdiMsgStr;
90 static boolean sdiMsgReady = false;
91
92 static boolean verbatim = false;
93 static boolean feedback = true;
94
95
96 // -- READ SERIAL (PC COMMS) DATA --
97 // If serial data is available, read in a single byte and add it to
98 // a String on each iteration
99 if (Serial.available()) {
100 char inByte1 = Serial.read();
101 if (feedback) { Serial.print(inByte1); }
102 if (inByte1 == '\r' || inByte1 == '\n') {
103 serialMsgReady = true;
104 } else {
105 serialMsgStr += inByte1;
106 }
107 }
108
109 // -- READ SDI-12 DATA --
110 // If SDI-12 data is available, keep reading until full message consumed
111 // (Normally I would prefer to allow the loop() to keep executing while the string
112 // is being read in--as the serial example above--but SDI-12 depends on very precise
113 // timing, so it is probably best to let it hold up loop() until the string is
114 // complete)
115 int avail = mySDI12.available();
116 if (avail < 0) {
117 mySDI12.clearBuffer();
118 } // Buffer is full; clear
119 else if (avail > 0) {
120 for (int a = 0; a < avail; a++) {
121 char inByte2 = mySDI12.read();
122 Serial.println(inByte2);
123 if (inByte2 == '\n') {
124 sdiMsgReady = true;
125 } else if (inByte2 == '!') {
126 sdiMsgStr += "!";
127 sdiMsgReady = true;
128 } else {
129 sdiMsgStr += String(inByte2);
130 }
131 }
132 }
133
134
135 // Report completed SDI-12 messages back to serial interface
136 if (sdiMsgReady) {
137 Serial.println(sdiMsgStr);
138 // Reset String for next SDI-12 message
139 sdiMsgReady = false;
140 sdiMsgStr = "";
141 }
142
143 // Send completed Serial message as SDI-12 command
144 if (serialMsgReady) {
145 Serial.println();
146 // Check if the serial message is a known command to the SDI-12 interface program
147 String lowerMsgStr = serialMsgStr;
148 lowerMsgStr.toLowerCase();
149 if (lowerMsgStr == "mode v") {
150 verbatim = true;
151 Serial.println("Verbatim mode; exact text will be sent. Enter \"mode s\" for "
152 "SDI-12 command mode.");
153 } else if (lowerMsgStr == "mode s") {
154 verbatim = false;
155 Serial.println("SDI-12 command mode; uppercase and ! suffix optional. Enter "
156 "\"mode v\" for verbatim mode.");
157 } else if (lowerMsgStr == "help") {
158 Serial.println(HELPTEXT);
159 } else if (lowerMsgStr == "fb off") {
160 feedback = false;
161 Serial.println("Feedback off; typed commands will not be visible. Enter \"fb "
162 "on\" to enable feedback.");
163 } else if (lowerMsgStr == "fb on") {
164 feedback = true;
165 Serial.println("Feedback on; typed commands will be visible. Enter \"fb off\" "
166 "to disable feedback.");
167 }
168 // If not a known command to the SDI-12 interface program, send out on SDI-12 data
169 // pin
170 else {
171 if (verbatim) {
172 mySDI12.sendCommand(serialMsgStr);
173 } else {
174 serialMsgStr.toUpperCase();
175 String fullCommand = serialMsgStr + "!";
176 mySDI12.sendCommand(fullCommand);
177 }
178 }
179 // Reset String for next serial message
180 serialMsgReady = false;
181 serialMsgStr = "";
182 }
183}