Davis Vantage Pro2 プラスとArmadilloでシリアル通信を行う

Implementing Serial Communication between Davis Vantage Pro2 Plus and Armadillo

気象観測機器として使用されるDavis Vantage Pro2と、組み込みLinuxボードであるArmadillo G3とのシリアル通信を実装する機会がありました。この記事では、実際に使用したコードと共に、実装の詳細、注意点を共有したいと思います。

システム構成

  • 気象観測機器: Davis Vantage Pro2
  • 組み込みボード: Armadillo G3
  • 通信方式: シリアル通信 (RS-232C)
  • プログラミング言語: C++

以下に実装の概要を述べていきます。今回はこの参考マニュアルを参考にして作成しました。

https://wiki.greengaragedetroit.com/images/e/e4/VantageSerialProtocolDocs_v230.pdf

1.コードの詳細説明

int serial_port = open("/dev/ttyUSB0", O_RDWR);

まず、open()関数を使用して読み書き両方可のモードでシリアルポート(/dev/ttyUSB0)をオープンします。

struct termios tty;
memset(&tty, 0, sizeof(tty));
if (tcgetattr(serial_port, &tty) != 0) {
    std::cerr << "tcgetattrに失敗しました" << std::endl;
    return 1;
 }

termios構造体を使用してシリアルポートの現在の設定を取得するようにします。

// シリアルポート設定
  tty.c_cflag &= ~PARENB; // parity bit無効
  tty.c_cflag &= ~CSTOPB; // stop bitを1つに設定
  tty.c_cflag &= ~CSIZE; // データビットマスクのクリア
  tty.c_cflag |= CS8; // 8ビットデータ
  tty.c_cflag &= ~CRTSCTS; // ハードウェア制御なし
  tty.c_cflag |= CREAD | CLOCAL; // 読み取り有効化|制御ラインを無視
  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // ソフトウェア制御無効化
  tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 生データ設定
  tty.c_oflag &= ~OPOST; // 生データ設定

シリアルポートの各種パラメータ(パリティ、ストップビット、データビット数など)を設定します。

cfsetispeed(&tty, B19200);
cfsetospeed(&tty, B19200);

VantageProの参考マニュアルから、ボーレートを19200に設定します。

2. コマンド送信

const char* cmd = "LOOP 1\n";
write(serial_port, cmd, strlen(cmd));

“LOOP 1″コマンドをVantage Pro2に送信します。※このコマンドは、現在の気象データを1回送信するよう要求するものです。

3. データの受信

std::this_thread::sleep_for(std::chrono::seconds(1));
unsigned char buf[100];
int bytes_read = read(serial_port, buf, sizeof(buf));

コマンド送信後、1秒待機してからデータを読み取ります。

4. データの解析と保存

std::ofstream ofs("data.txt");
std::set<int> condition = {5,7,9,12,16,41,44,46,48,50,52,54,56,58,60,87,91,93,97};

for(long unsigned int i = 0; i < sizeof(buf)-1; i++){
    if(condition.count(i) > 0){
      unsigned int endian1 = (unsigned int)(*pbuf++);
      unsigned int endian2 = (unsigned int)(*pbuf++)*16*16;        
      ofs << endian1 + endian2 << std::endl;        
      i++;        
      continue;    
      }    
    ofs << (unsigned int)*pbuf++ << std::endl;
  }

受信したデータを解析し、ファイルに保存します。
特定のインデックス(condition内)のデータは2バイトとして扱い、それ以外は1バイトとして扱っています。

注意点と最適化

  • エラーハンドリング
    各システムコール(open, tcgetattr, write, readなど)の後にエラーチェックを行っています。実際の運用では、より詳細なエラーメッセージやログ出力を検討する方がいいと思います。
  • タイムアウト設定:
    tty.c_cc[VTIME] = 10;でタイムアウトを1秒に設定していますが、環境によっては調整が必要かもしれないです。
  • データ解析:
    現在のコードは固定長のデータを想定していますが、Vantage Pro2の出力フォーマットに変更がある場合は、より柔軟な解析方法を検討する必要があります。
  • 継続的なデータ取得:
    現在のコードは1回のデータ取得のみを行っています。実際の気象観測では定期的なデータ取得が必要ですので、このコードをcrontabでスケジューラーで実行させるか、whileループを使用して継続的にデータを取得する実装を検討してもいいと思います。

コード全体は以下になっています。

// ccserial.cc
#include <iostream>
#include <fstream>
#include <cstring>
#include <ctime>
#include <chrono>
#include <iomanip>
#include <set>
#include <thread>


#include <fcntl.h>
#include <unistd.h>
#include <termios.h>

int main() {
  // シリアルポートを開く
  int serial_port = open("/dev/ttyUSB0", O_RDWR);
  if (serial_port == -1) {
    std::cerr << "シリアルポートを開けませんでした" << std::endl;
    return 1;
  }

  // シリアルポートの設定を取得
  struct termios tty;
  memset(&tty, 0, sizeof(tty));
  if (tcgetattr(serial_port, &tty) != 0) {
    std::cerr << "tcgetattrに失敗しました" << std::endl;
    return 1;
  }

  // シリアルポート設定
  tty.c_cflag &= ~PARENB; // parity bit無効
  tty.c_cflag &= ~CSTOPB; // stop bitを1つに設定
  tty.c_cflag &= ~CSIZE; // データビットマスクのクリア
  tty.c_cflag |= CS8; // 8ビットデータ
  tty.c_cflag &= ~CRTSCTS; // ハードウェア制御なし
  tty.c_cflag |= CREAD | CLOCAL; // 読み取り有効化|制御ラインを無視
  tty.c_iflag &= ~(IXON | IXOFF | IXANY); // ソフトウェア制御無効化
  tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 生データ設定
  tty.c_oflag &= ~OPOST; // 生データ設定

  tty.c_cc[VMIN] = 0; // 最小読み取り文字数
  tty.c_cc[VTIME] = 10; // タイムアウト秒数 (1/10秒)
    
  cfsetispeed(&tty, B19200); // 入力baudrate
  cfsetospeed(&tty, B19200); // 出力baudrate

  if (tcsetattr(serial_port, TCSANOW, &tty) != 0) {
    std::cerr << "tcsetattrに失敗しました" << std::endl;
    return 1;
  }

  const char* cmd = "LOOP 1\n";
  if (write(serial_port, cmd, strlen(cmd)) == -1) {
    std::cerr << "コマンドの書き込み失敗しました" << std::endl;
    return 1;
  }

  std::this_thread::sleep_for(std::chrono::seconds(1)); // Wait for response

  unsigned char buf[100];

  unsigned char *pbuf;
  pbuf = &buf[1];

  int bytes_read = read(serial_port, buf, sizeof(buf));
  if (bytes_read == -1) {
    std::cerr << "データの読み取りに失敗しました" << std::endl;
    return 1;
  }

  // ファイル出力
  std::ofstream ofs("data.txt");
  if (!ofs.is_open()) {
      std::cerr << "ofsを開けませんでした" << std::endl;
      return 1;
  }
  
  std::set<int> condition = {5,7,9,12,16,41,44,46,48,50,52,54,56,58,60,87,91,93,97};

  for(long unsigned int i = 0; i < sizeof(buf)-1; i++){
    if(condition.count(i) > 0){
      unsigned int endian1 = (unsigned int)(*pbuf++);
      unsigned int endian2 = (unsigned int)(*pbuf++)*16*16;
      ofs << endian1 + endian2 << std::endl;
      i++;
      continue;
    }
    // 1bytes データの出力
    ofs << (unsigned int)*pbuf++ << std::endl;
  }

  // シリアルポートを閉じる
  close(serial_port);

  ofs.close();

  return 0;
}

まとめ

Davis Vantage Pro2とArmadillo G3を用いたシリアル通信の実装について、基本的なコードと共に解説しました。
この実装を基礎とすれば気象観測システムの構築が可能だと思います。実際の運用に当たっては、セキュリティ、安定性、拡張性などを考慮し、継続的な改善を行っていくことが重要です。


Categories:

,