So, I used this Android sample as a guide to make a bluetooth connection without any kind of validation (This app will have a very restrict userbase and will not be available to download at the store).
I was able to transfer string just fine, and it works like a charm. My problem is when trying to transfer images.
I have one activity that sends the byte[] of the image to the bluetooth service and a handler on the other activity that recieves the message and do wharever with the said message.
The thing is, because of the size of the buffer the handler receives parts of the original byte[]. What I'm tryng to do is to merge all the parts in one byte and save it.
This is the loop that I do inside my handler:
byte[] result = new byte[originalByteSize];
byte[] readBuf = (byte[]) msg.obj;
if (cont < byteTimes){
if (result == null) {
result = appendData(readBuf,readBuf);
} else {
result = appendData(result,readBuf);
}
} else {
new SavePhotoTask(cont).execute(result);
}
This is the appendData function
protected byte[] appendData(byte[] firstObject,byte[] secondObject){
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
try {
if (firstObject!=null && firstObject.length!=0)
outputStream.write(firstObject);
if (secondObject!=null && secondObject.length!=0)
outputStream.write(secondObject);
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
And here is where I write the file:
public class SavePhotoTask extends AsyncTask<byte[], String, String> {
int counter = 0;
public SavePhotoTask(int cont){
this.counter = cont;
}
#Override
protected String doInBackground(byte[]... jpeg) {
File photo = new File(Environment.getExternalStorageDirectory(), counter + "_photo.jpg");
if (photo.exists()) {
photo.delete();
}
try {
FileOutputStream fos = new FileOutputStream(photo.getPath());
fos.write(jpeg[0]);
fos.close();
} catch (java.io.IOException e) {
Log.e("PictureDemo", "Exception in photoCallback", e);
}
return (null);
}
What I needed is just a tip in the right direction, thanks.
I solved my problem with this answer
The problem was in the way I was writing and reading the stream.
public static void writeItem(OutputStream out, String s) throws IOException
{
// Get the array of bytes for the string item:
byte[] bs = s.getBytes(); // as bytes
// Encapsulate by sending first the total length on 4 bytes :
// - bits 7..0 of length
out.write(bs.length); // modulo 256 done by write method
// - bits 15..8 of length
out.write(bs.length>>>8); // modulo 256 done by write method
// - bits 23..16 of length
out.write(bs.length>>>16); // modulo 256 done by write method
// - bits 31..24 of length
out.write(bs.length>>>24); // modulo 256 done by write method
// Write the array content now:
out.write(bs); // Send the bytes
out.flush();
}
public static String readItem(InputStream in) throws IOException
{
// first, read the total length on 4 bytes
// - if first byte is missing, end of stream reached
int len = in.read(); // 1 byte
if (len<0) throw new IOException("end of stream");
// - the other 3 bytes of length are mandatory
for(int i=1;i<4;i++) // need 3 more bytes:
{
int n = in.read();
if (n<0) throw new IOException("partial data");
len |= n << (i<<3); // shift by 8,16,24
}
// Create the array to receive len bytes:
byte[] bs = new byte[len];
// Read the len bytes into the created array
int ofs = 0;
while (len>0) // while there is some byte to read
{
int n = in.read(bs, ofs, len); // number of bytes actually read
if (n<0) throw new IOException("partial data");
ofs += n; // update offset
len -= n; // update remaining number of bytes to read
}
// Transform bytes into String item:
return new String(bs);
}
Related
I am working on a project that involves communication between an Android Uno and an Android phone. The phone sends a request signal "*" that once received, the Arduino sends random integers in a loop. Right now, the Android device is receiving the message but it is showing up as boxed question marks, and not receiving all of the messages. Any ideas? Thank you so much!
Arduino code:
#include <SoftwareSerial.h>
const int RX_PIN = 0;
const int TX_PIN = 1;
SoftwareSerial bluetooth(RX_PIN, TX_PIN);
char commandChar;
void setup (){
bluetooth.begin (9600);
Serial.begin(38400);
}
void loop () {
if(bluetooth.available()){
commandChar = bluetooth.read();
switch(commandChar){
case '*':
Serial.println("Got the request code");
for(int i = 0; i < 10; i++){
bluetooth.print(random(21));
}
break;
}
}
}
Android code:
public void run() {
initializeConnection();
byte[] buffer = new byte[256];
int bytes;
// Keep looping to listen for received messages
while (true) {
try {
bytes = mmInStream.read(buffer);//read bytes from input buffer
String readMessage = new String(buffer, 0, bytes);
Log.e("Received Message: ", readMessage);
} catch (IOException e) {
break;
}
}
}
public void initializeConnection() {
try {
PrintWriter out;
out = new PrintWriter(mmOutStream, true);
out.println("*");
out.flush();
}catch (NullPointerException NPE) {
}
}
Console output:
08-13 19:02:46.546 4019-4128/? E/Received Message:: �
08-13 19:02:46.596 4019-4128/? E/Received Message:: ����
Ah I think I spot the problem. Random numbers are being sent from the arduino to the app, and the app is logging these bytes as ascii literals. Instead of sending random numbers, try sending well-formed ascii (visual characters).
You can send the hex bytes [0x68,0x65,0x6c,0x6c,0x6f] for "hello", or use SoftwareSerialPrint's built-in HEX option.
So change it to this, see if that works.
bluetooth.print(random(21), HEX);
Edit:
Let's try this on the app side instead. This will convert the received bytes into a hexadecimal string representation so we can see it in ascii properly.
bytes = mmInStream.read(buffer);//read bytes from input buffer
StringBuilder sb = new StringBuilder(bytes * 2);
for(byte b: buffer)
sb.append(String.format("%02x", b));
Log.e("Received Message: ", sb.toString());
I need to monitor an InputStream for '\n'. Once the '\n' is found at the end of the InputStream, the code will return all the InputStream buffer back to UI. I have tried several methods without luck.
Here is my pseudo code.
public void run() {
byte[] buffer = new byte[256];
int bytes;
// Keep listening and till InputStream has '\n'
try {
while (true) {
// Read from the InputStream some code here to check the occurrence of '\n';
break;
}
SystemClock.sleep(1);
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
// Some code here to handle exception
}
}
I am trying to write an app that will log the output of an arduino Due to a text file on the phone or tablet. The output rate is 1kHz. I have based my app on the Blueserial code (https://github.com/plastygrove/BlueSerial). The bluetooth connection gets established properly with the arduino bluetooth module, the commands are sent and received properly and everything seems to work just fine. However, the file that I am saving the data to is missing blocks of data, usually around 200ms worth every so often (I have a millisecond timestamp included in my data), resulting in corrupted data. I have been trying to figure out the source of the problem and I think it might be related to the gc but at this point I am at a loss. This is the code that writes my data to the file:
private class ReadInput implements Runnable {
private boolean bStop = false;
private Thread t;
public ReadInput() {
t = new Thread(this, "Input Thread");
t.start();
}
public boolean isRunning() {
return t.isAlive();
}
public void run() {
InputStream inputStream;
try {
inputStream = mBTSocket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(inputStream);
while (!bStop) {
byte[] buffer = new byte[250];
int bytes = 0;
if (bis.available() > 0) {
bytes = bis.read(buffer);
strInput = new String(buffer, 0, bytes);
sb.append(strInput);
int endOfLineIndex = sb.indexOf("\r\n"); // determine the end-of-line
pw.print(strInput); // print buffer to the file buffer
pw.flush(); // flush buffer and force write to media
if (endOfLineIndex > 0) { // if end-of-line,
String sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length()); // and clear the string
pw.print(strInput); // write buffer to file buffer
pw.flush(); // force writing to file
pw.close(); // close print writer
try {
f.close(); // close file output stream
} catch (IOException e) {
e.printStackTrace();
}
}
sb.delete(0, sb.length()); strInput = "";
}
//Thread.sleep(100);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void stop() {
bStop = true;
}
}
and this is my file outputsteam and printwriter declarations:
String strInput = null;
static PrintWriter pw = null;
static FileOutputStream f = null;
private StringBuilder sb = new StringBuilder();
The data I am sending is formatted as so:
24.330,-58,5,119,460\n
24.331,-86,25,-105,460\n
24.332,66,41,-145,460\n
24.333,90,-23,-85,4622,-7,119,460\n
24.524,6,-95,107,461\n
24.525,10,-7,-173,461\n
24.526,-22,33,103,461\n
and in this example you can see where it skipped some data. Thank you for helping out!
There appears to be code missing or you have some extra logic.
For example the StringBuilder doesn't appear to be doing anything,
bStop is never set/cleared. But you are always printing out the incoming data
via strInput.
The end of line handling also looks off, specifically this:
String sbprint = sb.substring(0, endOfLineIndex); // extract string
sb.delete(0, sb.length());
You extract the string and then delete the whole buffer sb.delete(0,sb.length())
same at the bottom of the loop.
From my Android phone, I'm trying to read (using Bluetooth) incomming strings from an external GPS device. I've followed mainly the BluetoothChat example and everything seems to work as expected so far. My reading thread is executing and I can see variable bytes packets incoming when looping with the following code:
Log.d(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true)
{
try
{
bytes = mmInStream.read(buffer);
// Test...
String strReadBuf = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothHandler.MessageType.READ,
bytes, buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
sendErrorMessage(R.string.bt_connection_lost);
break;
}
}
The strings I'm supposed to read are text strings (NMEA format) but I'm reading only 0 and -32 bytes in my buffer array. Any idea why I'm getting this?
Log.d(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true)
{
try
{
bytes = mmInStream.read(buffer);
// Test...
//String strReadBuf = new String(buffer, 0, bytes);
//I've changed for
String strReadBuf = new String(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothHandler.MessageType.READ,
bytes, buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
sendErrorMessage(R.string.bt_connection_lost);
break;
}
}
I used the String constructor String(byte[]), where byte[] is the buffer deprecating the byte[] size. I've used many times and that works for me even if the buffer size changes over time.
I have a problem related to Memory Leak. In my app I have to read 2MB data from a Video file, and the method related to same always called when the Activity's onCreate method called then the same statement which allocated 2MB byte array in the code, returns OutofMemory Exception frequently after 10 to 15 attempts because heap memory exceeds. The code is explained below (it is the part of my whole code):
//Reading DRM video from sdcard
File file = new File("/sdcard/TvAnyTime/watch/"+IDValue+".mp4");
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e2) {
e2.printStackTrace();
}
//reading 2^21 bytes
fileData = new byte[2097152];
int read = 0;
while(read != fileData.length) {
try {
read += is.read(fileData, read, fileData.length - read);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//The finalHashPattern is then xored across the video file 2^10 times
for(int i=0;i<2097152;i+=2048)
{
byte[] res = new byte[2048];
bytesafterXor = new byte[2048];
for(int j=0;j<2048;j++)
{
res[j] = fileData[i+j];
bytesafterXor[j] = (byte)(res[j]^finalhash[j]);
finaldatafile[i+j] = bytesafterXor[j];
}
finalHashafterXor.add(bytesafterXor);
}
The statement fileData = new byte[2097152]; is responsible for the OutOfMemory Exception because it is allocated every time when onCreate is called. Can we prevent the same by allocating each time a large memory? can we read it in chunks of data? Please suggest me the right solution regarding the same.
Thanks in advance.
Have you considered processing the input inside the loop where your reading the bytes in, rather than reading all of the bytes
fileData = new byte[2048];
int read = 0;
while(read != fileData.length) {
try {
read += is.read(fileData, read, fileData.length);
for(int i = 0; i < 2048; i++) {
// Processing here
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
You may put fileData as a static pointer an then in case onCreate alloc memory only once, when fileData==NULL;
Ok, since my edit to Kingamajick's answer "mysteriously" disappeared while waiting for peer review, here is how to do file (or streams in general) chunked reading right:
fileData = new byte[2048];
try {
int c = 0;
while( (c = is.read(fileData)) >= 0 ) {
// Process all bytes from fileData[0] through fileData[c-1], e.g.:
for(int i = 0; i < c; i++) {
// do something with fileData[i]
// ...
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Note that for files, the buffer (fileData above) will most likely always be completely filled by the read operation until there are not enough bytes left to read in the file. When reading from, for instance, network streams this is usually not the case as data is not always instantly available and when it becomes available it is likely just as much data as arrived in the last network packet. However, the above approach works for all streams.
Edit:
From your comment I take that you do not want to process the whole file but only the first 2mb. In this case you can modify the above approach a bit, like:
fileData = new byte[2048];
int leftToRead = 2048*1024; // Total amount of bytes you want to read.
try {
int c = 0;
// How many bytes may we read at once?
int maxRead = Math.min( fileData.length, leftToRead );
while( (leftToRead > 0) && (c = is.read(fileData, 0, maxRead)) >= 0 ) {
// Process all bytes from fileData[0] through fileData[c-1], e.g.:
for(int i = 0; i < c; i++) {
// do something with fileData[i]
// ...
}
// We read c bytes, so we may have some bytes left:
leftToRead -= c;
// How many bytes may we read at once?
maxRead = Math.min( fileData.length, leftToRead );
}
// Optionally:
if ( leftToRead > 0 ) {
System.out.println("Premature end of file.");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
When processing the file byte-by-byte, as you do in your code excerpt, you can basically freely choose the size of the fileData buffer. A smaller buffer does not provide any real benefit, because it may cause more read operations on the underlying file system. Something in the range from 1kb to 64kb is usually a good size.
You can exclude this critical file processing from Java code to Native code.
http://marakana.com/forums/android/examples/49.html
http://android.wooyd.org/JNIExample/files/JNIExample.pdf