RS485 750cm Ultrasonic Level Sensorを試す

秋月電子通商で、いつも使っている超音波センサー[HC-SR04](https://akizukidenshi.com/catalog/g/gM-11009/)よりちょっとお高いセンサーが売っていたので購入してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/9dec5ee7-f441-5ad8-414b-abd9df0d8413.png)

### 特徴：
###### やや大きい
HC-SR04に比べると大きいです。基板に直接つけられるような足はないですが、
本体にリング状のネジが付いているので、筐体に穴をあけて固定する事ができます。
###### RS485で通信
Groveコネクタとなっていますが、I2Cでは使用できません。RS485ドライバが必要です。
今回は[こちら](https://akizukidenshi.com/catalog/g/gI-15806/)を使用しました。
###### 斜めの壁に弱い
センサーの指向性が高いのが災いしてか、斜めの壁に当たると超音波が返ってきません。
HC-SR04だと指向性が低いせいか、ある程度近い距離であれば斜めの壁でも返ってきます。
そのため、遠くまで計測できる割にはレスポンスがあまりよくない印象があります。
###### コマンドを送ると距離が返ってくる
HC-SR04はエコーの反射時間から自分で距離を計算する必要がありますが、
このセンサーは[コマンド](https://files.seeedstudio.com/wiki/RS485_Ultrasonic_level_Sensor/RS485-750cm-Ultrasonic-Level-Sensor.pdf)を送るだけで距離が返ってきて便利です。
例えば
```01 03 01 01 00 01 D4 36```と送ると
```01 03 02 02 EF F8 A8```のような値が返ってきます。
返り値の最初の01 03は固定、02はバイト数、続く２バイトが距離です。
温度補正も込みで計算してくれるコマンドもあります。
###### 計測時間が長い
コマンドを送ると距離が返ってくるのはよいのですが、計測にやや時間がかかります。
温度補正込みの計測には500[msec]、温度補正無しの計測には100[msec]ほどかかります。
###### 計測範囲外(>750[cm])はゼロが返ってくる
計測範囲外は0で返ってきます。一方最短距離は250で返ってくるので、
０であれば範囲外、と切り分ける必要があります。
###### 前面に柔らかい布等があると計測範囲外が返ってくることがある
これも高い指向性のせいか、柔らかい布が超音波を吸収してしまうようです。

### 結論：
お手軽で指向性が高く高性能ですが、用途を選ぶ感じです。
視覚障碍者向けの障害物センサーに使えないかなと試してみましたが、
そういった用途には向いていないようです。

###### サンプルソースコード
```
/*
https://akizukidenshi.com/download/ds/linear/ltc485cn8.pdf
RO (Pin 1): Receiver Output. If the receiver output is
enabled(RE low), then if A > B by 200mV, RO will be high.
If A < B by 200mV, then RO will be low.
RE (Pin 2): Receiver Output Enable. A low enables the
receiver output, RO. A high input forces the receiver
output into a high impedance state.
DE (Pin 3): Driver Outputs Enable. A high on DE enables
the driver output. A and B, and the chip will function as a
line driver. A low input will force the driver outputs into a
high impedance state and the chip will function as a line
receiver.
DI (Pin 4): Driver Input. If the driver outputs are enabled
(DE high), then a low on DI forces the outputs A low and
B high. A high on DI with the driver outputs enabled will
force A high and B low.
GND (Pin 5): Ground Connection.
A (Pin 6): Driver Output/Receiver Input.
B (Pin 7): Driver Output/Receiver Input.
VCC (Pin 8): Positive Supply; 4.75 < VCC < 5.25

https://files.seeedstudio.com/wiki/RS485_Ultrasonic_level_Sensor/RS485-750cm-Ultrasonic-Level-Sensor.pdf
1. Read the calculated value of distance:
Command: 01 03 01 00 00 01 85 F6
Return: 01 03 02 02 F2 38 A1
Description: The slave address is 0x01, the calculated value of distance is 0x02F2, convert to decimal is 754, the distance
value = 754mm
2. Read the real-time distance value:
Command: 01 03 01 01 00 01 D4 36
Return: 01 03 02 02 EF F8 A8
Description: The slave address is 0x01, the real-time distance value is 0x02EF, convert to decimal is 754, the distance
value = 751mm
3. Read the temperature value:
Command: 01 03 01 02 00 01 24 36
Return: 01 03 02 01 2C B8 09
Description: The slave address is 0x01, the temperature is 0x012C, convert to decimal is 300, the temperature
value=30.0℃
4. Modify the slave address:
Command: 01 06 02 00 00 05 48 71
Return: 01 06 02 00 00 05 48 71
Description: Change the address 0x01 to 0x05.
5. Modify the baud rate:
Command: 05 06 02 01 00 01 19 F6
Return: 05 06 02 01 00 01 19 F6
Description: Slave address is 0x05, change the baud rate to 0x01 (2400bps)
*/

#include <Arduino.h>
#include <SoftwareSerial.h>
#include "TmDeltaTimeNM.h"
#define _RE_PIN 4
#define _DE_PIN 5
#define RO_PIN 2
#define DI_PIN 3
#define MOTOR_PIN 13
#define BAUD_RATE 9600
#define MOTOR_ON_MS (20)

uint8_t buff[256];
SoftwareSerial mySerial(RO_PIN, DI_PIN); // RX, TX

//unsigned char cmd[] = {0x01, 0x03, 0x01, 0x01, 0x00, 0x01, 0x85, 0xf6};
unsigned char cmd[] = {0x01, 0x03, 0x01, 0x01, 0x00, 0x01, 0xd4, 0x36};


void setup() {
  // put your setup code here, to run once:
  pinMode(_RE_PIN, OUTPUT);
  pinMode(_DE_PIN, OUTPUT);
  pinMode(RO_PIN, INPUT);
  pinMode(DI_PIN, OUTPUT);
  pinMode(MOTOR_PIN, OUTPUT);
  Serial.begin(115200);
  mySerial.begin(BAUD_RATE);
  delay(100);
}

void loop() {
  digitalWrite(_RE_PIN, HIGH);
  digitalWrite(_DE_PIN, HIGH);
  mySerial.write(cmd, 8);
  digitalWrite(_RE_PIN, LOW);
  digitalWrite(_DE_PIN, LOW);
  int dataLen = mySerial.available();
  if(dataLen > 0){
    int recvSize = mySerial.readBytes(buff, dataLen);
    for(int i=0;i<recvSize;i++){
      Serial.print(buff[i], HEX);
      Serial.print(",");
    }
    Serial.print(" DST[mm]=");
    uint16_t dist = buff[3] << 8 | buff[4];
    Serial.println(dist);
  }
  delay(200);
}

uint16_t crc16(uint8_t* _data, uint16_t _len){
  uint16_t crc = 0xffff;
  for(uint16_t i=0; i<_len;i++){
    crc = (crc ^ _data[i]) & 0xffff;
    for(int j=0;j<8;j++){
      uint8_t lsb = crc & 1;
      crc >>= 1;
      if(lsb==1){
        crc = (crc ^ 0xa001) & 0xffff;
      }
    }
  }
  return crc;
}

```