3ピンのDYP-ME007TXをArduino UNOで使う

以前購入したこのセンサ、OUTってかいてるからアナログの値出力してると思ったら、
どうやらシリアルで動いていた様子…ハマった。


下記のコードで動作確認。
OUTピンをD6にさせばいいみたい。

#include <SoftwareSerial.h>

// TX_PIN is not used by the sensor, since that the it only transmits!
#define PING_RX_PIN 6
#define PING_TX_PIN 7

SoftwareSerial mySerial(PING_RX_PIN, PING_TX_PIN);

long inches = 0, mili = 0;
byte mybuffer[4] = {0};
byte bitpos = 0;

void setup() {
  Serial.begin(9600);
  mySerial.begin(9600);
}


void loop() {
  bitpos = 0;
  while (mySerial.available()) {
    // the first byte is ALWAYS 0xFF and I'm not using the checksum (last byte)
    // if your print the mySerial.read() data as HEX until it is not available, you will get several measures for the distance (FF-XX-XX-XX-FF-YY-YY-YY-FF-...). I think that is some kind of internal buffer, so I'm only considering the first 4 bytes in the sequence (which I hope that are the most recent! :D )
    if (bitpos < 4) {
      mybuffer[bitpos++] = mySerial.read();
    } else break;
  }
  mySerial.flush(); // discard older values in the next read

  mili = mybuffer[1] << 8 | mybuffer[2]; // 0x-- : 0xb3b2 : 0xb1b0 : 0x--
  inches = 0.0393700787 * mili;
  Serial.print("PING: ");
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(mili);
  Serial.println("mili");

  delay(100);
}


微調整する部分は、
たまにマイナスの値がくるのでそれを無視するのと、
インチとmiliでしか値が出てないのでcmに変換が必要。



本来の販売元はTindieで出てるこれっぽい。
https://www.tindie.com/products/upgradeindustries/serial-ultrasonic-distance-sensor/

Serial Ultrasonic Distance Sensor (DYP-ME007TX)

Description
This is a fully digital, serial output ultrasonic sensor that uses typical RS-232/UART output signaling. Only one wire required!

This module constantly pumps out readings at 10 Hz which makes it incredibly easy to operate.

Package Contents

1DYP-ME007TX Serial Ultrasonic Sensor
Specifications

Interface: RS-232 / UART (constant output)
Serial Format: 9600 Baud No Parity 8 bits per byte 1 Stop Bit (output)
Range: up to 4m
Accuracy: 1mm
Sound Frequency: 40 kHz
Output Rate: 10 Hz
Response Envelope: &lt; 30 deg
Supply voltage: 5V
Supply current: 15mA typical
Serial output format

The output from this sensor comes in packets of 4-bytes:

Data[0] = 0xFF: Packet Header
Data[1]= 0x0-0xFF: Data Most Significant Byte (MSB)
Data[2]= 0x0-0xFF: Data Least Significant Byte (LSB)
Data[3]= 0x0-0xFF: Data Checksum: = (0XFF + MSB + LSB) & 0xFF
Data is in MILLIMETERS
16 bit value in mm = Data[1] >> 8 | Data[2]


ちなみに、MegaでやるとなぜかSoftwareSerialでDigital Pinを使おうとするとうごかなくて、
A8とかのピンだったら動く様子。ナジェナンディスカ…。


もう一点追記、
SoftwareSerialを複数やる場合、一度に複数ポートをよめないみたいで、
myPort.listen()で切り替える必要がある。
更に、listenして直後はデータ読めないので、ちょっとまってあげる必要がある。
めどい。。。
もろもろ加味したコードが下記。

#include<SoftwareSerial.h>

long showTimer;

#define NUM_SENSORS 7
#define MAX_RANGE 5000

SoftwareSerial mySerial[NUM_SENSORS] = {
  SoftwareSerial(A8,5),
  SoftwareSerial(A9,6),
  SoftwareSerial(A10,7),
  SoftwareSerial(A11,8),
  SoftwareSerial(A12,9),
  SoftwareSerial(A13,10),
  SoftwareSerial(A14,11),
};

int currentSensorIndex = 0;
long millimeters[NUM_SENSORS];
long waitTimer;

void setup () {
  Serial.begin(115200);

  for (int i=0;i<NUM_SENSORS;i++) {
    mySerial[i].begin(9600);
    millimeters[i] = 0;
  }
}

void loop() {
  if (currentSensorIndex < NUM_SENSORS) {
    if (waitTimer == 0) {
      mySerial[currentSensorIndex].listen();
      waitTimer = millis();
    }
    else if (millis() - waitTimer > 100) {
      readSerial(&mySerial[currentSensorIndex], currentSensorIndex);
      waitTimer = 0;
      currentSensorIndex++;
    }
  }
  else {
    currentSensorIndex = 0;
  }

  if (millis() - showTimer > 100) {
    for (int i=0;i<NUM_SENSORS;i++) {
      Serial.print(millimeters[i]);
      Serial.print("\t");
    }
    Serial.println();

    showTimer = millis();
  }
}

void readSerial(SoftwareSerial *s, int idx) {
  if (s->available() > 3) {
    byte myByte = s->read();
    byte myBuffer[2] = {0};
    if (myByte == 0xFF) // Start-Byte erkennen
    {
      myBuffer[0] = s->read(); // HighByte einlesen
      myBuffer[1] = s->read(); // LowByte einlesen
      s->flush();              // Checksumme und ggf. weitere aufgelaufene Messungen verwerfen
    }

    long mm = myBuffer[0] << 8 | myBuffer[1]; // HighByte und LowByte verodern
    if (mm > 0 && mm < MAX_RANGE) {
      millimeters[idx] = mm;
    }
  }
}