はじめに
部屋やベランダにセンサ仕掛けて色んな値取ってきたいなぁ、と思い ZigBee 始めました。通信部分は Boost Asio Serial_port 使ったのでプラットフォーム依存しないと思います(多分)。
ZigBee 事始め
- 作者: 濱原和明,佐藤尚一ほか著
- 出版社/メーカー: CQ出版
- 発売日: 2012/02/27
- メディア: 雑誌
- 購入: 1人 クリック: 14回
- この商品を含むブログ (2件) を見る
XBee の足のピッチはブレッドボードより狭いのでピッチ変換基板を仕入れておくと捗ります。
- 出版社/メーカー: スイッチサイエンス
- メディア: エレクトロニクス
- クリック: 7回
- この商品を含むブログを見る
通信プログラム
通信部分を最低限必要そうな部分だけ書くと次のようになります。本の第4章相当です。
#include <boost/asio.hpp> #include <boost/array.hpp> #include <iostream> using namespace boost::asio; int main(int argc, char *argv[]) { // API フレームに AT コマンド(IS: リモートピンの値を取得)を突っ込んだもの boost::array<unsigned char, 32> send_api_frame = {0x7e, 0x0, 0xf, 0x17, 0x1, 0x0, 0x13, 0xa2, 0x0, 0x40, 0x71, 0xa2, 0xa9, 0xff, 0xfe, 0x2, 0x49, 0x53, 0x9b}; // 受信データ boost::array<unsigned char, 32> receive_api_frame; // API フレーム送りまーす try { io_service io; serial_port port( io, "COM3" ); port.set_option(serial_port_base::baud_rate(9600)); port.set_option(serial_port_base::character_size(8)); port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); port.set_option(serial_port_base::parity(serial_port_base::parity::none)); port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); port.write_some( buffer(send_api_frame) ); Sleep( 2000 ); port.read_some( buffer(receive_api_frame) ); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; exit(EXIT_FAILURE); } // 受信結果書き出し for (size_t i = 0; i < receive_api_frame.size() - 1; ++i) { std::cout << std::hex << (unsigned int)receive_api_frame[i] << " "; } std::cout << std::endl; return 0; }
実行結果:
7e 0 1b 97 1 0 13 a2 0 40 71 a2 a9 0 0 49 53 0 1 0 1 e 0 1 2 42 2 3f 2 40 42
至れり尽くせり感あります。とても楽ちんです。
返信データは、
1.2 [V] * 0x0240/0x03FF 〜 0.68 [V]
と本を参考に計算できます。本当は 3 V の電圧を抵抗で4分割した値を取っているので 0.75 V と出て欲しかったのですが、電池ヘタってきた感あるので、こんなもんでしょう。
ただ、コードを見ていただくと分かるように Sleep(2000) と直打ちして取り敢えずデータが返ってきそうな時間(ここでは2秒)待ってから結果を出力するようなコードになっていてイケてないです。そこで何とかならないかなと思い、read_some の代わりに async_read_some を使ってみました。
参考:C++ のお勉強 - IT戦記
#include <boost/asio.hpp> #include <boost/array.hpp> #include <boost/bind.hpp> #include <iostream> using namespace boost::asio; void read_handler(serial_port& port, const boost::system::error_code& error, boost::array<unsigned char, 32> buf, size_t length) { std::cout << length << std::endl; for (size_t i = 0; i < length; ++i) { std::cout << std::hex << static_cast<unsigned int>(buf[i]) << " "; } std::cout << std::endl; } int main(int argc, char *argv[]) { // 送信データ boost::array<unsigned char, 20> send_api_frame = {0x7e, 0x00, 0x0f, 0x17, 0x01, 0x00, 0x13, 0xa2, 0x00, 0x40, 0x71, 0xa2, 0xa9, 0xff, 0xfe, 0x02, 0x49, 0x53, 0x9b}; // 受信データ boost::array<unsigned char, 32> receive_api_frame; // API フレーム送りまーす try { io_service io; serial_port port( io, "COM3" ); port.set_option(serial_port_base::baud_rate(9600)); port.set_option(serial_port_base::character_size(8)); port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); port.set_option(serial_port_base::parity(serial_port_base::parity::none)); port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); port.write_some( buffer(send_api_frame) ); Sleep(10); port.async_read_some( buffer(receive_api_frame), boost::bind(read_handler, _1, boost::ref(receive_api_frame), _2) ); io.run(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; exit(EXIT_FAILURE); } }
実行結果:
7e 0 1b 97 1 0 13 a2 0 40 71 a2
あ、全部返って来ない…(´・ω:;.:...
全部返ってくる前に read_handler が呼ばれてしまい、結果、断片的なデータになってしまっています。非同期意味なかった、そりゃそうか。。
しっかりチェックするには受信データの長さと受信データ内の2バイト目、3バイト目の Length MSB/LSB を比較しないと駄目っぽいです。
#include <boost/asio.hpp> #include <boost/array.hpp> #include <iostream> using namespace boost::asio; int main(int argc, char *argv[]) { // API フレームに AT コマンド(IS: リモートピンの値を取得)を突っ込んだもの boost::array<unsigned char, 32> send_api_frame = {0x7e, 0x0, 0xf, 0x17, 0x1, 0x0, 0x13, 0xa2, 0x0, 0x40, 0x71, 0xa2, 0xa9, 0xff, 0xfe, 0x2, 0x49, 0x53, 0x9b}; // 受信データ boost::array<unsigned char, 32> receive_api_frame; receive_api_frame.assign(0xff); // API フレーム送りまーす try { io_service io; serial_port port( io, "COM3" ); port.set_option(serial_port_base::baud_rate(9600)); port.set_option(serial_port_base::character_size(8)); port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); port.set_option(serial_port_base::parity(serial_port_base::parity::none)); port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); port.write_some( buffer(send_api_frame) ); // 全データ受信するまでグルグル size_t total_length = 0; boost::array<unsigned char, 32> buf; for(;;) { size_t length = port.read_some( buffer(buf) ); for (size_t i = 0; i < length; ++i) { receive_api_frame[total_length + i] = buf[i]; } total_length += length; std::cout << length << " " << total_length << std::endl; // 受信データ長チェック const int NOT_COUNTED_FRAME_LENGTH = 4; int received_length = static_cast<size_t>(receive_api_frame[2]) + NOT_COUNTED_FRAME_LENGTH; if (receive_api_frame[2] != 0xff && total_length == received_length) break; } } catch (const std::exception& e) { std::cerr << e.what() << std::endl; exit(EXIT_FAILURE); } // 受信結果書き出し for (size_t i = 0; i < receive_api_frame.size() - 1; ++i) { std::cout << std::hex << static_cast<unsigned int>(receive_api_frame[i]) << " "; } std::cout << std::endl; return 0; }
実行結果:
10 10 1 11 1 12 1 13 1 14 1 15 1 16 1 17 14 31 7e 0 1b 97 1 0 13 a2 0 40 71 a2 a9 0 0 49 53 0 1 0 1 e 0 1 2 3c 2 3c 2 3d 4e
IS コマンドとかであれば 3 バイト目の LSB だけ使って計算しておけば事足りそうなので手抜きしました。というかそもそも 2 バイト目の MSB がどんなものか理解していない。。
チェックサムとか計算した版
チェックサムとか最終的な値とか計算した版を以下に掲載します。
#include <boost/asio.hpp> #include <boost/array.hpp> #include <vector> #include <sstream> #include <iostream> using namespace boost::asio; const std::string START_DELIMITER = "7E"; const std::string LENGTH_MSB = "00"; const std::string LENGTH_LSB = "0F"; const std::string FRAME_TYPE = "17"; const std::string FRAME_ID = "01"; const std::string REMOTE_ADDRESS = "00 13 A2 00 40 71 A2 A9"; const std::string DESTINATION_ADDRESS = "FF FE"; const std::string APPLY_CHANGES = "02"; const std::string AT_COMMAND_P1 = "50 31"; const std::string AT_COMMAND_IS = "49 53"; const std::string PARAM_LOW = "04"; const std::string PARAM_HIGH = "05"; int main(int argc, char *argv[]) { // 送信する API フレーム std::stringstream ss( START_DELIMITER + " " + LENGTH_MSB + " " + LENGTH_LSB + " " + FRAME_TYPE + " " + FRAME_ID + " " + REMOTE_ADDRESS + " " + DESTINATION_ADDRESS + " " + APPLY_CHANGES + " " + AT_COMMAND_IS ); // 16 進数のコマンドに変換 std::vector<unsigned char> send_api_frame; while (!ss.eof()) { int val; ss >> std::hex >> val; send_api_frame.push_back(static_cast<unsigned char>(val)); } // チェックサムを付加 auto it = send_api_frame.begin() + 3; // 4桁目から unsigned int check_sum = 0; // for (; it != send_api_frame.end(); ++it) { // 末尾まで総和 check_sum += static_cast<unsigned int>(*it); } check_sum = 0xFF - (check_sum & (0xFF)); // 0xFF から総和の下二桁を引いた値 send_api_frame.push_back(static_cast<unsigned char>(check_sum)); // 送信コマンド書き出し for (auto i = send_api_frame.begin(); i != send_api_frame.end(); ++i) { std::cout << std::hex << static_cast<unsigned int>(*i) << " "; } std::cout << std::endl; // 読み取りデータ boost::array<unsigned char, 32> receive_api_frame; receive_api_frame.assign(0xff); // API フレーム送りまーす try { io_service io; serial_port port( io, "COM3" ); port.set_option(serial_port_base::baud_rate(9600)); port.set_option(serial_port_base::character_size(8)); port.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); port.set_option(serial_port_base::parity(serial_port_base::parity::none)); port.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); port.write_some( buffer(send_api_frame) ); // 全データ受信するまでグルグル size_t total_length = 0; boost::array<unsigned char, 32> buf; for(;;) { size_t length = port.read_some( buffer(buf) ); for (size_t i = 0; i < length; ++i) { receive_api_frame[total_length + i] = buf[i]; } total_length += length; // 受信データ長チェック const int NOT_COUNTED_FRAME_LENGTH = 4; int received_length = static_cast<size_t>(receive_api_frame[2]) + NOT_COUNTED_FRAME_LENGTH; if (receive_api_frame[2] != 0xff && total_length == received_length) break; } } catch (const std::exception& e) { std::cerr << e.what() << std::endl; exit(EXIT_FAILURE); } // 受信結果書き出し for (size_t i = 0; i < receive_api_frame.size() - 1; ++i) { std::cout << std::hex << static_cast<unsigned int>(receive_api_frame[i]) << " "; } std::cout << std::endl; // 計算結果書き出し const int AIN3HI_INDEX = 28; const int AIN3LO_INDEX = 29; unsigned char ain3hi = receive_api_frame[AIN3HI_INDEX]; unsigned char ain3lo = receive_api_frame[AIN3LO_INDEX]; float result = 1.2f * (ain3hi * 0x0100 + ain3lo) / 0x03FF; std::cout << result << " [V]" << std::endl; int wait; std::cin >> wait; return 0; }
おわりに
ボタン電池で駆動したとしてどれくらい持つんだろうか…。