bokumin.org

Github

Implementing Serial Communication between Davis Vantage Pro2 Plus and Armadillo

This article is a translation of the following my article:

 

 

* Translated automatically by Google.
* Please note that some links or referenced content in this article may be in Japanese.
* Comments in the code are basically in Japanese.

 

by bokumin

 

Implementing Serial Communication between Davis Vantage Pro2 Plus and Armadillo

 

 

I had the opportunity to implement serial communication between Davis Vantage Pro2, which is used as a weather observation device, and Armadillo G3, which is an embedded Linux board. In this article, I would like to share the code I actually used, as well as implementation details and points to note.

 

System configuration

 

  • Weather observation equipment: Davis Vantage Pro2
  • Embedded board: Armadillo G3
  • Communication method: シリアル Communication (RS-232C)
  • Programming language: C++

 

I will outline the implementation below. This time I created it with reference to this Reference manual.

 

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

 

Detailed code explanation

 

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

 

First, open the serial port (/dev/ttyUSB0) in read/write mode using the open() function.

 

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

 

Use the termios structure to retrieve the serial port’s current settings.

 

// シリアルポート設定
  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; // 生データ設定

 

Set various parameters of the serial port (parity, stop bit, number of data bits, etc.).

 

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

 

Set the baud rate to 19200 from the VantagePro reference manual.

 

Send command

 

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

 

Send the “LOOP 1” command to Vantage Pro2. *This command requests the current weather data to be sent once.

 

Receiving data

 

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

 

After sending the command, wait 1 second and then read the data.

 

Data analysis and storage

 

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;
  }

 

Analyze the received data and save it to a file.
The data at a specific index (in condition) is treated as 2 bytes, and the rest are treated as 1 byte.

 

Notes and optimization

 

  • Error handling
    Error checking is performed after each system call (__MASK_CODE_97___, tcgetattr, write, read, etc.). In actual operation, I think it would be better to consider more detailed error messages and log output.
  • Timeout settings:
    tty.c_cc[VTIME] = 10; sets the timeout to 1 second, but it may need to be adjusted depending on the environment.
  • Data parsing:
    The current code assumes fixed-length data, but if there are changes to Vantage Pro2’s output format, a more flexible parsing method should be considered.
  • Continuous data retrieval:
    The current code only retrieves data once. Actual weather observation requires regular data acquisition, so you may want to consider implementing this code by running it on a scheduler in crontab or by using a while loop to continuously acquire data.

 

The entire code is below.

 

// 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;
}

 

まとめ

 

I explained the implementation of serial communication using Davis Vantage Pro2 and Armadillo G3 along with basic code.
I think it is possible to build a weather observation system based on this implementation. During actual operation, it is important to consider security, stability, scalability, etc., and make continuous improvements.