0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[最大20m] 距離センサー TSD20を使用する

0
Last updated at Posted at 2026-05-07

マイコンで距離を測るとき、まず思い浮かぶのは300円台で買える超音波センサーですよね。

手軽で便利なのですが、測れるのは最大4mほどで、もう少し距離がほしい場面では物足りなく感じていました。

そこで調べてみたところ、TSD20 というセンサーなら2500円程度と比較的安く、20m前後まで測れることが分かり、試してみることにしました。

ところが実際に使ってみると、データシートに”罠”がいくつもありましたので、そのあたりをまとめてみました。
TSD10,TSD80というセンサーもありますが、似たような罠があるようです。

罠1 データシートにウソが書いてある

Overviewがいきなり間違ってます。

image.png

同梱ケーブルの話ではあるのですが、実際には

No. Def/Col
1 NC(White)
2 3.3V(Red)
3 TX(Yellow)
4 RX(Green)
5 NC(Blue)
6 GND(Black)

です

罠2 チェックサムがわかりにくい

data sheet
にあるコマンド表のチェックサムの扱いが分かりにくいです
image.png

実際はこんな感じです。

表記例 (bytes)
ww xx yy checksum ww,xx,yy,checksum(4bytes)
ww xx yy(checksum) ww,xx,yy(3bytes)
ww xx yy ww,xx,yy(3bytes)

つまり
checksumとあるのは
「ww,xx,yyからチェックサムを計算して後ろに足す」
(checksum)、および無印は
「直前のyyがチェックサム」
です。

チェックサム計算方法 ```cpp uint8_t CheckSum(uint8_t *_pbuff, uint16_t _cmdLen) { uint8_t cmd_sum=0; uint16_t i; for(i=0;i<_cmdLen;i++) { cmd_sum += _pbuff[i]; } cmd_sum = (~cmd_sum); return cmd_sum; } ```

罠3 一度セットしたレジスタは保存される

工場出荷状態はUART出力、ボーレート460800bpsですが、
image.png
UARTから5A06 02 80 04 73 checksumを実行した後だと電源を落としてもボーレートが115200bpsになり、
image.png
5A1F 02 1F 1F A0を実行した後では電源を落としてもIICモードで立ち上がります。
データシートには書いてありません

その他

・センサーのTXはマイコンのRXに、RXはマイコンのTXにつなぐ

・0x5C, 0x02, 0x11, EC は 0x1102 = 0x02+(0x11<<8) = 4354mm
image.png

サンプルコード
// https://positive-inno.com/wp-content/uploads/2026/04/TSD20-user-manual.pdf
#include <Arduino.h>
#include <M5Unified.h>

#define RX_PIN G38 // from sensor TX // M5AtomS3
#define TX_PIN G39 // from sensor RX // M5AtomS3

uint8_t trushBuf[256]; // TSD20Serialの受信バッファをクリアするための一時的なバッファ

HardwareSerial TSD20Serial(1); // UART1を使用

uint8_t CheckSum(uint8_t *_pbuff, uint16_t _cmdLen)
{
  uint8_t cmd_sum=0;
  uint16_t i;
  for(i=0;i<_cmdLen;i++)
  {
  cmd_sum += _pbuff[i];
  }
  cmd_sum = (~cmd_sum);
  return cmd_sum;
}

bool SendCmd(uint8_t *_pbuff, uint16_t _cmdLen, bool _addCkSum = true)
{
    uint8_t serialCmd[_cmdLen+2]; // head(1) + cmd(_cmdLen) + sum(1)
    uint8_t checksum = CheckSum(_pbuff, _cmdLen); // データ部分のチェックサム
    serialCmd[0] = 0x5A; // ヘッダ
    memcpy(&serialCmd[1], _pbuff, _cmdLen); // コマンドデータをコピー
    if(_addCkSum){
      serialCmd[_cmdLen+1] = checksum; // チェックサムをコマンドの最後のバイトにセット
      TSD20Serial.write(serialCmd, sizeof(serialCmd));
    } else {
      TSD20Serial.write(serialCmd, _cmdLen+1); // チェックサムなしで送信
    }
    TSD20Serial.flush();
    return true;
  }

  void DisplayHex(const uint8_t* data, size_t length) {
    M5.Lcd.printf("%02X:", length);
    for (size_t i = 0; i < length; ++i) {
      M5.Lcd.printf("%02X ", data[i]);
    }
    M5.Lcd.println();
  }

  void WaitAndHoldResponce(uint16_t expectedLen, uint16_t displayMs = 3000, uint16_t timeoutMs=1000) {
    // 応答の前に受信バッファをクリア 
//    TSD20Serial.readBytes(trushBuf, TSD20Serial.available()); 
    while (TSD20Serial.available() > 0) {
      TSD20Serial.readBytes(trushBuf, TSD20Serial.available()); // バッファをクリア
    }
    uint32_t startTime = millis();
    while (TSD20Serial.available() < expectedLen) {
      if (millis() - startTime > timeoutMs) {
        M5.Lcd.println("Timeout waiting for response");
        delay(displayMs);
        return;
      }
      uint8_t retCmd[expectedLen];
      DisplayHex(retCmd, expectedLen);
      delay(displayMs);
    }
  }


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

  // put your setup code here, to run once:
  M5.begin();

  // ディスプレイに「Start!」と表示
  M5.Lcd.setTextSize(1); // 文字サイズ調整(必要に応じて)
  M5.Lcd.setTextColor(WHITE, BLACK); // 白文字・黒背景
  M5.Lcd.setCursor(0, 0); // 表示位置(必要に応じて調整)
  M5.Lcd.clear();
  M5.Lcd.print("Start!\n");

  // TSD20 UART初期化(初回のみ460800,2回目以降はSet Frequencyした値)
  // Change to IICした後であればIICでコマンドを送る必要がある
  TSD20Serial.begin(115200, SERIAL_8N1, RX_PIN, TX_PIN);
  delay(100);

  /* --- シリアルナンバー取得 ---
  M5.Lcd.print("Get S/N\n");
  uint8_t serialCmd[] = {0x0D, 0x04, 0x0D, 0x0D, 0x0D, 0x0D, 0xBA};
  SendCmd(serialCmd, sizeof(serialCmd));
  WaitAndHoldResponce(9);
  */

  /* Set Frequency
  */
  M5.Lcd.print("Set Frequency\n");
  uint8_t freqCmd[] = {0x0B, 0x02, 0xE7, 0x03, 0x08};
  SendCmd(freqCmd, sizeof(freqCmd));
  WaitAndHoldResponce(6);

  /* Start Reading
  */
  M5.Lcd.print("Start Reading\n");
  uint8_t startCmd[] = {0x0A, 0x02, 0x02, 0x00, 0xF1};
  SendCmd(startCmd, sizeof(startCmd),false);
  WaitAndHoldResponce(6);

  /* Stop Reading
  M5.Lcd.print("Stop Reading\n");
  uint8_t stopCmd[] = {0x0A, 0x02, 0x00, 0x00, 0xF3};
  SendCmd(stopCmd, sizeof(stopCmd),false);
  WaitAndHoldResponce(6);
  */

  /* Change to IIC
  M5.Lcd.print("Change to IIC\n");
  uint8_t iicCmd[] = {0x1F, 0x02, 0x1F, 0x1F, 0xA0};
  SendCmd(iicCmd, sizeof(iicCmd),false);
  WaitAndHoldResponce(6);
  */

}

void loop() {
  while (TSD20Serial.available() > 0) {
    int cmdLen = TSD20Serial.available();
    uint8_t cmd[cmdLen];
    cmdLen = TSD20Serial.readBytes(cmd, cmdLen);
    M5.Lcd.clear();
    M5.Lcd.setCursor(0, 10); // 表示位置(必要に応じて調整)
    DisplayHex(cmd, cmdLen);
    if(cmd[0] == 0x5C){ // データフレームのヘッダ
      uint16_t distance = (cmd[2] << 8) | cmd[1]; // 距離データ(例: 2バイト little endian)
      M5.Lcd.setCursor(0, 30); // 距離表示位置
      M5.Lcd.printf("Distance: %d mm", distance);
    }
  }
  delay(1);
}

以上、はまりどころはありましたが、センサー自体は優秀でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?