2
\$\begingroup\$

I'm developing and app that communicates via Bluetooth with another PCB. It consists of 3 seekbars that regulates the value of 3 parameters of the PCB.

The structure is this:

  1. Send petition string for read the first value
  2. Receive the first value
  3. Send petition string for read the second value
  4. Receive the second value
  5. Send petition string for the third value
  6. Receive the third value

The first thing that I want to do, but I don't know how is, when it enters on an if, if this is true, then it must continue executing the code. But if it isn't true, then it should get out of the function and stop executing the code. But the way I've done it, it continues executing.

Then, other times it crashes because the way I implemented it to wait 1 second between each communication I know that isn't the best way, and few times instead of doing it right this way:

  1. Send petition string for read the first value
  2. Receive the first value
  3. Send petition string for read the second value
  4. Receive the second value

It does something like:

  1. Send petition string for read the first value
  2. Send petition string for read the second value
  3. Receive the first value

Here I receive a null string and it crashes. Now I've implemented a condition to check that is there any null string, but the solution is that I never get any null string.

When I press the "read values" button, this is what it happens:

public void receiveValues() {       

    /**Petition string that is sent to the PCB to request the variable's value*/
    final String message_full1 = 2b e1 b4 e9 ff 1f b5;  //variable 1
    final String message_full2 = 2b e1 b8 e9 ff 1f b3;  //variable 2
    final String message_full3 = 2b e1 bc e9 ff 1f b1;  //variable 3
    final String message_full4 = 2b e0 bc f3 ff 1f 7c;  //save request
    final String message_full5 = 2b e0 be f3 ff 1f 7a;  //save status


    /*Send the first string to the PCB*/
    byte[] send1 = message_full1.getBytes();
    GlobalVar.mTransmission.write(send1);


    /**Delay of 1 second to let some time to receive the confirmation*/
    read1_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**Read write confirmation after 1s = 1000ms*/
            String inpuRead = "2b 00 ff fe c7 80";  //This string is what I receive as an answer via bluetooth

            /**We check that the received string is not null, to avoid program crash*/
            if (inpuRead != null) { //|| !inpuRead.equals("")) {

                /*If it nos null, then we call the next function*/
                int splitInt = splitReceivedString (inpuRead);  //This function is declared below and extracts from the string , only the chars that we need.
                receive1 = splitInt;
                Toast.makeText(getApplicationContext(), "Loading values", Toast.LENGTH_LONG).show();
            }
            else {
                Toast.makeText(getApplicationContext(), "Communication error", Toast.LENGTH_SHORT).show();
            }
        }
    }, 1000);


    /**Delay to wait to send de second petition string*/
    write2_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**write message 2 after 1s = 1000ms*/
            byte[] send2 = message_full2.getBytes();
            GlobalVar.mTransmission.write(send2);
        }
    }, 2000);

    /**Delay of 1 second to let some time to receive the confirmation*/
    read2_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**Read write confirmation after 1s = 1000ms*/
            String inpuRead = "2b 00 ff fe c7 80";

            if (inpuRead != null) {
                int splitInt = splitReceivedString (inpuRead);
                receive2 = splitInt;
            }
            else {
                Toast.makeText(getApplicationContext(), Communication error, Toast.LENGTH_SHORT).show();
            }
        }
    }, 3000);

    /**Delay to wait to send de third petition string*/
    write3_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**write message 3 after 1s = 1000ms*/
            byte[] send3 = message_full3.getBytes();
            GlobalVar.mTransmission.write(send3);
        }
    }, 4000);

    /**Delay of 1 second to let some time to receive the confirmation*/
    read3_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**Read write confirmation after 1s = 1000ms*/
            String inpuRead = "2b 00 ff fe c7 80";

            if (inpuRead != null) {
                int splitInt = splitReceivedString (inpuRead);
                receive3 = splitInt;

                /**Set loaded values on seekbars*/
                bar1.setProgress(receive1);
                bar2.setProgress(receive2);
                bar3.setProgress(receive3);

                /**Message indicating the end of the transmission*/
                Toast.makeText(getApplicationContext(), "Values loaded!", Toast.LENGTH_SHORT).show();
            }
            else {
                Toast.makeText(getApplicationContext(), "Communication error", Toast.LENGTH_SHORT).show();
            }
        }
    }, 5000);

    /**This makes a save request on the pCB*/
    write4_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**write message 3 after 1s = 1000ms*/
            byte[] send4 = message_full4.getBytes();
            GlobalVar.mTransmission.write(send4);
        }
    }, 6000);

    /**This request a save statos on the PCB*/
    write5_handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            /**write message 3 after 1s = 1000ms*/
            byte[] send5 = message_full5.getBytes();
            GlobalVar.mTransmission.write(send5);

            /**Reset out string buffer to zero*/
            GlobalVar.mOutStringBuffer.setLength(0);
        }
    }, 7000);
}


/**
 * FUNCTION THAT SPLITS THE RECEIVED STRING TO GET THE DESIRED VALUES
 */

private int splitReceivedString (String s) {  //For example, s = 2b 00 ff fe c7 80

    StringTokenizer tokens = new StringTokenizer(s," ");
    String one = tokens.nextToken();
    String two = tokens.nextToken();
    String three = tokens.nextToken();
    String four = tokens.nextToken();
    String five = tokens.nextToken();
    String six = tokens.nextToken();

    /**The next strings are whose got the seekbar's value*/  //f.e: "fffec780"
    received_hexValue = three + four + five + six;
    received_hexValue = received_hexValue.trim();


    /**Conversion from hex to int to set the seekbar's values*/
    int_value_receive = (int)Long.parseLong(received_hexValue, 16);
    int_value_receive = -200000 - int_value_receive;
    newIntValue = (int_value_receive * 100) / (200000 * (-1));

    return newIntValue;  //For this hex value, the int value to introduce in the seekbar is "60"
}

One user on Stack Overflow has recommended me to write the code this way more clearly, and not to set a 1 second wait time because isn't the best way to do it. But I have to do something like this, because I don't have a condition to set. When i send the petition string to have the value returned, the condition would be to have this value returned, but If I don't set a time to wait between the petition sent and the returned string, mostly for sure that I'm getting a null string from the Bluetooth's input socket.

Proposed code:

private Handler handler = new Handler();

//TODO: don't call functions read1, write2 etc, call it something like     "readSomeValue" where "SomeValue" is what you're trying to read
private void read1() throws IOException {
    String inpuRead = "2b 00 ff fe c7 80";  //This string is what I receive as an     answer via bluetooth
    if (inpuRead != null) { //|| !inpuRead.equals("")) {
        int splitInt = splitReceivedString (inpuRead);
        receive1 = splitInt;
        Toast.makeText(getApplicationContext(), "Loading values",     Toast.LENGTH_LONG).show();
    }
    else {
        throw new IOException("Error in read1");
    }
}

private void write2() {
    byte[] send2 = message_full2.getBytes();
    GlobalVar.mTransmission.write(send2);
}

private void read2() throws IOException {
    String inpuRead = "2b 00 ff fe c7 80";
    if (inpuRead != null) {
        int splitInt = splitReceivedString (inpuRead);
        receive2 = splitInt;
    }
    else {
        throw new IOException("Error in read2");
    }
}

public void receiveValues() {
    handler.post(new Runnable() {
        @Override
        public void run() {
            try {
                read1();
                read2();
                read3();
            } catch (IOException e) {
                Toast.makeText(getApplicationContext(), "Communication error! " +     e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
     });
}
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

It is quite hard to see what you are trying to achieve from your code. I think that you have two separate applications communicating over Bluetooth and that is working. However the way that you are coding the read/write methods is never going to work. Whoever said not to code the 1 second wait is entirely right, it might work if the planets are aligned, but it is a poor solution.

If you need messages to be sent received in sequence I think that you need to develop something much more smart. You should already have a Thread controlling your reading and writing of data to the Input/Output streams - probably mTransmission. I would consider adding a flag or sequence variable to each message - so you are no longer writing a String, you are writing a String that identifies itself as something, say message2. Importantly reading and writing to streams are both blocking operations, so you can use this to drive triggering the next message - it is critical that this all happens in a separate thread or you application will hang. Your logic is now more like this:

  1. Devices paired, sockets opened.
  2. App 1 - Send petition Object (string, seq) to request the first value.
  3. App 1 - Trigger read in anticipation of a response (set socket timeout to a sensible value).
  4. App 1 - Receive something.
  5. App 1 - Validate received object is what you were expecting, process.
  6. App 1 - Rinse, repeat for other messages in sequence.

All the while on your other application

  1. Devices paired, sockets opened.
  2. App 2 - Socket with long timeout set to read in anticipation of message.
  3. App 2 - Reads first message.
  4. App 2 - Checks seq number to determine what to do with data.
  5. App 2 - Writes data back with new seq number.
  6. App 2 - Goto 1.

Applications should be coded to handle out of sequence messages, but I suspect Application 2 doesn't even care, if you were to repeatedly send request 1 it should just keep replying with response 1.

In order to not have code hanging around waiting for each of these reads you should consider using a callback or listener which you can pass into your mTransmission object. When a read is successful or times out this can then be communicated back to the calling class.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for your answer. I have to say that I can only work with this app, the aplication on the PCB is done by other person and I can't modify it. The app i'm doing is based on the "BluetoothChat example", but I modified to work sending the value of 3 seekbar's instead of being a chat app. I'm not an expert android developer, I'm just starting with it. I would have liked to learn some of java before starting this project, but I haven't had time and I'm learning on the way. So I now that is hard work, but I would apreciate a lot an example of what you are proposing to do. \$\endgroup\$
    – user29368
    Sep 9, 2013 at 7:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.