I'm building an Android app that sends and receives values from Arduno, using Bluetooth. The Arduino sends the String "OBJECT_FOUND" when a ultrasonic sensor founds an object near by.
I'm using a code taken from a Tutorial. Sometimes, is possible to see the values sent by arduino while the ultrasonic sensor is in front of an object, but It takes too long to receive the data, and most of the time is not possible to see the sent values. The time is crucial for the functioning of my system, and I would like to receive the data in real time.
Here's the code:
public void beginListenForData() {
final Handler handler = new Handler();
final byte delimiter = 10; //This is the ASCII code for a newline character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stopWorker) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
if (data.equals("OBJECT_FOUND")) {
anyObject = true;
} else {
anyObject = false;
}
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (IOException ex) {
stopWorker = true;
}
}
}
});
workerThread.start();
}
Should I avoid the sending a "big" string, and send only 0's and 1's to increase the proccess?
The lag can be cause by another problem, I'm trying to figured out what's the problem.
Thank you.
I use the common tcp client to receive string messages through TCP.
I want after the reception of a specific message e.g. "XXX" my client to be ready to receive a bmp image.
My server in C++ sends the messages but the client does not receive the image...
After some suggestions .. se below I udated the code...
Here is my code:
TCP client:
public class TCPClient {
private String serverMessage;
public static final String SERVERIP = "192.168.1.88"; //your computer IP
public static final int SERVERPORT = 80;
private OnMessageReceived mMessageListener = null;
private boolean mRun = false;
private PrintWriter out;
private BufferedReader input;
private DataInputStream dis;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TCPClient(OnMessageReceived listener) {
mMessageListener = listener;
}
/**
* Sends the message entered by client to the serveraddress
* #param message text entered by client
*/
public void sendMessage(String message){
if (out != null && !out.checkError()) {
out.println(message);
out.flush();
}
}
public void stopClient(){
mRun = false;
if (out != null) {
out.flush();
out.close();
}
mMessageListener = null;
input = null;
input = null;
input = null;
serverMessage = null;
}
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
Log.e("TCP Client", "C: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, SERVERPORT);
try {
//send the message to the server
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Log.e("TCP Client", "C: Sent.");
Log.e("TCP Client", "C: Done.");
//receive the message which the server sends back
dis = new DataInputStream(socket.getInputStream());
// The buffer reader cannot can't wrap an InputStream directly. It wraps another Reader.
// So inputstreamreader is used.
input = new BufferedReader(new InputStreamReader(dis, "UTF-8"));
Log.d("MyApp","We are here");
//this.input = new DataInputStream(in);
//in this while the client listens for the messages sent by the server
while (mRun) {
Log.d("MyApp", "We are here 2");
serverMessage = input.readLine();
if (serverMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(serverMessage);
Log.d("RESPONSE FROM SERVER", "S: Received Message: '" + serverMessage + "'");
}
if ("XXX".equals(serverMessage)) {
Log.d("MyApp", "We are here 3");
serverMessage = null;
while (mRun) {
WriteSDCard writeSDCard = new WriteSDCard();
writeSDCard.writeToSDFile(serverMessage);
}
}
}
} finally {
socket.close();
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + serverMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
//class at on asynckTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
public class WriteSDCard extends Activity {
private static final String TAG = "MEDIA";
private TextView tv;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//(not needed) setContentView(R.layout.main);
//(not needed) tv = (TextView) findViewById(R.id.TextView01);
checkExternalMedia();
String message =null;
}
/** Method to check whether external media available and writable. This is adapted from
http://developer.android.com/guide/topics/data/data-storage.html#filesExternal */
private void checkExternalMedia(){
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// Can read and write the media
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// Can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Can't read or write
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
tv.append("\n\nExternal Media: readable="
+mExternalStorageAvailable+" writable="+mExternalStorageWriteable);
}
/** Method to write ascii text characters to file on SD card. Note that you must add a
WRITE_EXTERNAL_STORAGE permission to the manifest file or this method will throw
a FileNotFound Exception because you won't have write permission. */
void writeToSDFile(String inputMsg){
// Find the root of the external storage.
// See http://developer.android.com/guide/topics/data/data- storage.html#filesExternal
File root = android.os.Environment.getExternalStorageDirectory();
tv.append("\nExternal file system root: "+root);
// See http://stackoverflow.com/questions/3551821/android-write-to-sd-card-folder
File dir = new File (root.getAbsolutePath() + "/download");
dir.mkdirs();
Log.d("WriteSDCard", "Start writing");
File file = new File(dir, "myData.txt");
try {
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
pw.println(inputMsg);
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i(TAG, "******* File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest?");
} catch (IOException e) {
e.printStackTrace();
}
tv.append("\n\nFile written to "+file);
}
/** Method to read in a text file placed in the res/raw directory of the application. The
method reads in all lines of the file sequentially. */
}
And the server side:
Code:
void sendBMP( int cs, int xs, int ys)
{
int imgdataoffset = 14 + 40; // file header size + bitmap header size
int rowsz = ((xs) + 3) & -4; // size of one padded row of pixels
int imgdatasize = (((xs*3) + 3) & -4) * ys; // size of image data
int filesize = imgdataoffset + imgdatasize;
int i, y;
HTLM_bmp_H HTLM_bmp_h;
HTLM_bmp_h.bmfh.bfSize = filesize;
HTLM_bmp_h.bmfh.bfReserved1 = 0;
HTLM_bmp_h.bmfh.bfReserved2 = 0;
HTLM_bmp_h.bmfh.bfOffBits = imgdataoffset;
HTLM_bmp_h.bmih.biSize = 40;
HTLM_bmp_h.bmih.biWidth = xs;
HTLM_bmp_h.bmih.biHeight = ys;
HTLM_bmp_h.bmih.biPlanes = 1;
HTLM_bmp_h.bmih.biBitCount = 24;
HTLM_bmp_h.bmih.biCompression = 0;
HTLM_bmp_h.bmih.biSizeImage = imgdatasize;
HTLM_bmp_h.bmih.biXPelsPerMeter = 1000;
HTLM_bmp_h.bmih.biYPelsPerMeter = 1000;
HTLM_bmp_h.bmih.biClrUsed = 1 << 24;
HTLM_bmp_h.bmih.biClrImportant = 0;
printf("Start Sending BMP.\n");
send(cs,(unsigned char *)"BM",2,0);
send(cs,(unsigned char *)&HTLM_bmp_h,sizeof(HTLM_bmp_h),0);
printf("Sending...\n");
Buff_ptr = 0;
send(cs, (unsigned char *)Rbuffer, BUFF_SIZE,0 );
send(cs, (unsigned char *)Gbuffer, BUFF_SIZE,0 );
send(cs, (unsigned char *)Bbuffer, BUFF_SIZE,0 );
send(cs, (unsigned char *)"\n",1,0);
send(cs, (unsigned char *)"END\n",4,0);
printf("Done\n\n");
}
typedef struct {
// char bfType1;
// char bfType2;
int bfSize;
short bfReserved1;
short bfReserved2;
int bfOffBits;
} BMFH;
typedef struct {
unsigned int biSize;
int biWidth;
int biHeight;
short biPlanes;
short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BMIH;
typedef struct {
BMFH bmfh;
BMIH bmih;
} HTLM_bmp_H;
main()
{
TSK_Handle tsk_cam;
tsk_cam=TSK_create( (Fxn)TSK_webview, NULL);
TSK_setpri(tsk_cam, 8);
}
char buffer[2048];
Void TSK_webview()
{
int s,cs;
struct sockaddr_in addr; /* generic socket name */
struct sockaddr client_addr;
int sock_len = sizeof(struct sockaddr);
int frame = 0;
LgUns i=0;
int len;
int x = DSKeye_SXGA_WIDTH, y = DSKeye_SXGA_HEIGHT;
DSKeye_params CAM_params = {
....
};
lwIP_NetStart();
/**************************************************************
* Main loop.
***************************************************************/
s = socket( AF_INET, SOCK_STREAM, 0 );
addr.sin_port = htons(80);
addr.sin_addr.s_addr = 0;
memset(&(addr.sin_zero), 0, sizeof(addr.sin_zero));
printf("start\n");
if( bind(s, (struct sockaddr*)&addr, sizeof(struct sockaddr)))
{
printf("error binding to port\n");
return ;
}
printf("xx1\n");
if(DSKeye_open(&CAM_params)) {
printf("xx2\n");
SYS_abort("DSKcam_CAMopen");
printf("xx3\n"); fflush(stdout);}
printf("xx4\n");
while(1==1) {
printf("Waiting for client to be connected ... \n");
listen(s, 10);
cs = accept(s, &client_addr, &sock_len);
printf("Client connected.\n");
send(cs,(unsigned char *)"Server connected\n",17,0);
recv(cs, (unsigned char*)buffer, 17, 0);
switch (*(buffer)){
case 'A' :
...
case 'B' :
...
}
REG32(0xA0000080)=REG32(0xA0000080) - 0x800000; ///Disable stepper controller vhdl Quartus Block
for(frame = 0; frame < 4; frame++){ // Allow AEC etc to settle
SrcFrame=DSKeye_getFrame();
}
printf("Demosaicing of %d x %d image is ongoing \n", x, y);
demosaic(SrcFrame, x, y);
break;
}
printf("Demosaicing completed ...\n");
send(cs,(unsigned char *)"Demosaicing completed\n",22,0);
send(cs,(unsigned char *)"XXX\n",4,0);
sendBMP(cs, x, y);
fflush(stdout);
lwip_close(cs);
}
the send : lwip_send
int lwip_send(int s, void *data, int size, unsigned int flags)
{
struct lwip_socket *sock;
struct netbuf *buf;
err_t err;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%d, flags=0x%x)\n", s, data, size, flags));
sock = get_socket(s);
if (!sock) {
set_errno(EBADF);
return -1;
}
switch (netconn_type(sock->conn)) {
case NETCONN_RAW:
case NETCONN_UDP:
case NETCONN_UDPLITE:
case NETCONN_UDPNOCHKSUM:
/* create a buffer */
buf = netbuf_new();
if (!buf) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) ENOBUFS\n", s));
sock_set_errno(sock, ENOBUFS);
return -1;
}
/* make the buffer point to the data that should
be sent */
netbuf_ref(buf, data, size);
/* send the data */
err = netconn_send(sock->conn, buf);
/* deallocated the buffer */
netbuf_delete(buf);
break;
case NETCONN_TCP:
err = netconn_write(sock->conn, data, size, NETCONN_COPY);
break;
default:
err = ERR_ARG;
break;
}
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d\n", s, err));
sock_set_errno(sock, err_to_errno(err));
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) ok size=%d\n", s, size));
sock_set_errno(sock, 0);
return size;
}
You can't mix a buffered reader and a data input stream on the same socket. The buffered reader will read-ahead and steal data you expect to read via the data input stream. You will have to use the data input stream for everything. And correspondingly at the sender.
You're doing incorrect comparison for string equality.
In Java, string comparison for equality is done using String.equals(Object anObject)
You're using if (serverMessage == "XXX") {....
You should use if ("XXX".equals(serverMessage)) {....
I'm creating an app to read string values over Bluetooth serial port. My data receiving but in two parts. If I send $F00,A,B,0,M# via bluetooth it only reads $ in first part and F00,A,B,0,M# in next part. I provided my code here. Please do correct me if I'm wrong.
InputStream inputStream=null;
int avilableBytes=0;
public ConnectedThread(BluetoothSocket socket){
InputStream temp=null;
try{
temp=socket.getInputStream();
}catch (IOException e){
e.printStackTrace();
}
inputStream=temp;
}
public void run() {
try{
int bytes;
while (true){
try{
avilableBytes=inputStream.available();
if (avilableBytes>0){
byte[] buffer=new byte[avilableBytes];
bytes=inputStream.read(buffer);
final String readMessage=new String(buffer,0,bytes);
bt_handler.obtainMessage(handlerState,bytes,-1,readMessage).sendToTarget();
Log.d("PRAVEEN",readMessage);
}
}catch (IOException e){
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
Data are like stream bytes and can not be processed immediately when it comes with a few bytes. Data will not come all at once as a single packet. You have to use the other byte[] buffer (MainBuffer) in which you will gradually save incoming byte and move the index in that buffer. Then, from time to time (e.g. in the timer once per second) take data from the main buffer and processed it. By default you must implement some data frame with a separator (eg. Data * data * data * - Many ways to do it good or bad). I dealt with this in .net via Xamarin, but just as an example it may be helpfull :
update example, format
In ConnectedThread :
public override void Run()
{
while (true)
{
try
{
int readBytes = 0;
lock (InternaldataReadLock)
{
readBytes = clientSocketInStream.Read(InternaldataRead, 0, InternaldataRead.Length);
Array.Copy(InternaldataRead, TempdataRead, readBytes);
}
if (readBytes > 0)
{
lock (dataReadLock)
{
dataRead = new byte[readBytes];
for (int i = 0; i < readBytes; i++)
{
dataRead[i] = TempdataRead[i];
}
}
Bundle dataBundle = new Bundle();
dataBundle.PutByteArray("Data", dataRead);
Message message = btlManager.sourceHandler.ObtainMessage();
message.What = 1;
message.Data = dataBundle;
btlManager.sourceHandler.SendMessage(message);
}
}
catch (System.Exception e)
{
btlManager.btlState = BTLService.BTLState.Nothing;
}
}
}
In BTLHandler :
public override void HandleMessage(Message msg)
{
switch (msg.What)
{
case 1:
{
byte[] data = msg.Data != null ? msg.Data.GetByteArray("Data") : new byte[0];
btlService.BTLReceiveData(data);
}
break;
}
}
public void BTLReceiveData(byte[] data)
{
lock (dataReadLock)
{
for (int i = 0; i < data.Length; i++)
{
dataRead[dataReadWriteCursor] = data[i];
dataReadWriteCursor++;
}
}
}
In Timer :
int tmpWriteCursor = dataReadWriteCursor;
int tmpReadCursor = dataReadReadCursor;
lock (dataReadLock)
{
int newBytes = dataReadWriteCursor - dataReadReadCursor;
for (int i = 0; i < newBytes; i++)
{
dataReadMain[dataReadReadCursor] = dataRead[dataReadReadCursor++];
}
}
bool odradkovani = false;
string tmpRadek = "";
int lastLineIndex = 0;
List<string> list = new List<string>();
for (int i = LastWriteLineIndex; i < tmpWriteCursor; i++)
{
if (dataReadMain[i] >= 32 && dataReadMain[i] <= 255)
{
tmpRadek += (char)dataReadMain[i];
}
else if (dataReadMain[i] == 13) odradkovani = true;
else if (dataReadMain[i] == 10)
{
if (odradkovani)
{
odradkovani = false;
list.Add(Utils.GetFormatedDateTime(DateTime.Now) + " " + tmpRadek);
tmpRadek = "";
lastLineIndex = i + 1;
}
}
else
{
tmpRadek += "?" + dataReadMain[i].ToString() + "?";
}
}
WriteDataToLog(list);
LastWriteLineIndex = lastLineIndex;
My Arduino is listening for a character to be sent from my phone to print sensor data to send back via a Bluetooth module. This is working fine and the communication is fast and accurate. However, the problem occurs when I request data from two different sensors.
I will request data from sensor A and get my results fine. I then request data from sensor B, and I get what seems to be a leftover request to sensor A and get information from sensor A again. I can request data from sensor B twice in a row, and I will then receive data from sensor B accurately.
I have tried to flush the input stream, but that made my application crash. I also tried to use InputStream.wait() and then interrupt the wait with InputStream.notify() when requesting data from a different sensor. This also crashed the program.
This is my receive code. The character that is being passed in is the character that defines what sensor data to send back from the Arduino.
public String receive(Character c) {
try {
myOutputStream.write(c);
final Handler handler = new Handler();
final byte delimiter = 10; // ASCII code for a newline
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
myThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted()
&& !stopWorker) {
try {
int bytesAvailable = myInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
myInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0,
encodedBytes, 0,
encodedBytes.length);
final String data = new String(
encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
status = data;
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}
}
catch (IOException ex) {
stopWorker = true;
}
}
}
});
myThread.start();
return (status);
}
catch (IOException e) {
// TODO Auto-generated catch block
status = "Failed";
e.printStackTrace();
}
return (status);
}
This is some of the Arduino code, and it is very simple.
if(bluetooth.available()) //If something was sent from phone
{
char toSend = (char)bluetooth.read(); //Reads the char sent
if (toSend == 'T')
{
//If the char is T, turn on the light.
float voltage = analogRead(14) * 5.04;
voltage /= 1024.0;
float temperatureC = (voltage - .5) * 100;
float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
bluetooth.println(temperatureF);
}
How can I fix this problem?
For downloading stuff I work with the apache classes HTTPResponse HTTPClient etc.
I check for a valid download like this:
entity.writeTo(new FileOutputStream(outfile));
if(outfile.length()!=entity.getContentLength()){
long fileLength = outfile.length();
outfile.delete();
throw new Exception("Incomplete download, "+fileLength+"/"
+entity.getContentLength()+" bytes downloaded");
}
But it seems that the exception is never triggered. How to properly handle this? Is entity.getContentLength the length of the file on server or the amount of data received?
The file request should always come with a MD5 checksum. If you have an MD5 header then all you need to do is check that against the files generated MD5. Then your done, its better to do it this way as you can have a file with the same number of bytes but one byte gets garbled in transmission.
entity.writeTo(new FileOutputStream(outfile));
String md5 = response.getHeaders("Content-MD5")[0].getValue();
byte[] b64 = Base64.decode(md5, Base64.DEFAULT);
String sB64 = IntegrityUtils.toASCII(b64, 0, b64.length);
if (outfile.exists()) {
String orgMd5 = null;
try {
orgMd5 = IntegrityUtils.getMD5Checksum(outfile);
} catch (Exception e) {
Log.d(TAG,"Exception in file hex...");
}
if (orgMd5 != null && orgMd5.equals(sB64)) {
Log.d(TAG,"MD5 is equal to files MD5");
} else {
Log.d(TAG,"MD5 does not equal files MD5");
}
}
Add this class to your project:
public class IntegrityUtils {
public static String toASCII(byte b[], int start, int length) {
StringBuffer asciiString = new StringBuffer();
for (int i = start; i < (length + start); i++) {
// exclude nulls from the ASCII representation
if (b[i] != (byte) 0x00) {
asciiString.append((char) b[i]);
}
}
return asciiString.toString();
}
public static String getMD5Checksum(File file) throws Exception {
byte[] b = createChecksum(file);
String result = "";
for (int i = 0; i < b.length; i++) {
result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
}
return result;
}
public static byte[] createChecksum(File file) throws Exception {
InputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
}