LoginSignup
3
2

More than 3 years have passed since last update.

バイナリデータを1バイトごとに16進数の文字列で扱えるようにする

Last updated at Posted at 2018-01-29

シェルスクリプトでIoTをやるにはシリアルポートさえ読めればいいということに気づいたのですが、世の中そんなに甘くはなかった。
シリアルポートでパソコンやIoTゲートウェイに接続される測定器やセンサの中には、データを改行(CR/LF/CRLF)区切りの文字列でくれないものもたくさんあります。
そういったものはASCIIコードとは縁もゆかりもない、1ビット1ビットに丹精に意味が込められた無慈悲なビット列が流れてきます。(シリアル通信の時点で1バイト単位にはなってはいるけれど)

catで簡単にシリアルポートからデータが読み出せるといっても、流れているのが改行区切りのテキストではないとすると、シェルスクリプトでもバイナリを扱える必要が出てきます。
1バイトずつ16進数の文字列に変換してしまえばなんとかなるのでは?と思い、まずはその変換をやってみることにしました。

最初、odコマンドをつかって、以下のようにしようとしました。

cat binary_data | od -vtx1 -An | tr -s ' ' '\n'

ところが、odが出力をバッファリングしているようで、ファイルに保存済みのバイナリデータを変換するのはうまくいくけれど、1バイトずつリアルタイムに16進数のテキストに変換されてほしいので、これではあまり意味がない。

POSIXコマンドでできる範囲ではどうやら無理そうだったので、諦めてCで短いコードを書きました。(Cは経験があまりないのでこれでいいのかよくわからない・・・)
フォーマットが%02xになっているのは、1バイトあたり2文字の固定長になるようにしたいからです。

byte2hex.c
#include <stdio.h>
#include <unistd.h>

int main(void)
{
  unsigned char buf[1];

  while(read(0, buf, 1) > 0) {
    printf("%02x\n", buf[0]);
    fflush(NULL);
  }

  return 0;
}

コンパイルは以下の通り。

c99 byte2hex.c -o byte2hex

1バイト(0x41 = "A")を16進数文字列に変換してみる。

$ printf "%b" "\x41" | ./byte2hex
41

1秒おきに連続でデータを流してみる

$ while :; do printf "%b" "\x41"; sleep 1; done | ./byte2hex
41
41
41
41
41

さらに後ろに別のコマンドをパイプで繋いでもちゃんと流れてくる

 while :; do printf "%b" "\x41"; sleep 1; done | ./byte2hex | tee /dev/null
41
41
41
41
41

1バイトが3バイト(2文字+LF)になるのでデータ量は3倍になってしまいますが、これでバイナリを1バイト1行のストリームとして扱えるようになりました。

3
2
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
3
2