Implementing Serial Communication between Davis Vantage Pro2 Plus and Armadillo
This article is a translation of the following my article:
Original: Davis Vantage Pro2 プラスとArmadilloでシリアル通信を行う
* 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 awhileloop 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.