I am trying to receive data from Arduino to my Android device. I started from here
In the activity part of the application, they did
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(fromUser){
if(sUsbController != null){
sUsbController.send((byte)(progress&0xFF));
}
}
}
In the send function
public void send(byte data) {
mData = data;
synchronized (sSendLock) {
sSendLock.notify();
}
}
And in the UsbRunnable part
private class UsbRunnable implements Runnable {
private final UsbDevice mDevice;
UsbRunnable(UsbDevice dev) {
mDevice = dev;
}
#Override
public void run() {//here the main USB functionality is implemented
UsbDeviceConnection conn = mUsbManager.openDevice(mDevice);
if (!conn.claimInterface(mDevice.getInterface(1), true)) {
return;
}
// Arduino Serial usb Conv
conn.controlTransfer(0x21, 34, 0, 0, null, 0, 0);
conn.controlTransfer(0x21, 32, 0, 0, new byte[] { (byte) 0x80,
0x25, 0x00, 0x00, 0x00, 0x00, 0x08 }, 7, 0);
...
...
conn.bulkTransfer(epOUT, new byte[] { mData }, 1, 0);
So, App took the progress of the seekbar and sent it to the Arduino.
But, I want my app to receive data from Arduino. I guess I need to use bulktransfer function as well. think that I want to save the data to mData variable.
How can I do that?
Using the bulkTransfer method is the way to go. You will need to use the IN endpoint to receive the data. For instance, to get a byte of data from the Arduino use this:
byte[] reply = new byte[1]; // to store data from Arduino
int size = 1; // receive at most 1 byte of data
int timeout = 100; // try to receive data for up to 100 milliseconds
int count = conn.bulkTransfer(epIN, reply, size, timeout);
if(count < 0) {
Log.d("ArduinoUSB", "Failure occurred when receiving from Arduino");
} else {
Log.d("ArduinoUSB", "Received " + count + " bytes: " + Arrays.toString(reply));
}
The data will be stored in reply.
Related
I wrote an Android Phone app in Android Studio that connects to a UbloX NINA B1 to send and receive data on the SPS Service.
I'm having issues with the incoming data from the NINA B1 (Peripheral). I'm not sure if I connected correctly and turned the notifications on correctly.
Below is the code where I do the connection to the SPS Service. (Hardcoded to select Group 3 and child 0)
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
// Clicked on a Caracteristic (eg. FIFO or Credits)
public boolean enableNotification() {
// Hardcode for SPS service on NINA B112, can only use this service, other services will not be used
// This groupPosition and childPosition was determined from A_05 code, The selectable list gave these values when selecting SPS service
int groupPosition = 3;
int childPosition = 0;
BluetoothGattCharacteristic characteristic;
characteristic = characteristics_HashMapList.get(services_ArrayList.get(groupPosition).getUuid().toString()).get(childPosition);
// Todo find a better way to do below two lines. This is a temp way to pass characteristic & mBTLE_Service to static variables to use in HomeFragment for sending data
//Copy characteristics of SPS service to static act_characteristic for use by HomeFragment
this.act_characteristic = characteristic;
//Copy mBTLE_Service of SPS service to static act_service for use by HomeFragment
this.act_service = mBTLE_Service;
if (Utils.hasWriteProperty(characteristic.getProperties()) != 0) {
String uuid = characteristic.getUuid().toString();
//act_characteristic.setTitle(uuid);
//act_characteristic.setService(mBTLE_Service);
//act_characteristic.setCharacteristic(characteristic);
//dialog_btle_characteristic.show(getFragmentManager(), "Dialog_BTLE_Characteristic");
} if (Utils.hasReadProperty(characteristic.getProperties()) != 0) {
if (mBTLE_Service != null) {
mBTLE_Service.readCharacteristic(characteristic);
}
} if (Utils.hasNotifyProperty(characteristic.getProperties()) != 0) {
if (mBTLE_Service != null) {
mBTLE_Service.setCharacteristicNotification(characteristic, true);
}
}
return false;
}
Here is where I set the notifications ON, this part I'm not so confident about.
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(getString(R.string.CLIENT_CHARACTERISTIC_CONFIG)));
// Todo: if the indications and notifications is to be used, then mode the indications part back into setCharacteristicNotification() and add a check for Indications, then change below code to set both indication and notify
if (enabled) {
// Enable INDICATION & NOTIFICATION
final byte[] ENABLE_INDICATION_NOTIFICATION = {0x03, 0x00};
descriptor.setValue(ENABLE_INDICATION_NOTIFICATION);
}
else {
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
}
//ToDo Shorten the delay time so that the Notifications Enable still works
// Delay in milli seconds
int m_sec = 200;
Delay.delay(m_sec, new Delay.DelayCallback() {
#Override
public void afterDelay() {
// Enable Notifications after 200 m_sec Delay
mBluetoothGatt.writeDescriptor(descriptor);
}
});
}
This is where I handle the incoming Bytes, I send a Modbus message from the Phone to NINA B1, then NINA B1 replies with a Modbus message. The Incoming byte count is always < 20 bytes. And I fill the RxBuff until the correct amount of bytes are received AND the Slave Address is correct, then I decode the received message.
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) {
final Intent intent = new Intent(action);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
intent.putExtra(EXTRA_UUID, characteristic.getUuid().toString());
}
// For all other profiles, writes the data formatted in HEX.
// Only Pack 20 bytes at a time coming from BLE server into data[]
final byte[] rx_Data;
int tempSlavAdr;
rx_Data = characteristic.getValue();
if (rx_Data != null && rx_Data.length > 0) {
// Only handle message if request came from ReadInput_Thread
if (this.request_flag) {
// ToDO: See if other non Modbus data arrives between packets
// Populate RxBuff
for (int i = 0; i < rx_Data.length; i++) {
this.RxBuff[this.WrPtr++] = rx_Data[i];
}
Log.d(TAG,"QTY Bytes in rx_Data = " + (Integer)rx_Data.length);
// Start Decode message only after all expected bytes are received && first message is Slave Address
// Prevent Negative numbers in Slave Address: tempSlavAdr is used in the the next if statement
tempSlavAdr = (byte) this.RxBuff[0];
if (tempSlavAdr < 0) tempSlavAdr += 256;
if ((this.WrPtr == FragmentHome.ExpectedCount_sent) && (tempSlavAdr == slaveAddr)) {
// Copy RxBuff over to Dec_RxBuff
int Dec_RxBuff[] = new int[255];
for (int i = 0; i < this.WrPtr; i++) {
Dec_RxBuff[i] = (this.RxBuff[i]);
Dec_RxBuff[i] += 256;
Dec_RxBuff[i] = Dec_RxBuff[i] & 0xFF;
}
// Clear flag indicating that Thread Send a Request
this.request_flag = false;
message_rx = false;
// Decode ModbusMessage and message Good
if (modbusHandler.DecodeModbusMessage(Dec_RxBuff)){
message_rx = true;
}
// Decode ModbusMessage and message Bad
else{
message_rx = false;
}
// Clear buffers
WrPtr = 0;
Arrays.fill(rx_Data, (byte) 0);
Arrays.fill(RxBuff, (byte) 0);
// Todo What if WrPtr stop short of Expected counter? Do some protection
// WrPtr > ExpectedCount Clear Buffers
} else if (WrPtr > FragmentHome.ExpectedCount_sent) {
// Clear buffers
// HomeFragment.set_Actual(false);
Log.d(TAG,"RX Count to big expected " + FragmentHome.ExpectedCount_sent + " got " + WrPtr);
WrPtr = 0;
Arrays.fill(rx_Data, (byte) 0);
Arrays.fill(RxBuff, (byte) 0);
}
// Todo Use this intent to pass data to a service to Decode Modbus Data (Service not written yet)
// intent.putExtra(EXTRA_DATA, new String(data) + "\n" + Utils.hexToString(data));
}
}
else {
//intent.putExtra(EXTRA_DATA, "0");
}
//sendBroadcast(intent);
}
I still get some data loss, and not sure how to handle this. Phone and NINA B1 is next to one another so distance is not a problem.
Can I get some help on how to set the indications correctly? And why I might lose bytes.
Marinus
I am writing a Xamarin.Android application, but this question is applicable to native Android and BLE in general. I have a write characteristic that I can write to, and it works as long as I don't send more than 600 characters. Anything over 600 characters gets truncated. Looking at my logs, I can see that the text is being split into 20 character packets, and OnCharacteristicWriteRequest is called for each packet, but stops being called after 600 characters. I am testing with 2 Android tablets. My code to write to the characteristic:
public override void OnServicesDiscovered(BluetoothGatt gatt, [GeneratedEnum] GattStatus status)
{
base.OnServicesDiscovered(gatt, status);
try
{
if (status != GattStatus.Success)
{
Log?.Invoke("discover services failed");
return;
}
Log?.Invoke("services discovered");
if(RequestForAddressExists(gatt.Device.Address))
{
lock (_requestsLocker)
{
Java.Util.UUID serviceUuid = GetRequestedServiceUuid(gatt.Device.Address);
Java.Util.UUID characteristicUuid = GetRequestedCharacteristicUuid(gatt.Device.Address);
BluetoothGattCharacteristic characteristic = gatt.GetService(serviceUuid).GetCharacteristic(characteristicUuid);
Log?.Invoke("characterisitic found");
var request = _requests.FirstOrDefault(r => r.DeviceAddress == gatt.Device.Address);
if (characteristic.Properties.HasFlag(GattProperty.Write))
{
Log?.Invoke("writing characteristic...");
string data = ((WriteCharacteristicRequest)request).Data;
characteristic.SetValue($"{data}{Constants.WriteCharacteristicEndDelimiter}");
characteristic.WriteType = GattWriteType.Default;
gatt.WriteCharacteristic(characteristic);
}
else
{
Log?.Invoke("GattProperty not supported");
_requests.Remove(request);
}
}
}
}
catch (Exception e)
{
Log?.Invoke(e.Message);
}
}
public override void OnCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, [GeneratedEnum] GattStatus status)
{
base.OnCharacteristicWrite(gatt, characteristic, status);
if (status != GattStatus.Success)
{
Log?.Invoke($"OnCharacteristicWrite status not success: {status}");
}
else
{
Log?.Invoke("OnCharacteristicWrite success");
}
gatt.Disconnect();
gatt.Close();
lock (_requestsLocker)
{
var r = _requests.FirstOrDefault(x => x.DeviceAddress == gatt.Device.Address);
if (r != null)
{
_requests.Remove(r);
}
}
}
My code to accept the write request:
public override void OnCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, bool preparedWrite, bool responseNeeded, int offset, byte[] value)
{
base.OnCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
Log?.Invoke("OnCharacteristicWriteRequest");
string data = System.Text.Encoding.UTF8.GetString(value);
Log?.Invoke(data);
string characteristicId = new Guid(characteristic.Uuid.ToString()).ToString().ToUpperInvariant();
var record = _writeCharacteristicsReceived.FirstOrDefault(c => c.DeviceAddress == device.Address && c.CharacteristicId.ToUpperInvariant() == characteristicId);
if(record != null)
{
record.Data += data;
}
else
{
record = new CharacteristicWriteReceived()
{
CharacteristicId = characteristicId,
DeviceAddress = device.Address,
Data = data
};
_writeCharacteristicsReceived.Add(record);
}
if (record.Data.EndsWith(Constants.WriteCharacteristicEndDelimiter) == true)
{
Log?.Invoke("end found");
_writeCharacteristicsReceived.Remove(record);
record.Data = record.Data.Substring(0, record.Data.Length - Constants.WriteCharacteristicEndDelimiter.Length); // remove the end delimeter
Log?.Invoke(record.Data);
OnCharacteristicWriteReceived?.Invoke(record);
}
if (responseNeeded)
{
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, 0, value);
}
}
public override void OnExecuteWrite(BluetoothDevice device, int requestId, bool execute)
{
// need to override OnExecuteWrite and call SendResponse here as well,
// since the execute packet corresponds to the last ATT packet that the client sends as a "finish" marker,
// and the client expects a response to know that the server accepted the writes
base.OnExecuteWrite(device, requestId, execute);
BluetoothGattServer.SendResponse(device, requestId, GattStatus.Success, 0, new byte[0]);
}
The funny thing is, even when the text is truncated, I still get status == GattStatus.Success in my OnCharacteristicWrite. Why is it being truncated? Is there a maximum number of packets that can be sent?
Both devices are continuously advertising and scanning on BLE while writing to this characteristic...could that cause a problem?
A characteristic value can only be 512 bytes long per specification. Writing a longer value is not allowed, even if apparently some stacks don't enforce it.
When you write a value longer than what fits in the MTU (default 23 bytes minus 3 for header), the sender Bluetooth stack splits it up in multiple chunks (Prepared Write) and then sends an Execute request to commit. For each chunk you have the offset parameter so you know at which offset to write the current chunk.
I am trying to create an IoT based smart house system. It contains of my android application which sends and receives messages from/to PubNub (subscribes and publishes on certain channels) and Arduino UNO board with some LEDs and movement sensor (PIR). My android app seems to be working fine, it sends and receives messages, but the problem is with Arduino. In the beginning it receives messages from PubNub but after couple (exactly 2) publishes it stops receiving messages, it can just publish. Here is my arduino code:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
char pubkey[] = "<pub key>";
char subkey[] = "<sub key>";
char channel[] = "publish";
char channel1[] = "subscribe";
char uuid[] = "Arduino";
volatile int pirpin = 2;
iotbridge arduino;
void initialize(){
Serial.begin(9600);
Serial.println("Serial set up");
pinMode(6, OUTPUT);
digitalWrite(6, LOW);
pinMode(pirpin, INPUT);
while (!Ethernet.begin(mac)) {
Serial.println("Ethernet setup error");
delay(1000);
}
Serial.println("Ethernet set up");
}
void do_something(String value){
Serial.println("in the callback");
Serial.println(value);
}
void pin_ISR() {
arduino.send(channel1, "\"Movement\"");
digitalWrite(6, HIGH);
delay(50);
}
void setup()
{
initialize();
arduino.init( pubkey, subkey, uuid);
Serial.println("PubNub set up");
attachInterrupt(0, pin_ISR, RISING);
}
void loop()
{
String returnmessage;
Ethernet.maintain();
Serial.println("waiting for a message");
returnmessage = arduino.connect(channel);
// callback function of sorts, to work with the received message
do_something(returnmessage);
Serial.println();
}
I am just testing the receiving and sending messages so it does not contain LED control. It uses interrupt on pin2 and when it becomes HIGH Arduino publishes a message, which should be received by android app. Thank you for your help in advance.
I am doing some works with Texas Instruments SensorTag CC2541. I am trying to show the temperature data in the advertising packets.
First, I implemented the temperature service UUID in the advertising data packet. This is done in the SensorTag.c firmware (using IAR Workbench for 8051).
static uint8 advertData[] =
{
// Flags; this sets the device to use limited discoverable
// mode (advertises for 30 seconds at a time) instead of general
// discoverable mode (advertises indefinitely)
0x02, // length of this data
GAP_ADTYPE_FLAGS,
DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
0x03,
GAP_ADTYPE_16BIT_MORE,
LO_UINT16(IRTEMPERATURE_SERV_UUID),
HI_UINT16(IRTEMPERATURE_SERV_UUID)
};
And here is the Scan Response Array:
// GAP - SCAN RSP data (max size = 31 bytes)
static uint8 scanRspData[] =
{
// complete name
0x0A, // length of this data
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
0x53, // 'S'
0x65, // 'e'
0x6E, // 'n'
0x73, // 's'
0x6F, // 'o'
0x66, // 'f'
0x74, // 't'
0x69, // 'i'
0x61, // 'a'
// connection interval range
0x05, // length of this data
GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
LO_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),
HI_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),
LO_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),
HI_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),
0x03,
GAP_ADTYPE_16BIT_MORE,
LO_UINT16(IRTEMPERATURE_SERV_UUID),
HI_UINT16(IRTEMPERATURE_SERV_UUID)
};
Then, I use a sample Bluetooth Low Energy GAT application and modify it to show the data when it is scanning. The original app was used for another beacon device and it showed the beacon name + RSSI + thermometer service when scanning.
Here is the original code:
class TemperatureBeacon {
/* Full Bluetooth UUID that defines the Health Thermometer Service */
public static final ParcelUuid THERM_SERVICE = ParcelUuid.fromString("00001809-0000-1000-8000-00805f9b34fb");
/* Short-form UUID that defines the Health Thermometer service */
private static final int UUID_SERVICE_THERMOMETER = 0x1809;
private String mName;
private float mCurrentTemp;
//Device metadata
private int mSignal;
private String mAddress;
/* Builder for Lollipop+ */
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public TemperatureBeacon(ScanRecord record, String deviceAddress, int rssi) {
mSignal = rssi;
mAddress = deviceAddress;
mName = record.getDeviceName();
byte[] data = record.getServiceData(THERM_SERVICE);
if (data != null) {
mCurrentTemp = parseTemp(data);
} else {
mCurrentTemp = 0f;
}
}
/* Builder for pre-Lollipop */
public TemperatureBeacon(List<AdRecord> records, String deviceAddress, int rssi) {
mSignal = rssi;
mAddress = deviceAddress;
for(AdRecord packet : records) {
//Find the device name record
if (packet.getType() == AdRecord.TYPE_NAME) {
mName = AdRecord.getName(packet);
}
//Find the service data record that contains our service's UUID
if (packet.getType() == AdRecord.TYPE_SERVICEDATA
&& AdRecord.getServiceDataUuid(packet) == UUID_SERVICE_THERMOMETER) {
byte[] data = AdRecord.getServiceData(packet);
mCurrentTemp = parseTemp(data);
}
}
}
private float parseTemp(byte[] serviceData) {
/*
* Temperature data is two bytes, and precision is 0.5degC.
* LSB contains temperature whole number
* MSB contains a bit flag noting if fractional part exists
*/
float temp = (serviceData[0] & 0xFF);
if ((serviceData[1] & 0x80) != 0) {
temp += 0.5f;
}
return temp;
}
public String getName() {
return mName;
}
public int getSignal() {
return mSignal;
}
public float getCurrentTemp() {
return mCurrentTemp;
}
public String getAddress() {
return mAddress;
}
#Override
public String toString() {
return String.format("%s (%ddBm): %.1fC", mName, mSignal, mCurrentTemp);
}
}
As you can see, the program uses the THERM_SERVICE UUID (00001809-0000-1000-8000-00805f9b34fb) and its short form (0x1809). I tried to modify it with the SensorTag IR Temperature UUID service (F000AA00-0451-4000-B000-000000000000). And the result is that, it cannot show neither the name of the scanned device nor the temperature data.
Can you give some suggestions for this problem? Thank you!
The missing part in your question is the scan response structure in your modified SensorTag code. The advertData only notifies the central (iOS) of it's presence and available services (temperature). The scan response may carry more information, such as the value of the temperature. Could you post the scan response array?
I have a tcp server which is not in java also its a hardware device , I need to send and receve the data ,
I am connecting with server and sending some configuration data using following code
this.clientSocket=new Socket("198.168.1.17",9999);
this.os=new DataOutputStream(this.clientSocket.getOutputStream());
this.in=new DataInputStream(this.clientSocket.getInputStream());
System.out.println("Conncted");
char data[]={0x03,0x03,0x00};
byte b[]=new byte[data.length];
for (int i = 0; i < b.length; i++) {
b[i] = (byte) data[i];
}
try {
os.write(b);
Device receives data correctly , now in my tcp client i am not able to receive data , though i write following code just after above code
int size =in.available();
byte data1[]=new byte[size];
// in.read(data1);
String str=new String(data1);
System.out.println("Reading data:"+str);
It only shows reading data , and string has not data
also i tried about code in thread
public void run(){
try{
while(true){
int size =in.available();
byte data[]=new byte[size];
in.read(data);
String str=new String(data);
System.out.println("Reading data:"+str);
}
in thread it only shows reading data ,
Please help how can i get data from the server also please note server is built in i can not change the server code
what available() method do in code "int size =in.available();"????????
Solution may be:
available() method return 0 so you are not able to receive data.
use this code for receiving data from socket. write this code before onCreate.
private EditText mInputMessageView;
private Emitter.Listener onNewMessage = new Emitter.Listener() {
#Override
public void call(final Object... args) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
JSONObject data = (JSONObject) args[0];
String username;
String message;
try {
username = data.getString("username");
message = data.getString("message");
} catch (JSONException e) {
return;
}
// add the message to view
addMessage(username, message);
}
});
}
};