SDI-12 library with Arduino MKR 1010

Hi!

I am trying to use the SDI-12 library (GitHub - 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.) with MKR 1010 wifi as a dataloggin system for Atmos 22 anemometer (http://publications.metergroup.com/Integrator%20Guide/18195%20ATMOS%2022%20Integrator%20Guide.pdf).

Everything works fine with Arduino Uno. I'm using continuous measurement command aR4! . The problem with MKR is that it frequently skips every 4. ,5. or 6. measurement.

I noticed that every time the measurement is skipped, there is only 0 or 1 byte in the buffer to read. With oscilloscope I noticed that the signal is then really short compared to normal measurements.

I'm also using a bidirectional level shifter 3.3V <->5.0V made from two resistors and a mosfet. It seems to work fine.

Is there a fundamental issue with MKR:s compatibility with the SDI-12 library, or is there a fix for this problem? I would like to use MKR because I need wifi connection, RTC clock and SD-card compatibility and MKR has first two of them.

EDIT:

Here is the code. It is a bit messy, because I it's a heavily modified version of the libraries examples for test purposes.

At the beginning of the code there are some notes about the Atmos 22 anemometer.


* =================REMARKS ABOUT ATMOS 22 ============
 * - Sensor measurenmet sequence:
 *    -Internal clock from 0-59, increases by 1 every second and resets to 0 after 59
 *        -In poer up timer is set to 55
 *    -Averanging command resets timer to 55
 *    - Orientation is measured every 60s
 *    -MEAUREMENTS:
 *        -Atmos takes wind and air temperature measurements every 10s at internal timer intervals of 0, 10, 20, 30, 40,50. 
 *            -Averanging commands( aM!, aR0!, aR3!,..) compute the averages , accumulations or maximums of these measurements and
 *            reset the internal counters and accumulators
 *        
 * - aR4! mode:
 *    -Instantaneous measurements
 *    - Command triggers a sensor measurenment and returns the data automatically
 *    
 *    -response: 
 *               a<TAB><NorthWindSpeed> <EastWindSpeed> <gustWindSpeed> <airTemperature> <xOrientation>
                 <yOrientation> <nullValue><CR><sensortype><Checksum><CRC>
                 
                 -NOTE THAT VALUES ARE SPACE DELIMITED!!!
                 
      - Must be used at intervals of 10s or greater!! (as aR3!)
      - Power Up time for continious mode is 10s, after that communication is possible
      
   - 3.6 V <Supply voltage< 15.0 V
   - digital logig level must  be greater than 3.9 V. Note that many Arduinoboards use 3.3 V logil leves,
      so level shifter is needed 
   - ERROR codes
        -9999 general error
        -9990 meaurement error
                
 */

// CODE:
 

#include <SDI12.h>

#define SERIAL_BAUD 115200 /*!< The baud rate for the output serial port */
#define DATA_PIN 6         /*!< The pin of the SDI-12 data bus */
#define POWER_PIN -1       /*!< The sensor power pin (or -1 if not switching power) */

// === SENSOR ADDRESS ===
const uint8_t ADDRESS = 1;

/** Define the SDI-12 bus */
SDI12 mySDI12(DATA_PIN);

uint8_t numSensors = 0;

String sdiResponse = "";   


/**
 * @brief converts allowable address characters ('0'-'9', 'a'-'z', 'A'-'Z') to a
 * decimal number between 0 and 61 (inclusive) to cover the 62 possible
 * addresses.
 */
byte charToDec(char i) {
  if ((i >= '0') && (i <= '9')) return i - '0';
  if ((i >= 'a') && (i <= 'z')) return i - 'a' + 10;
  if ((i >= 'A') && (i <= 'Z'))
    return i - 'A' + 36;
  else
    return i;
}

/**
 * @brief maps a decimal number between 0 and 61 (inclusive) to allowable
 * address characters '0'-'9', 'a'-'z', 'A'-'Z',
 *
 * THIS METHOD IS UNUSED IN THIS EXAMPLE, BUT IT MAY BE HELPFUL.
 */
char decToChar(byte i) {
  if (i < 10) return i + '0';
  if ((i >= 10) && (i < 36)) return i + 'a' - 10;
  if ((i >= 36) && (i <= 62))
    return i + 'A' - 36;
  else
    return i;
}

/**
 * @brief gets identification information from a sensor, and prints it to the serial
 * port
 *
 * @param i a character between '0'-'9', 'a'-'z', or 'A'-'Z'.
 */
void printInfo(char i) {
  String command = "";
  command += (char)i;
  command += "I!";
  mySDI12.sendCommand(command);
  delay(100);

  String sdiResponse = mySDI12.readStringUntil('\n');
  sdiResponse.trim();
  // allccccccccmmmmmmvvvxxx...xx<CR><LF>
  Serial.print(sdiResponse.substring(0, 1));  // address
  Serial.print(", ");
  Serial.print(sdiResponse.substring(1, 3).toFloat() / 10);  // SDI-12 version number
  Serial.print(", ");
  Serial.print(sdiResponse.substring(3, 11));  // vendor id
  Serial.print(", ");
  Serial.print(sdiResponse.substring(11, 17));  // sensor model
  Serial.print(", ");
  Serial.print(sdiResponse.substring(17, 20));  // sensor version
  Serial.print(", ");
  Serial.print(sdiResponse.substring(20));  // sensor id
  Serial.print(", ");
}

// ============ MEASUREMENT METHOD ==================


void getContinuousResults(char i) {
  // uint8_t resultsReceived = 0;
  uint8_t cmd_number      = 4; // aR4! command
  
    String command = "";
    // aR4! command
    command = "";
    command += i;
    command += "R";
    command += cmd_number;
    command += "!";  // SDI-12 command to get data [address][D][dataOption][!]
    mySDI12.sendCommand(command);

    uint32_t start = millis();
    
    while (mySDI12.available() < 3 && (millis() - start) < 1000) {}

    // Checking the bytes in the buffer
    Serial.print("Test " + String(mySDI12.available()) + ' ');
    
     while (mySDI12.available()) {  // build string from response
    char c = mySDI12.read();
      sdiResponse += c;
      delay(10);  // 1 character ~ 7.5ms
      }

  Serial.println(sdiResponse);  // write the response to the screen
  sdiResponse = "";  // clear the response string 
  mySDI12.clearBuffer();
  
}


// this checks for activity at a particular address
// expects a char, '0'-'9', 'a'-'z', or 'A'-'Z'
boolean checkActive(char i) {
  String myCommand = "";
  myCommand        = "";
  myCommand += (char)i;  // sends basic 'acknowledge' command [address][!]
  myCommand += "!";

  for (int j = 0; j < 3; j++) {  // goes through three rapid contact attempts
    mySDI12.sendCommand(myCommand);
    delay(100);
    if (mySDI12.available()) {  // If we here anything, assume we have an active sensor
      mySDI12.clearBuffer();
      return true;
    }
  }
  mySDI12.clearBuffer();
  return false;
}

// =======================  SETUP  ===========================


void setup() {
  Serial.begin(SERIAL_BAUD);
  while (!Serial);

  Serial.println("Opening SDI-12 bus...");
  mySDI12.begin();
  delay(10000);  // allow things to settle for 10s,POWER up time for c-mode

  mySDI12.setTimeoutValue(-8888); // chagne timeout, -9999 is a general error code in Atmos22
  
  Serial.println("Timeout value: ");
  Serial.println(mySDI12.TIMEOUT);
  
  // Check that the Atmos 22 is active and the address is correct
  char addr = decToChar(1);
    if (checkActive(addr)) {
      numSensors++;
      //isActive[i] = 1;
      printInfo(addr);
      Serial.println();
    }

  if (numSensors == 0) {
    Serial.println(
      "No sensors found, please check connections and restart the Arduino.");
    while (true) { delay(10); }  // do nothing forever
  }

  Serial.println();
  Serial.println(
    "Time Elapsed (s), NWS, EWS, GWS, T, xOr, yOr, nullValue,... ");
  Serial.println(
    "-------------------------------------------------------------------------------");
}



//  ==================== LOOP ========================
void loop() {
      
      char addr = decToChar(ADDRESS);   // Addres from int to char
      Serial.print(millis());           // Print elapsed time
      Serial.print(", ");               
      getContinuousResults(addr);       // Get continious resoults from sensor

      delay(10000);                     // wait 10s between measurement attempts.
      
}`

Depending on what you really saw on the scope this might mean that the sensor doesn't work correctly. If the sensor isn't returning a complete signal you cannot get that measurement.

I doubt that, the probability that the problem is in your code (given you don't have a hardware problem) is much higher. But as you didn't post your code we cannot check for errors.

I posted the code in the original post.

The interesting thing is that the anemometer works perfectly with Arduino Uno and with the SDI-12 library. I'm able to get both average and continuous measurements with Uno, but MKR does not work properly with same codes. MKR skips frequently some measurements with continuous measurement command (R4!) and I'm not able to receive any average measurement data (M! commands) that I can get with Uno.

Do you have screenshots of the scope readings? It would be interesting to see what happens if it doesn't work. That may show which part of the communication is a problem and if the hardware might be to blame.

I think that the problem is the mosfet based level shifter. I used esp8266 and the same problem occurred.

What would be a good level shifter for SDI-12 protocol?

I would use a MOSFET based one. Post schematics of your's!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.