2017년 8월 8일 화요일

Receiving and Combining Multiple Byte


Hi.  I am new on this forum although I have been surfing it for a while.  New to the MIT App Inventor program although it is quite intuitive to use.

My question.  I am developing a weighing system that has a load cell connected to an Arduino Uno that connects to Android via  Bluetooth (HC-05 module).  The values I am trying to bluetooth from the Arduino are from 0 to around 550.  The Arduino will only transmit 1 byte at a time so I have been experimenting with various ways to get the correct value into MIT AI.  At the moment within the Arduino code I have changed the 2 byte value into an array of 2 1 byte values (basically cut the 16 bits in half to create 2 1 byte values).  This then gets tranmitted as an array.

My problem is that no matter what I have tried in MIT AI I cannot put the correct number into a field (I need a number not text BTW as I will need to do some maths on the number).  The attached file shows my current code - I have 2 text fields called DataByte1 and DataByte2 that I have used to experiment with 2 options to get the right number.  The first simply reads the bluetoothed value.  The second I was looking at whether I could sum or join the 2 separate bytes.

When trying to send a value of 380, DataByte1 returns a value of 123.  By splitting the binary number for 380 in Arduino (0000 0001 0111 1100) I get 2 separate binary numbers being 1 (0000 0001) and 124 (0111 1100).  MIT AI seems to take 1 from 124 to get its value of 123.  DataByte2 returns a value of 246 which is obviously the 123 that it used for DataByte1 added to itself.

Can somebody please let me know how to take a large number from the Arduino and enter it as a value in an MIT AI field or specifically how to use the 2 1-byte numbers that I am receiving??  Spent several days on this already


--
see this example how to receive something correctly

--
On the Arduino side, which command are you using to send the numbers?
Write() or Println()?

Keeping the data as a 16 bit integer on the Arduino side and sending it via a print() statement will eliminate the need to do math.
Receive the data as text in AI2, and AI2's math blocks can handle the text to numeric conversion automatically as needed.

Could you post your Arduino code here?

Also, there have been some HC-05 samples posted on this board in the past.
See this FAQ or use the board's search box for HC05.

--
I looked at your blocks.

It looks like you confused the blue '+' block with concatenation?

Also, you were reading Byte 1 twice and Byte 2 once?
What will that do to your position in the input buffer?

The formula for converting two consecutive  unsigned numbers into one number would be:
(256 * Byte1) + Byte2.

But how would you know if you had slipped down the buffer by 1 byte, so that you would be
receiving Byte2Byte1Byte2Byte1... instead of Byte1Byte2Byte1Byte2?

The solution to that would be to send text, with  delimiter byte (LF works nicely.)
See the samples in the FAQ.

--
Thanks for the replies.  I have got this working but it is slow although slow is not necessarily a nagative thing as this project relates to weighing a car so I don't require multiple updates per second.  Not sure yet if the speed thing is on the Arduino side or the Android side.  I know my code is not well written and could possibly do with the use of a few loops although I am not sure if that would speed things up.  I will investigate that side of things more.

I am using the BTserial.write() command in the Arduino code as I didn't realise that the Android code would auto convert a text string to a number.  After my Arduino code has done some calculations on values pulled in from 4 different sensors I have this as the output code at the end of the main loop() (I am still using Serial.print so that I can see what is being sent via Bluetooth in the serial monitor.  This will be deleted once it all works) 12 variables each of 1-byte going to Android and 16 variables to the Arduino serial monitor!!!;

_______________________

// SEND DATA TO SERIAL MONITOR FOR TESTING PURPOSES ONLY (DELETE IN FINAL VERSION)
  Serial.println("Readings: ");
  Serial.print("FL:  ");
  Serial.print(weight_FL);
  Serial.print("    ");
  Serial.print(DataSentFL1);
  Serial.print("    ");
  Serial.print(DataSentFL2);
  Serial.print("    ");
  Serial.println(DataSentFL3);
  Serial.print("FR:  ");
  Serial.print(weight_FR);
  Serial.print("    ");
  Serial.print(DataSentFR1);
  Serial.print("    ");
  Serial.print(DataSentFR2);
  Serial.print("    ");
  Serial.println(DataSentFR3);
  Serial.print("RL:  ");
  Serial.print(weight_RL);
  Serial.print("    ");
  Serial.print(DataSentRL1);
  Serial.print("    ");
  Serial.print(DataSentRL2);
  Serial.print("    ");
  Serial.println(DataSentRL3);
  Serial.print("RR:  ");
  Serial.print(weight_RR);
  Serial.print("    ");
  Serial.print(DataSentRR1);
  Serial.print("    ");
  Serial.print(DataSentRR2);
  Serial.print("    ");
  Serial.println(DataSentRR3);


  // send data via Bluetooth - flush ensure all data is sent before continuing
  BTserial.write(DataSentFL1);
  BTserial.flush();
  BTserial.write(DataSentFL2);
  BTserial.flush();
  BTserial.write(DataSentFL3);
  BTserial.flush();
  BTserial.write(DataSentFR1);
  BTserial.flush();
  BTserial.write(DataSentFR2);
  BTserial.flush();
  BTserial.write(DataSentFR3);
  BTserial.flush();
  BTserial.write(DataSentRL1);
  BTserial.flush();
  BTserial.write(DataSentRL2);
  BTserial.flush();
  BTserial.write(DataSentRL3);
  BTserial.flush();
  BTserial.write(DataSentRR1);
  BTserial.flush();
  BTserial.write(DataSentRR2);
  BTserial.flush();
  BTserial.write(DataSentRR3);
  BTserial.flush();

  delay(2000);
___________________

In MIT AI2 I have worked out that for each loop of the Arduino, it is presenting multiple 1-byte pieces of data.  The MIT AI reads each one, processes it then reads the next.  I have therefore written code to receive each one into a variable on which some maths can be performed.  My current MIT AI project is displaying each piece of data received as well as the calulated numbers as I want to have visibility of what is received for testing purposes.  It is VERY clunky!!!.  I have attached pisctures of the main clock loop from AI2.

It works, but now it is time to see if I can make it work a little quicker.


--
I need to clarify my reply above as upon reflection it is lacking.

The Arduino is currently taking a measurement from a scale and then converting that number into 3 separate 1-byte numbers;

if (weight_FL > 255) {
    if (weight_FL > 510){
      DataSentFL1 = weight_FL - 510;
      DataSentFL2 = 255;
      DataSentFL3 = 255;
    }
    else {
      DataSentFL1 = weight_FL - 255;
      DataSentFL2 = 255;
      DataSentFL3 = 0;
    }
  }
  else {
      DataSentFL1 = weight_FL;
      DataSentFL2 = 0;
      DataSentFL3 = 0;
  }

i.e. a large number up to 765 gets split up so that the Arduino can send each number/variable as a single unit rather than me having to concetenate in AI2.  The downside is that 4 variables become 12.

Now my understanding of AI2 is that when looking for information via Bluetooth if I tell it to "ReceiveUnsigned1ByteNumber" that it will look in the buffer and take the first available byte (clearing that byte out of the buffer as it reads it).  When I ask it to "ReceiveUnsigned1ByteNumber" again it takes the next byte and so on.  Providing I know the order that the Arduino is transmitting each byte I should be able to allocate the correct bytes to the correct AI2 vaiable?  I see your point Abraham about the bytes getting out of sync and I have perhaps seen evidence of that occuring in this mornings testing :(.  Not sure how I would get around this unless the Arduino pre-fixed each bit of data with an identifier?

I will start working on test code to use strings instead as you suggested just to see what happens.....

--
Have now changed the Arduino code to use strings.  I now have it working great.  Thanks for the help.

--
OK I now have a new issue that I have spent hours researching without luck....

I am trying to send numbers from AI2 to Arduino via Bluetooth.  I am sending two numbers.  The first will always be 1 to 4 so a simple 1-byte number.  The second is a number that varies between 0 and 500.  I have a small routine to break this number down into 2 1-byte numbers with a zero being sent if the number is less than 256.  All good so far.  Block stuff attached.  Bluetooth is set to CharacterEncoding UTF-8 and DelimeterByte 0 (not sure what this second variable means).

When this information is being sent to the Arduino the "/" delimeters are showing as UTF-8 47 so trying to send the numbers 1 and 245 shows in Arduino as 147047245.  I cannot get the Arduino to parse this data as I never know how long the second value (0 in this example) will be and therefore where the "/" delimeters will be.

I am lost.  How can I send these two numbers from AI2 to Arduino via Bluetooth such that Arduino can read them????

--
The Blocky thing.


--
The key to this problem is to figure out how to convert string format numbers into internal number format on the Arduino (receiving) side.

Once you have settled on a all-text transmission format, that frees you up to insert non-numeric characters like "/" or "," between the numbers in your messages.

It also makes it possible to signal the end of a message by adding a special character like Line Feed (decimal 10) at the end of a message, to serve as a Delimiter.

Let's see what we get when we Google "Arduino text to integer"...

Looks good to me!

--

Sending Simple Data to Other Apps


When you construct an intent, you must specify the action you want the intent to "trigger." Android defines several actions, including ACTION_SENDwhich, as you can probably guess, indicates that the intent is sending data from one activity to another, even across process boundaries. To send data to another activity, all you need to do is specify the data and its type, the system will identify compatible receiving activities and display them to the user (if there are multiple options) or immediately start the activity (if there is only one option). Similarly, you can advertise the data types that your activities support receiving from other applications by specifying them in your manifest.
Sending and receiving data between applications with intents is most commonly used for social sharing of content. Intents allow users to share information quickly and easily, using their favorite applications.
Note: The best way to add a share action item to an ActionBar is to use ShareActionProvider, which became available in API level 14. ShareActionProvider is discussed in the lesson about Adding an Easy Share Action.
Send Text Content


Figure 1. Screenshot of ACTION_SENDintent chooser on a handset.
The most straightforward and common use of the ACTION_SEND action is sending text content from one activity to another. For example, the built-in Browser app can share the URL of the currently-displayed page as text with any application. This is useful for sharing an article or website with friends via email or social networking. Here is the code to implement this type of sharing:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(sendIntent);
If there's an installed application with a filter that matches ACTION_SEND and MIME type text/plain, the Android system will run it; if more than one application matches, the system displays a disambiguation dialog (a "chooser") that allows the user to choose an app.
However, if you call Intent.createChooser(), passing it your Intent object, it returns a version of your intent that will always display the chooser. This has some advantages:
  • Even if the user has previously selected a default action for this intent, the chooser will still be displayed.
  • If no applications match, Android displays a system message.
  • You can specify a title for the chooser dialog.
Here's the updated code:
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
The resulting dialog is shown in figure 1.
Optionally, you can set some standard extras for the intent: EXTRA_EMAILEXTRA_CCEXTRA_BCCEXTRA_SUBJECT. If the receiving application is not designed to use them, it simply ignores them.
Note: Some e-mail applications, such as Gmail, expect a String[] for extras like EXTRA_EMAIL and EXTRA_CC, useputExtra(String, String[]) to add these to your intent.
Send Binary Content

Binary data is shared using the ACTION_SEND action combined with setting the appropriate MIME type and placing the URI to the data in an extra named EXTRA_STREAM. This is commonly used to share an image but can be used to share any type of binary content:
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
shareIntent.setType("image/jpeg");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));
Note the following:
⦁ You can use a MIME type of "*/*", but this will only match activities that are able to handle generic data streams.
⦁ The receiving application needs permission to access the data the Uri points to. The recommended ways to do this are:
  ⚬ Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
  ⚬ Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.
Send Multiple Pieces of Content

To share multiple pieces of content, use the ACTION_SEND_MULTIPLE action together with a list of URIs pointing to the content. The MIME type varies according to the mix of content you're sharing. For example, if you share 3 JPEG images, the type is still "image/jpeg". For a mixture of image types, it should be "image/*" to match an activity that handles any type of image. You should only use "*/*" if you're sharing out a wide variety of types. As previously stated, it's up to the receiving application to parse and process your data. Here's an example:
ArrayList<Uri> imageUris = new ArrayList<Uri>();
imageUris.add(imageUri1); // Add your image URIs here
imageUris.add(imageUri2);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
shareIntent.setType("image/*");
startActivity(Intent.createChooser(shareIntent, "Share images to.."));
As before, make sure the provided URIs point to data that a receiving application can access.