Javaと組込み機器Cで通信するときの留意事項

AndroidアプリなどJava言語と、組込み機器C言語で通信させるとき、せちがらい制約があります。その制約と対応方法の覚書です。

古くはVB6(VBA)C#Switf には構造体はありますが Java だけなんで?て感じです。Java側でByte配列へ編集もしくは分解するしかないようです。以下事例です。

組込み側

///  BTスレーブ要求フレーム ///
typedef struct _ST_SLAVE_REQ {
  uint8_t		head[ 4 ];	// HERAER兼パケット識別
 	uint16_t	len;		    // パケット長
 	uint16_t	req;		    // 要求コード
  uint8_t		foot[ 4 ];	// FOOTER
 	uint16_t	csum;		    // CHECKUSUM
} ST_SLAVE_REQ;
// Memo: 実際にはCPUアーキテクチャによってパウンダリ調整なし、2byte調整、4byte調整があるのでよく確認します。

Java側 :

///  BTスレーブ要求フレーム ///
short   frmLen = 14;
byte[]  buff = new byte[frmLen];

copyBytes(buff, 0, appCom.ID_SLAVE, 4 );          // 4: HERAER兼パケット識別
copyBin16(buff, 4, frmLen );            	        // 2: パケット長
copyBin16(buff, 6, appCom.REQ_START_MONITER );  	// 2: 要求コード
copyBytes(buff, 8, appCom.ID_TAIL, 4 );           // 4: HERAER兼パケット識別
short   csum = checksum(buff, frmLen - 2);        // Checksum
copyBin16(buff, 12, csum );            	           // 2: Checksum

Bundle arg = new Bundle();
arg.putByteArray( "BINARY",  buff );     // BIN DATA

WriteValueBLE( mServiceCANonNUS, mMessengerCANonNUS, arg );

byteコピー などはメソッドを追加します。web検索すると Unsafe.copyMemory なるものがでてきますが作った方が早い!

    ///////////////////////////////////////////////////
    // Byteコピー
    ///////////////////////////////////////////////////
    public boolean copyBytes(
        byte[]  to,
        int     offset,
        byte[]  from,
        int     len
    ) {
        if ( from.length < len ) {  // ちゃんと実体sizeをちぇっくしましょう。
            return false;
        }

        if ( to.length - offset < len ) {
            return false;
        }

        for( int i = 0; i < len; i ++ ) {
            to[ i + offset ] =  from[ i ];
        }
        return true;
    }

unit16変換関数を作ります。バイトオーダーは組込み機器側のCPUアーキテクチャに合せます。

///////////////////////////////////////////////////
// int16コピー
///////////////////////////////////////////////////
public boolean copyBin16(
    byte[]  to,
    int     offset,
    short   from
) {
    if ( to.length - offset < 2) {
        return false;
    }

    // ARMはリトルエンディアン
    to[ 0 + offset ] =  (byte)( from % 256 );
    to[ 1 + offset ] =  (byte)( from / 256 );
    return true;
 }


似たような言語では、C#はあり、Switf は無いようです。 (アプリだけ作ってる方々は、組込み機器ではsignedを基本使わないこと自体あまり知られていないのかもしれませんが…)

以下の場合、Java側で正しい値になりません。以下例では、frm.len は 4002( 0x9C42) となり、Java側で 0x9C がUnder Flow し -100 となります。そのまま extractBin16 で加算されると66 (0x42) + -100 * 256 = -25,534 と全く異なる値になってしまいます。

組込み側:

///  BTスレーブ応答フレーム ///
typedef struct _ST_SLAVE_RESP {
		uint16_t	len;		         // パケット長 6
    uint8_t    data[40000];    // CAN DATA部
} ST_SLAVE_RESP;

		:
	前略
		:
ST_SLAVE_RESP frm;

frm.len = sizeof(ST_SLAVE_RESP) - sizeof( uint16_t );	// パケット長 ,checksum分引く = 40,002(0x9C42)
		:
	中略
		:
 err_code = ble_nus_data_send( /*in*/    &m_nus,
			 						  /*in*/     &frm,
			  					  /*in-out*/ &sendLen,
									  /*in*/     m_conn_handle );

Java側:

/// BLEデータの取出し
byte alldata[] = characteristic.getValue();

// パケット長 6
short len = extractBin16(alldata, 4);
		:
	中略
		:

///////////////////////////////////////////////////
// int16取り出し
///////////////////////////////////////////////////
public short extractBin16(
    byte[]  from,
    int     offset
) {
    short   val  = 0;
    short    tmp;

    // QualcommはARM系なのでリトルエンディアン、Nordic もARM系なのでリトルエンディアン
    val  +=  from[ 0 + offset ];            // 0x42 → 10進 66
    val  +=  ( from[ 1 + offset ] * 256 );  // 0x9C → 10進 -100

    return val;
}

修正例は以下のとおりです。これは VB6(VBA) も同様で昔はよく引っ掛かっていたものです。

///////////////////////////////////////////////////
// int16取り出し
///////////////////////////////////////////////////
public short extractBin16(
    byte[]  from,
    int     offset
) {
    short   val  = 0;
    short    tmp;

    // ARMはリトルエンディアン
    tmp  =  from[ 0 + offset ];
    if ( tmp < 0 ) {    // Javaはunsignedがないため、1の補数で補完する
        tmp = (short)(255 + tmp + 1);
    }
    val  +=  tmp;

    tmp  =  from[ 1 + offset ];
    if ( tmp < 0 ) {    // Javaはunsignedがないため、1の補数で補完する
        tmp = (short)(255 + tmp + 1);
    }
    val  +=  ( tmp * 256 );

    return val;
}

チエックサム計算も同様にくるってしまいます。尚、shortがオーバーフローしたとき、プログラム異常になってしまうのかわかりませんが対策しておきます。

組込み側:

uint16_t CalcCheckSum(
 	 uint8_t *buf, 	// i : 対象Buffer
	 int16_t len	  // i : 対象Buffer長。
) {
	uint16_t i;
	uint16_t sum;

	sum = 0;
	for( i = 0; i < len; i ++ ) {
		sum += buf[ i ];
	}
	return	sum;
}

Java側:

 ///////////////////////////////////////////////////
 // checksum
 ///////////////////////////////////////////////////
 public short checksum(
     byte[]  to,
     int     len
 ) {
     long    sum = 0;
     short    tmp;

     for( int i = 0; i < len; i ++ ) {
         tmp = (short)to[ i ] ;
         if ( tmp < 0 ) {    // Javaはunsignedがないため補完する
             tmp = (short)(255 + tmp + 1);
         }
         sum += tmp ;
     }

     return((short)sum);
 }


組込みC側と送受信する文字列は、Stringでは渡せないです。copyValueOf(char[] data)、valueOf(char[] data) があるようですがコードもかさむんで、Byte[] でよいでしょう。

    final byte[]  ID_MASTER = {0x42,0x54,0x30,0x32};   // "BT02" MASTERから
    final byte[]  ID_TAIL   = {0x42,0x45,0x4E,0x44};   // "BEND"	 終了

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です