RS-485トランシーバを使ってRS-485センサーとModbus通信

業務上RS-485センサーを複数使うことが多いのですが、
Amazonで購入しているRS-485⇔Serialの通信変換ボードの在庫が危ういことが多々あります。
いずれ自作できれば……と思ったので、手始めにRS-485トランシーバを使ってModbus通信を行ってみました。

# 材料

- Seeeduino Mega
- [RS485/RS422トランシーバ　LTC485CN8](https://akizukidenshi.com/catalog/g/gI-02792/)
- ジャンパーワイヤ
- RS-485センサー

# 環境

- MacOS Catalina 10.15.7
- Arduino IDE 1.8.12

# 配線

下記の図のように配線します。
今回使用しているセンサーが5V駆動なのでMegaからそのまま電源が取れましたが、
駆動電圧が異なる方は別の電源を取ってくるなど工夫してください。

![配線図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/5d7b1e08-acad-76c0-64b9-2c139ed3d6c0.png)

ブレッドボード上で配線した写真です。（汚くてすみません。。。）
![配線写真.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/5b3e4e57-60eb-659b-4c19-6063d643fcd0.jpeg)


# コード

```c:rs485_test.ino

#include <Arduino.h>
#include <stdio.h>

#define DE_PIN  3
#define RE_PIN  2

byte slaveID = 0x01;
// 各自のRS-485センサーでの命令を記述してください
byte getVolt[] = {slaveID, 0x03, 0x00, 0x16, 0x00, 0x02, 0x25, 0xCF};

// 各自のRS-485センサーでのレスポンス時間に従って書き換えてください
const int modbusTimeout = 30 * 1000;

uint32_t startMillis = 0;
int bytesRead = 0;

byte responseBuffer[20];
unsigned char p[4];
float f = 0;

void setup() {
  pinMode(DE_PIN, OUTPUT);
  pinMode(RE_PIN, OUTPUT);
  
  Serial.begin(115200);
  Serial1.begin(9600);

  Serial.println("setup completed!");
}

void loop() {
  transmit_enable();

  Serial1.write(getVolt, sizeof(getVolt)/sizeof(getVolt[0]));
  Serial1.flush();
  startMillis = millis();

  Serial.print("write instructions : ");
  // iは各自のセンサーの命令バイト数に従って書き換えてください
  for(int i=0; i < 8; i++){
    Serial.print(getVolt[i], HEX);  Serial.print(" ");
  }

  receive_enable();

  while ((Serial1.available() == 0) && ((millis() - startMillis) < modbusTimeout))
  { delay(1UL);}

  Serial.println();

  if (Serial1.available() > 0){
    // バイト数(10)は各自のセンサーのレスポンスバイト数に従って書き換えてください
    bytesRead = Serial1.readBytes(responseBuffer, 10);

    Serial.print("response frame : ");
  
    if(bytesRead > 0){
      // jは各自のセンサーのレスポンスバイト数に従って書き換えてください
      for(int j=0; j < 9; j++){
        Serial.print(responseBuffer[j], HEX); Serial.print(" ");
      }
    }
  
    Serial.println();
    
    // ここは私が使用しているセンサーの仕様で記述しています
    // 各自のセンサーのレスポンス仕様に従ってresponseBufferから値を取得してください
    p[0] = responseBuffer[3];
    p[1] = responseBuffer[4];
    p[2] = responseBuffer[5];
    p[3] = responseBuffer[6];
    f = get_float(p); // byte to float
    // 値取得ここまで
      
    Serial.print("float value : "); Serial.print(f);
    Serial.println();
  }

  Serial.println();
  
  emptyResponseBuffer(&Serial1);
  delay(5 * 1000UL);
}

void transmit_enable() {
  digitalWrite(DE_PIN, HIGH);
  digitalWrite(RE_PIN, HIGH);
}

void receive_enable() {
  digitalWrite(DE_PIN, LOW);
  digitalWrite(RE_PIN, LOW);
}

// Byte convert to float
float get_float(unsigned char *p) 
{ 
  float value = 0;
  unsigned char *valueAddress = (unsigned char*)&value;
  
  *valueAddress = p[1];
  valueAddress++;
  *valueAddress = p[0];
  valueAddress++;
  *valueAddress = p[3];
  valueAddress++;
  *valueAddress = p[2];
  
  return(value); 
}

// This empties the serial buffer
void emptyResponseBuffer(Stream *stream)
{
    while (stream->available() > 0)
    {
        stream->read();
        delay(1);
    }
}
```

# 結果
今回使用した命令は、センサーの内部電圧（3.3V）を返す命令でした。
![結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256451/9a6cba7d-acaf-ef75-7b8c-8b4b87c8e031.png)
このように、無事レスポンスが返ってきました。

# まとめ
ひとまず基本の通信ができたので安心です。
もっと簡易に通信できるよう、基板制作に挑戦していきたいと思います。