마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

ble 대용량 write callback 안오는 문제

0 추천

저희 회사에서 만든 ble 장치와 통신하는 앱을 만들고 있습니다. 통신은 발송용 캐릭터리스틱과 수신용 캐릭터 리스틱을 이용해서 수발신을 하고 있습니다.

발신은 발송할 데이터를 큐에 넣고 writeCharacteristc을 한 후, onCharacteristicWrite 콜백이 오면 결과에 따라서 큐의 다음 데이터를 발송하는 방식입니다. 수신은 onChracteristicChanged 콜백을 통해 받고 브로드캐스팅 합니다.

일반적인 짧은 양의 데이터는 수 발신이 모두 잘 되고 있습니다. 그런데.. 60KB파일을 전송하고자 하는데, 발송마다 20~30ms와 같은 딜레이를 주지 않으면 어느 순간 onChracteristicChanged이 발생하지 않습니다. writeCharacteristc이 initiate가 실패한것도 아니었습니다.

전송속도 문제떄문에 딜레이를 주고 싶지 않은데, 위와같은 현상이 발생하여 고민입니다. 구글링 열심히 해봤지만 비슷한 증상을 찾지 못했습니다. 아시는 분 도움 부탁드립니다.

 
-- 추가 --
베가 아이언 1 4.4.2, 갤럭시s3 4.3 갤럭시s3 4.4.4에서는 딜레이 0은 모두 실패

노트3 5.0은 딜레이 0도 잘되네요.

 

코드 첨부합니다.

쓰기 콜백 부분입니다. 안드로이드 콜백에서 이부분을 다시 호출하도록 했습니다.

/**
     *  onCharacteristicWrite 콜백에 따라 실패 또는 성공 처리 하고, 다음 스트림을 발송한다.
     *
     * @param isGattSuccess 발송 성공 여부
     * @param wroteValue 발송 값
     */
    protected void onCharacteristicWrite(boolean isGattSuccess, byte[] wroteValue) {

        stopWriteCallbackTimer();
        //Log.i("STREAM", "onCharacteristicWrite " + isGattSuccess);


        MposInputStream stream = streamQueue.peek();
   
        //notify send part result
        int msg = (isGattSuccess ? MSG_SEND_PART_BYTES_SUCCESS : MSG_SEND_PART_BYTES_FAIL);
        notifyMsg(msg, stream.getAid(), stream.getCommandCode(), wroteValue);

       /*

        try {
            Thread.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//*/


            if (isGattSuccess) {

                if (stream.available() > 0) {

                    writeFromStream();

                    return;

                } else {

                   // Log.i("STREAM", "all stream data sent stream.available() : " +stream.available());
                    // all stream data sent
                    streamQueue.poll();
                    stream.close();

                    tcs.setResult(true);

                    trySendingFromQueue();

                    notifyMsg(MSG_SEND_BYTES_SUCCESS, stream.getAid(), stream.getCommandCode(), null);


                }

            } else {

              //  Log.i("STREAM", "NOT GattSuccess  stream.available() : " + stream.available());
                tcs.setResult(false);
                streamQueue.poll();
                stream.close();

                trySendingFromQueue();
                notifyMsg(MSG_SEND_BYTES_FAIL, stream.getAid(), stream.getCommandCode(), null);


            }



    }

 

쓰기 부분입니다. 데이터는 큐에 있고, 큐에는 인풋스트림이 들어갑니다. 발송은 스트림에서 20바이트씩 읽어서 발송합니다. 

/**
 * 스트림으로 부터 데이터를 읽어 발송한다.
 * @return true, if the write operation was initiated successfully
 */

protected boolean writeFromStream() {

    //Log.i("STREAM", "writeFromStream stream" );

    MposInputStream stream = streamQueue.peek();



    if ( ct.isCancellationRequested() && stream.available() > 0 ) {
        //cancel

        Log.i(TAG, "CANCELED - " + stream.available());

        tcs.setCancelled();
        streamQueue.poll();
        stream.close();


        notifyMsg(MSG_SEND_CANCELED, stream.getAid(), stream.getCommandCode(), null);

        trySendingFromQueue();

        return false;
    }


    byte[] data = null;

    try {

        int count = stream.read(buffer);

        if ( count < 0) return false;

        data = buffer.length == count ? buffer : Arrays.copyOf(buffer, count);

    } catch (IOException e) {

        e.printStackTrace();

        streamQueue.poll();
        stream.close();

        notifyMsg(MSG_SEND_PART_BYTES_FAIL, stream.getAid(), stream.getCommandCode(), data);
        notifyMsg(MSG_SEND_BYTES_FAIL, stream.getAid(), stream.getCommandCode(), null);

        trySendingFromQueue();


        return false;

    }


    //scheduleWriteCallbackTimeout(stream, data);

    boolean initiated = writeCharacteristic(data);

    if ( !initiated ){
        Log.v(TAG, "writeFromStream - initiated : " + initiated + " available : " + stream.available());
        stopWriteCallbackTimer();

        notifyMsg(MSG_SEND_PART_BYTES_FAIL, stream.getAid(), stream.getCommandCode(), data);
        notifyMsg(MSG_SEND_BYTES_FAIL, stream.getAid(), stream.getCommandCode(), null);

        trySendingFromQueue();

    }


    return initiated;
}
rict (170 포인트) 님이 2015년 10월 27일 질문
rict님이 2015년 10월 27일 수정
전체적인 문제점이 대락적으로 이해는 가나.... 조금더 자세하게 에러코드라던지 타겟 장비가 어떤형식이라던지 프로파일은 어떤걸 사용한다던가 하는 설명이 덧붙혀지면 답변하기에 좀 더 용의하지 안을까 생각해봅니다^^
콜백의 경우 원래 상태코드를 받게 되있습니다만 이경우는 아예 콜백자체가 오지 않는 경우 입니다.  프로파일은 gatt_hid  쪽의 커스텀 서비스를 이용하고 있습니다.
흠.. 저도 첨에 문자열 전송할 떄 그냥 멋모르고 50바이트로 설정하고 pcb랑 통신했다가 애를 먹었는데요.... write할때나 read 할때 20바이트로 설정해야하는지를 뒤늦게 알았어요...
http://stackoverflow.com/questions/9748219/bluetooth-file-transfer-android
여기 사이트에 보면 파일 전송시에 수신측 데이터 손상을 방지하기 위해 적당한 바이트 사이즈를 제시해주는데... 파일의 크기가 8 * 1024 이 사이즈가 기본으로 되어서 그 배수로 크기가 결정되어 진다고 가정하게 되면 그 범위를 벗어날 경우,, 특정 case 에 따라 예외가 발생될 수 있다라고 생각되어 집니다.
어디 까지나 제가 판단한 개인적 의견이구요.  참고하시라고 좀 찾아봤습니다.
답변감사드립니다.
그런데 파일 전송용 프로토콜의 패킷 사이즈는 140바이트 이하로 하고 있습니다. 20바이트씩 나눠서 보내기도 하고 있습니다.
전해주신 링크는 ble는 아닌것 같고, 제 상황과는 좀 다른것 같습니다.
아....ble 파일 전송용 프로토콜은 140바이트가 max 인가요? 일반 스트링은 max가 20바이트인것 같더라구요.  해당부분 해결 하시고 공유하시면 많은 유저들에게 도움 되리라 생각되네요^^ 응원드리고 갑니다.
ble의 프로토콜이 아니라. 저희 장치에서 정한 프로토콜이 데이터를 128바이트씩 보내도록 정했습니다. 헤더등 포함하면 140언더가 됩니다. ble는 20바이트가 맞다고 알고있습니다. 해결은..잘모르겠네요 일단 미뤄두기로 했습니다;

1개의 답변

0 추천
짧은데이타는 저도 해봣는데, 무리가없어서..

파일을 사용하신다고하니,

가능한 데이타를 줄여서 데이타를 압축해서 전송하면 문제가 좀 나아지지 않을가요

짧은 소견이입니다.
스마일리 (540 포인트) 님이 2015년 10월 27일 답변
우선 답변 감사드립니다. 압축도 생각해보겠습니다.
추가내용을 봐서는 성능혹은 버전 이슈로 보이는데요..? 그에 맞게 대응하셔야하는 일반적인 상항이고 딱히 논리 코드 이슈로는 생각이 들지 않습니다만...
...