バイトと16進数 〜OpenECHOを扱うための基礎の基礎〜

OpenECHOという、Javaのライブラリがあります。これは宅内の機器間通信のプロトコルであるECHONET LiteをJavaで実装したもので、使いこなすことができれば、自分のおウチを自分でどんどんスマートハウス化できてしまうという、Makerの人にとってはとても素敵なシロモノです。

このライブラリを扱う上で避けては通れないのが、バイトの扱い。なぜなら、ECHONET Liteの仕様で、機器間通信のパラメータや戻り値はバイトでやりとりすることになっているから。おそらく、プログラム大好きな人や、コンピュータの誕生と進化と共にプログラムを学んできた人からすればどうってことない問題なのでしょうけれど、初めて触った言語が大学の授業でのJavaで、しかもその後特にプログラムにハマるようなこともなかった自分からすれば、文字列、バイト、int、2進数、16進数等々の関係が良くわからないのです。

というわけで、わからないなりに整理。以下、おそらくもっと良い方法もあるのかもしれないけれど、とりあえず動けばいいや、の精神で記載しておりますのであしからず。

<int→byteの変換>

例えば、Androidの時間設定UI(TimePicker)で取得したintの値を、OpenECHOの炊飯予約時刻設定メソッド(reqSetValueOfRiceCookingReservationSettingTime)のパラメータに設定したい、という場合。「時」と「分」のintの値をそれぞれbyteに変換して、配列の形で引数に渡す必要があります。

以下、自分なりの対応です。

public class Util{
	public static byte int2byte(int value){
		return (new Integer(value)).byteValue();
	}
}
int hour   = 21; // 実際はGUIで取得した値を使用
int minute = 0; // 実際はGUIで取得した値を使用
try{
	devRiceCooker.set().reqSetValueOfRiceCookingReservationSettingTime(new byte[]{Uti.int2byte(hour), Util.int2byte(minute)}).send();
}catch(IOException e){
	e.printStackTrace();
}

Javaではbyte型は1バイト、int型は4バイトなので、256以上の値に対してこの変換をかけると、int型の下位1バイトだけが代入されることになると思われます(未確認)。が、OpenECHOでパラメータとして渡す値はほとんど256未満だと思うので、とりあえずこれで済ませます。

単純にbyteにキャストしてもよいのかもしれません。

<byte→16進数文字列の変換>

デバッグ時など、byte型に入れられた値を16進数でprintlnやLogで表示したいときもあると思います。

以下、自分なりの対応です。

public class Util{
	public static String toHexStr(byte[] arg){
		String ret = "";
		for(int i=0;i<arg.length;++i){
			String valueString = Integer.toHexString(arg[i] & 0xff);
			ret += "0x";
			if(valueStr.length() == 1){
				ret += "0";
			}
			ret += valueStr + ",";
		}
		return ret.substring(0, ret.length()-1);
	}
}

ハイライト部がキモで、0xffで&をとらないと、例えばarg[i]に格納されている値が2進数で11001000の場合には、格納時には10進数の200で格納したつもりでも、arg[i]=-56という扱いを受けます。これは、Javaのbyte型が符号付きの1byteのため、-128〜127の範囲で値を表現しようとするからです。詳細については、このあたりを参照していただくとして、0xffで&をとるとうまくいく理由を簡単にまとめると、

・0xffはint型(32bit, 4byte)なので、計算相手であるbyte型(8bit, 1byte)のarg[i]は、自動的にint型にキャストされる

・このときのキャストは、仮にarg[i]に格納されている値が2進数で11001000の場合は、負の数と判定されて符号がキープされるため、11111111 11111111 11111111 11001000になる

・これに対して、0xff(00000000 00000000 00000000 11111111)で&をとると、勝手に拡張された部分がすべて0になる

・計算の結果はint型なので、値の表示範囲も-2147483648〜2147483647まで拡大されており、200が表示される

となるようです。これで正しいintの値を取得し、toHexStringで16進数文字列に変換した後は、自分の好みで、16進数であることを表す”0x”、一桁でも二桁表示に合わせるための”0″をくっつけて表示しています。

気をつけなければいけないのは、「0xffで&」の手続きは、あくまで表示して確認するときに必要な手続きであるということ。arg[i]に対して0xffで&をとって表示した値が200であっても、0xffで&をとらずに表示した値が-56であっても、どちらの場合でもarg[i]には11001000が格納されており、bitの並びとしては同じものが格納されています。

<16進数文字列→byteの変換>

普通にOpenECHOを扱う上では出てこないと思いますが、せっかくなので。例えば文字列”ff”(0xはついていません)をbyteに変換したい場合は、次のようになります。

byte value = Integer.valueOf("ff", 16).byteValue();

繰り返しになりますが、どれもこれももっと良い方法があると思いますので、ご参考程度ということで、あしからず。