I'm developing an Android app using Android Studio. The app relies heavily on BLE (Bluetooth Low Energy), and the basic idea is that when a button is pressed, it will turn on a fan connected to an Arduino board. I'm pretty sure that I have the Arduino side of things all good, since I can connect to the Arduino through the Android app, and the Arduino is correctly advertising the services and characteristics. The problem I am having is that I am trying to write to a characteristic when a button is pressed, but I can't seem to be able to figure out how to obtain the current connected device to pass to the method to be able to write to the characteristic.
I am using the code from PunchThrough for all BLE-related tasks. (I have also read their article on BLE basics). Here is a link to their Github repository:
https://github.com/PunchThrough/ble-starter-android/tree/master/app/src/main/java/com/punchthrough/blestarterappandroid
I have confirmed that all their code is working properly in my app: I can scan for BLE devices, connect to one, and view services and characteristics for an associated device.
I have a separate page (activity) for the fan, which basically just has a button that turns the fan on and off. I am trying to do something like this:
public class Fan extends AppCompatActivity{
.
.
.
private BluetoothGatt gatt1;
byte[] charValueAray = new byte[1];
ConnectionManager connectionObject;
public BluetoothDevice deviceCurnt;
protected void onCreate(Bundle savedInstanceState){
.
. //Basic code that creates button and gets the XML activity file
.
public void onClick(View v)
{
if (fanButton.isChecked())
{
charValueArray[0] = 1; //When value is equal to 1, fan will turn on
fanCharacteristic.setValue(charValueArray);
connectionObject.writeCharacteristic(deviceCurnt, fanCharacteristic, charValueArray); //1
//Also tried this: gatt1.writeCharacteristic(fanCharacteristic); 2
}
else
{
//other code
}
}
Whenever I use 1 or 2, the app just crashes and returns to the main menu page of my app. The reason being probably because I am passing in a BluetoothDevice that is basically non existent, and same reason with the gatt1 reference. I have also already come up with a way to disable the button if there is no device connected, and I have confirmed that the app does not crash because of any "Not connected to any device to pass information to" type of error.
I'm guessing that I am also doing something wrong with the character values when using a byte array. I have looked at lots of sample code online about Android BLE, but I can't seem to find a way on how to write to a characteristic from within my own activity page. If someone can help me out to correctly write to a characteristic the right way I would greatly appreciate it. Thanks
Edit:
Here is the class ConnectionManager (by PunchThrough) that I am using to connect to BLE devices:
/*
* Copyright 2019 Punch Through Design LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.punchthrough.blestarterappandroid.ble
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothProfile
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
import timber.log.Timber
import java.lang.ref.WeakReference
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
private const val GATT_MIN_MTU_SIZE = 23
/** Maximum BLE MTU size as defined in gatt_api.h. */
private const val GATT_MAX_MTU_SIZE = 517
object ConnectionManager {
private var listeners: MutableSet<WeakReference<ConnectionEventListener>> = mutableSetOf()
private val deviceGattMap = ConcurrentHashMap<BluetoothDevice, BluetoothGatt>()
private val operationQueue = ConcurrentLinkedQueue<BleOperationType>()
private var pendingOperation: BleOperationType? = null
fun servicesOnDevice(device: BluetoothDevice): List<BluetoothGattService>? =
deviceGattMap[device]?.services
fun listenToBondStateChanges(context: Context) {
context.applicationContext.registerReceiver(
broadcastReceiver,
IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
)
}
fun registerListener(listener: ConnectionEventListener) {
if (listeners.map { it.get() }.contains(listener)) { return }
listeners.add(WeakReference(listener))
listeners = listeners.filter { it.get() != null }.toMutableSet()
Timber.d("Added listener $listener, ${listeners.size} listeners total")
}
fun unregisterListener(listener: ConnectionEventListener) {
// Removing elements while in a loop results in a java.util.ConcurrentModificationException
var toRemove: WeakReference<ConnectionEventListener>? = null
listeners.forEach {
if (it.get() == listener) {
toRemove = it
}
}
toRemove?.let {
listeners.remove(it)
Timber.d("Removed listener ${it.get()}, ${listeners.size} listeners total")
}
}
fun connect(device: BluetoothDevice, context: Context) {
if (device.isConnected()) {
Timber.e("Already connected to ${device.address}!")
} else {
enqueueOperation(Connect(device, context.applicationContext))
}
}
fun teardownConnection(device: BluetoothDevice) {
if (device.isConnected()) {
enqueueOperation(Disconnect(device))
} else {
Timber.e("Not connected to ${device.address}, cannot teardown connection!")
}
}
fun readCharacteristic(device: BluetoothDevice, characteristic: BluetoothGattCharacteristic) {
if (device.isConnected() && characteristic.isReadable()) {
enqueueOperation(CharacteristicRead(device, characteristic.uuid))
} else if (!characteristic.isReadable()) {
Timber.e("Attempting to read ${characteristic.uuid} that isn't readable!")
} else if (!device.isConnected()) {
Timber.e("Not connected to ${device.address}, cannot perform characteristic read")
}
}
fun writeCharacteristic(
device: BluetoothDevice,
characteristic: BluetoothGattCharacteristic,
payload: ByteArray
) {
val writeType = when {
characteristic.isWritable() -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
characteristic.isWritableWithoutResponse() -> {
BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
}
else -> {
Timber.e("Characteristic ${characteristic.uuid} cannot be written to")
return
}
}
if (device.isConnected()) {
enqueueOperation(CharacteristicWrite(device, characteristic.uuid, writeType, payload))
} else {
Timber.e("Not connected to ${device.address}, cannot perform characteristic write")
}
}
fun readDescriptor(device: BluetoothDevice, descriptor: BluetoothGattDescriptor) {
if (device.isConnected() && descriptor.isReadable()) {
enqueueOperation(DescriptorRead(device, descriptor.uuid))
} else if (!descriptor.isReadable()) {
Timber.e("Attempting to read ${descriptor.uuid} that isn't readable!")
} else if (!device.isConnected()) {
Timber.e("Not connected to ${device.address}, cannot perform descriptor read")
}
}
fun writeDescriptor(
device: BluetoothDevice,
descriptor: BluetoothGattDescriptor,
payload: ByteArray
) {
if (device.isConnected() && (descriptor.isWritable() || descriptor.isCccd())) {
enqueueOperation(DescriptorWrite(device, descriptor.uuid, payload))
} else if (!device.isConnected()) {
Timber.e("Not connected to ${device.address}, cannot perform descriptor write")
} else if (!descriptor.isWritable() && !descriptor.isCccd()) {
Timber.e("Descriptor ${descriptor.uuid} cannot be written to")
}
}
fun enableNotifications(device: BluetoothDevice, characteristic: BluetoothGattCharacteristic) {
if (device.isConnected() &&
(characteristic.isIndicatable() || characteristic.isNotifiable())
) {
enqueueOperation(EnableNotifications(device, characteristic.uuid))
} else if (!device.isConnected()) {
Timber.e("Not connected to ${device.address}, cannot enable notifications")
} else if (!characteristic.isIndicatable() && !characteristic.isNotifiable()) {
Timber.e("Characteristic ${characteristic.uuid} doesn't support notifications/indications")
}
}
fun disableNotifications(device: BluetoothDevice, characteristic: BluetoothGattCharacteristic) {
if (device.isConnected() &&
(characteristic.isIndicatable() || characteristic.isNotifiable())
) {
enqueueOperation(DisableNotifications(device, characteristic.uuid))
} else if (!device.isConnected()) {
Timber.e("Not connected to ${device.address}, cannot disable notifications")
} else if (!characteristic.isIndicatable() && !characteristic.isNotifiable()) {
Timber.e("Characteristic ${characteristic.uuid} doesn't support notifications/indications")
}
}
fun requestMtu(device: BluetoothDevice, mtu: Int) {
if (device.isConnected()) {
enqueueOperation(MtuRequest(device, mtu.coerceIn(GATT_MIN_MTU_SIZE, GATT_MAX_MTU_SIZE)))
} else {
Timber.e("Not connected to ${device.address}, cannot request MTU update!")
}
}
// - Beginning of PRIVATE functions
#Synchronized
private fun enqueueOperation(operation: BleOperationType) {
operationQueue.add(operation)
if (pendingOperation == null) {
doNextOperation()
}
}
#Synchronized
private fun signalEndOfOperation() {
Timber.d("End of $pendingOperation")
pendingOperation = null
if (operationQueue.isNotEmpty()) {
doNextOperation()
}
}
/**
* Perform a given [BleOperationType]. All permission checks are performed before an operation
* can be enqueued by [enqueueOperation].
*/
#Synchronized
private fun doNextOperation() {
if (pendingOperation != null) {
Timber.e("doNextOperation() called when an operation is pending! Aborting.")
return
}
val operation = operationQueue.poll() ?: run {
Timber.v("Operation queue empty, returning")
return
}
pendingOperation = operation
// Handle Connect separately from other operations that require device to be connected
if (operation is Connect) {
with(operation) {
Timber.w("Connecting to ${device.address}")
device.connectGatt(context, false, callback)
}
return
}
// Check BluetoothGatt availability for other operations
val gatt = deviceGattMap[operation.device]
?: this#ConnectionManager.run {
Timber.e("Not connected to ${operation.device.address}! Aborting $operation operation.")
signalEndOfOperation()
return
}
// TODO: Make sure each operation ultimately leads to signalEndOfOperation()
// TODO: Refactor this into an BleOperationType abstract or extension function
when (operation) {
is Disconnect -> with(operation) {
Timber.w("Disconnecting from ${device.address}")
gatt.close()
deviceGattMap.remove(device)
listeners.forEach { it.get()?.onDisconnect?.invoke(device) }
signalEndOfOperation()
}
is CharacteristicWrite -> with(operation) {
gatt.findCharacteristic(characteristicUuid)?.let { characteristic ->
characteristic.writeType = writeType
characteristic.value = payload
gatt.writeCharacteristic(characteristic)
} ?: this#ConnectionManager.run {
Timber.e("Cannot find $characteristicUuid to write to")
signalEndOfOperation()
}
}
is CharacteristicRead -> with(operation) {
gatt.findCharacteristic(characteristicUuid)?.let { characteristic ->
gatt.readCharacteristic(characteristic)
} ?: this#ConnectionManager.run {
Timber.e("Cannot find $characteristicUuid to read from")
signalEndOfOperation()
}
}
is DescriptorWrite -> with(operation) {
gatt.findDescriptor(descriptorUuid)?.let { descriptor ->
descriptor.value = payload
gatt.writeDescriptor(descriptor)
} ?: this#ConnectionManager.run {
Timber.e("Cannot find $descriptorUuid to write to")
signalEndOfOperation()
}
}
is DescriptorRead -> with(operation) {
gatt.findDescriptor(descriptorUuid)?.let { descriptor ->
gatt.readDescriptor(descriptor)
} ?: this#ConnectionManager.run {
Timber.e("Cannot find $descriptorUuid to read from")
signalEndOfOperation()
}
}
is EnableNotifications -> with(operation) {
gatt.findCharacteristic(characteristicUuid)?.let { characteristic ->
val cccdUuid = UUID.fromString(CCC_DESCRIPTOR_UUID)
val payload = when {
characteristic.isIndicatable() ->
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
characteristic.isNotifiable() ->
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
else ->
error("${characteristic.uuid} doesn't support notifications/indications")
}
characteristic.getDescriptor(cccdUuid)?.let { cccDescriptor ->
if (!gatt.setCharacteristicNotification(characteristic, true)) {
Timber.e("setCharacteristicNotification failed for ${characteristic.uuid}")
signalEndOfOperation()
return
}
cccDescriptor.value = payload
gatt.writeDescriptor(cccDescriptor)
} ?: this#ConnectionManager.run {
Timber.e("${characteristic.uuid} doesn't contain the CCC descriptor!")
signalEndOfOperation()
}
} ?: this#ConnectionManager.run {
Timber.e("Cannot find $characteristicUuid! Failed to enable notifications.")
signalEndOfOperation()
}
}
is DisableNotifications -> with(operation) {
gatt.findCharacteristic(characteristicUuid)?.let { characteristic ->
val cccdUuid = UUID.fromString(CCC_DESCRIPTOR_UUID)
characteristic.getDescriptor(cccdUuid)?.let { cccDescriptor ->
if (!gatt.setCharacteristicNotification(characteristic, false)) {
Timber.e("setCharacteristicNotification failed for ${characteristic.uuid}")
signalEndOfOperation()
return
}
cccDescriptor.value = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(cccDescriptor)
} ?: this#ConnectionManager.run {
Timber.e("${characteristic.uuid} doesn't contain the CCC descriptor!")
signalEndOfOperation()
}
} ?: this#ConnectionManager.run {
Timber.e("Cannot find $characteristicUuid! Failed to disable notifications.")
signalEndOfOperation()
}
}
is MtuRequest -> with(operation) {
gatt.requestMtu(mtu)
}
}
}
private val callback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
val deviceAddress = gatt.device.address
if (status == BluetoothGatt.GATT_SUCCESS) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
Timber.w("onConnectionStateChange: connected to $deviceAddress")
deviceGattMap[gatt.device] = gatt
Handler(Looper.getMainLooper()).post {
gatt.discoverServices()
}
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Timber.e("onConnectionStateChange: disconnected from $deviceAddress")
teardownConnection(gatt.device)
}
} else {
Timber.e("onConnectionStateChange: status $status encountered for $deviceAddress!")
if (pendingOperation is Connect) {
signalEndOfOperation()
}
teardownConnection(gatt.device)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
with(gatt) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Timber.w("Discovered ${services.size} services for ${device.address}.")
printGattTable()
requestMtu(device, GATT_MAX_MTU_SIZE)
listeners.forEach { it.get()?.onConnectionSetupComplete?.invoke(this) }
} else {
Timber.e("Service discovery failed due to status $status")
teardownConnection(gatt.device)
}
}
if (pendingOperation is Connect) {
signalEndOfOperation()
}
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
Timber.w("ATT MTU changed to $mtu, success: ${status == BluetoothGatt.GATT_SUCCESS}")
listeners.forEach { it.get()?.onMtuChanged?.invoke(gatt.device, mtu) }
if (pendingOperation is MtuRequest) {
signalEndOfOperation()
}
}
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
with(characteristic) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Read characteristic $uuid | value: ${value.toHexString()}")
listeners.forEach { it.get()?.onCharacteristicRead?.invoke(gatt.device, this) }
}
BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
Timber.e("Read not permitted for $uuid!")
}
else -> {
Timber.e("Characteristic read failed for $uuid, error: $status")
}
}
}
if (pendingOperation is CharacteristicRead) {
signalEndOfOperation()
}
}
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
with(characteristic) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Wrote to characteristic $uuid | value: ${value.toHexString()}")
listeners.forEach { it.get()?.onCharacteristicWrite?.invoke(gatt.device, this) }
}
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
Timber.e("Write not permitted for $uuid!")
}
else -> {
Timber.e("Characteristic write failed for $uuid, error: $status")
}
}
}
if (pendingOperation is CharacteristicWrite) {
signalEndOfOperation()
}
}
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
with(characteristic) {
Timber.i("Characteristic $uuid changed | value: ${value.toHexString()}")
listeners.forEach { it.get()?.onCharacteristicChanged?.invoke(gatt.device, this) }
}
}
override fun onDescriptorRead(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
with(descriptor) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Read descriptor $uuid | value: ${value.toHexString()}")
listeners.forEach { it.get()?.onDescriptorRead?.invoke(gatt.device, this) }
}
BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
Timber.e("Read not permitted for $uuid!")
}
else -> {
Timber.e("Descriptor read failed for $uuid, error: $status")
}
}
}
if (pendingOperation is DescriptorRead) {
signalEndOfOperation()
}
}
override fun onDescriptorWrite(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
with(descriptor) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Timber.i("Wrote to descriptor $uuid | value: ${value.toHexString()}")
if (isCccd()) {
onCccdWrite(gatt, value, characteristic)
} else {
listeners.forEach { it.get()?.onDescriptorWrite?.invoke(gatt.device, this) }
}
}
BluetoothGatt.GATT_WRITE_NOT_PERMITTED -> {
Timber.e("Write not permitted for $uuid!")
}
else -> {
Timber.e("Descriptor write failed for $uuid, error: $status")
}
}
}
if (descriptor.isCccd() &&
(pendingOperation is EnableNotifications || pendingOperation is DisableNotifications)
) {
signalEndOfOperation()
} else if (!descriptor.isCccd() && pendingOperation is DescriptorWrite) {
signalEndOfOperation()
}
}
private fun onCccdWrite(
gatt: BluetoothGatt,
value: ByteArray,
characteristic: BluetoothGattCharacteristic
) {
val charUuid = characteristic.uuid
val notificationsEnabled =
value.contentEquals(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) ||
value.contentEquals(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE)
val notificationsDisabled =
value.contentEquals(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)
when {
notificationsEnabled -> {
Timber.w("Notifications or indications ENABLED on $charUuid")
listeners.forEach {
it.get()?.onNotificationsEnabled?.invoke(
gatt.device,
characteristic
)
}
}
notificationsDisabled -> {
Timber.w("Notifications or indications DISABLED on $charUuid")
listeners.forEach {
it.get()?.onNotificationsDisabled?.invoke(
gatt.device,
characteristic
)
}
}
else -> {
Timber.e("Unexpected value ${value.toHexString()} on CCCD of $charUuid")
}
}
}
}
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
with(intent) {
if (action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
val device = getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
val previousBondState = getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1)
val bondState = getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)
val bondTransition = "${previousBondState.toBondStateDescription()} to " +
bondState.toBondStateDescription()
Timber.w("${device?.address} bond state changed | $bondTransition")
}
}
}
private fun Int.toBondStateDescription() = when (this) {
BluetoothDevice.BOND_BONDED -> "BONDED"
BluetoothDevice.BOND_BONDING -> "BONDING"
BluetoothDevice.BOND_NONE -> "NOT BONDED"
else -> "ERROR: $this"
}
}
private fun BluetoothDevice.isConnected() = deviceGattMap.containsKey(this)
}
As I am new to swift language I know how to convert speech to text in objective c using the microsoft API but as part of client request I need that in swift language .can anyone help me how to do that in swift language. I also added the sample code which I used in objective c
-(void)onFinalResponseReceived:(RecognitionResult*)response {
bool isFinalDicationMessage = self.mode == SpeechRecognitionMode_LongDictation &&
(response.RecognitionStatus == RecognitionStatus_EndOfDictation ||
response.RecognitionStatus == RecognitionStatus_DictationEndSilenceTimeout);
if (nil != micClient && self.useMicrophone && ((self.mode == SpeechRecognitionMode_ShortPhrase) || isFinalDicationMessage)) {
// we got the final result, so it we can end the mic reco. No need to do this
// for dataReco, since we already called endAudio on it as soon as we were done
// sending all the data.
[micClient endMicAndRecognition];
}
if ((self.mode == SpeechRecognitionMode_ShortPhrase) || isFinalDicationMessage) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self startButton] setEnabled:YES];
});
}
if (!isFinalDicationMessage) {
dispatch_async(dispatch_get_main_queue(), ^{
[self WriteLine:(#"********* Final n-BEST Results *********")];
for (int i = 0; i < [response.RecognizedPhrase count]; i++) {
RecognizedPhrase* phrase = response.RecognizedPhrase[i];
[self WriteLine:[[NSString alloc] initWithFormat:(#"[%d] Confidence=%# Text=\"%#\""),
i,
ConvertSpeechRecoConfidenceEnumToString(phrase.Confidence),
phrase.DisplayText]];
}
[self WriteLine:(#"")];
});
}
}
//convert speech
OSStatus status = [micClient startMicAndRecognition];
if (status) {
[self WriteLine:[[NSString alloc] initWithFormat:(#"Error starting audio. %#"),
ConvertSpeechErrorToString(status)]];
}
NSString* ConvertSpeechErrorToString(int errorCode) {
switch ((SpeechClientStatus)errorCode) {
case SpeechClientStatus_SecurityFailed: return #"SpeechClientStatus_SecurityFailed";
}
Try this:
func onFinalResponseReceived(_ response: RecognitionResult?) {
let isFinalDicationMessage: Bool = mode == SpeechRecognitionMode_LongDictation && (response?.recognitionStatus == RecognitionStatus_EndOfDictation || response?.recognitionStatus == RecognitionStatus_DictationEndSilenceTimeout)
if nil != micClient && useMicrophone && ((mode == SpeechRecognitionMode_ShortPhrase) || isFinalDicationMessage) {
// we got the final result, so it we can end the mic reco. No need to do this
// for dataReco, since we already called endAudio on it as soon as we were done
// sending all the data.
micClient.endMicAndRecognition()
}
if (mode == SpeechRecognitionMode_ShortPhrase) || isFinalDicationMessage {
DispatchQueue.main.async(execute: {() -> Void in
self.startButton().enabled = true
})
}
if !isFinalDicationMessage {
DispatchQueue.main.async(execute: {() -> Void in
self.writeLine(("********* Final n-BEST Results *********"))
var i = 0
while i < response.recognizedPhrase.count() {
var phrase: RecognizedPhrase? = response.recognizedPhrase[i]
if let aText = phrase?.displayText {
self.writeLine(("[\(i)] Confidence=\(ConvertSpeechRecoConfidenceEnumToString(phrase?.confidence)) Text=\"\(aText)\""))
}
i
}
i += 1
self.writeLine((""))
})
}
}
// edit:
var status: OSStatus = micClient.startMicAndRecognition()
func (int errorCode) -> String? {
switch errorCode as? SpeechClientStatus {
case SpeechClientStatus_SecurityFailed:
return "SpeechClientStatus_SecurityFailed"
}
}
I get random memory crashes with no memory warning. I have updated my ios to 10.0.2, my Xamarin to 6.2. I added camera usage description and photo library usage description, as suggested.
I have the following code in my FinishedPickingMedia callback
public async override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info)
{
try
{
// determine what was selected, video or image
bool isImage = false;
switch (info[UIImagePickerController.MediaType].ToString())
{
case "public.image":
isImage = true;
break;
case "public.video":
break;
}
if (isImage)
{
UIImage originalImage = info[UIImagePickerController.OriginalImage] as UIImage;
if (originalImage != null)
{
UIImageOrientation OrIn = originalImage.Orientation;
Debug.WriteLine("scaling image");
var originalImage1 = await Task.Run(() => ScaleAndRotateImage.ScaleAndRotateImageView(originalImage, OrIn));
if (originalImage1 != null)
{
var Data = originalImage1.AsJPEG(0.0f);
if (Data != null)
{
UIImage resizedImage = UIImage.LoadFromData(Data);
if (originalImage != null)
{
originalImage.Dispose();
originalImage = null;
}
originalImage1.Dispose();
originalImage1 = null;
Data.Dispose();
Data = null;
GC.Collect();
#if DEBUG
var Process1 = Process.GetCurrentProcess();
if (Process1 != null)
{
Debug.WriteLine(string.Format("memory allocated by FinishedPickingMedia = {0}", Process1.WorkingSet64));
}
#endif
camController.ShowPhoto(resizedImage);
}
if (info != null)
{
info.Dispose();
info = null;
}
GC.Collect();
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception Occured in FinishedPickingMedia method due to " + ex.Message);
}
finally
{
// dismiss the picker
picker.DismissModalViewController(true);
picker.Dispose();
picker = null;
GC.Collect();
}
}
This seams to happen randomly, my application needs to capture more than 200 photos (archive) I researched on the internet and this seams to be an issue with iOS 10 and this control
I recently received a BLE device for Bluetooth to Serial. It uses TruConnect and I'm trying to get it to communicate with my serial device. The serial device receives communication over a serial cable and echoes back anything that is sent to it as well as any results from a command that is sent.
Right now I'm simply trying to send TruConnect commands to the BLE device to check the current baud rate that the BLE device is set for.
I wrote some code based on this TruConnect guide that I found:
https://truconnect.ack.me/1.5/apps/communicating_via_ble#reading_from_a_truconnect_device_serial_interface.
The problem seems to be that whenever I try to read anything from the tx characteristic when there should be data, the data is not right.
Setting up CBPeripheral events:
private void setupPerif(CBPeripheral perf)
{
selectedPeripheral = perf;
selectedPeripheral.UpdatedCharacterteristicValue += (sender, e) =>
{
var c = e.Characteristic;
if (c != null)
{
var uuid = c.UUID.ToString(true).ToLower();
if (uuid == UUID_RX)
{
//
}
else if (uuid == UUID_TX)
{
// expecting bytes to contain valid response data
// it almost always contains twenty 0s.
byte[] bytes = c.Value.Where(i => i != 13).ToArray();
var invalidBytes = c.Value.Where(i => i > 127).ToArray();
var nonZeros = c.Value.Where(i => i != 0).ToArray();
if (nonZeros.Length < 1)
{
return;
}
else
{
foreach (byte b in bytes)
handler.handleByteReceived((char)b);
}
}
else if (uuid == UUID_MODE)
{
//
}
}
};
selectedPeripheral.DiscoveredService += (sender, e) =>
{
var services = selectedPeripheral.Services;
if (services != null)
{
foreach (CBService service in services)
{
if (service.UUID.ToString(true).ToLower() == UUID_TRUCONNECT)
{
truConnect = service;
selectedPeripheral.DiscoverCharacteristics(truConnect);
}
}
}
};
selectedPeripheral.DiscoveredCharacteristic += (sender, e) =>
{
if (truConnect != null && truConnect.Characteristics != null)
{
foreach (CBCharacteristic c in truConnect.Characteristics)
{
var uuidString = c.UUID.ToString(true).ToLower();
if (uuidString == UUID_RX)
{
rx = c;
}
else if (uuidString == UUID_TX)
{
tx = c;
}
else if (uuidString == UUID_MODE)
{
mode = c;
// set to stream mode
selectedPeripheral.WriteValue(NSData.FromArray(new byte[] { MODE_COMMAND }), mode, CBCharacteristicWriteType.WithResponse);
}
}
}
};
selectedPeripheral.WroteCharacteristicValue += (sender, e) =>
{
// if UUID is for RX, we just wrote to RX. Drill down to
// TX characteristic and read it. This will trigger
// the UpdatedCharacteristicValue event.
string uuid = e.Characteristic.UUID.ToString(true).ToLower();
if (uuid == UUID_RX)
{
var services = selectedPeripheral.Services;
if (services != null)
{
foreach (CBService s in services)
{
if (s.UUID.ToString(true).ToLower() == UUID_TRUCONNECT)
{
var charachteristics = s.Characteristics;
if (charachteristics != null && charachteristics.Length > 0)
{
foreach (CBCharacteristic c in charachteristics)
{
if (c.UUID.ToString(true).ToLower() == UUID_TX)
{
Timer t = new Timer(new TimerCallback(delegate(object o)
{
selectedPeripheral.ReadValue(c);
}), null, TimeSpan.FromMilliseconds(100), TimeSpan.FromMilliseconds(-1));
}
}
}
}
}
}
}
};
manager.ConnectPeripheral(selectedPeripheral);
}
Writes to rx. This is what should be used to actually send commands.
public void sendCommand(string command)
{
command += endString + "\n";
if (rx != null)
{
NSData d = NSData.FromString(command);
foreach (CBService s in selectedPeripheral.Services)
{
if (s.UUID.ToString(true).ToLower() == UUID_TRUCONNECT)
foreach (CBCharacteristic c in s.Characteristics)
{
if (c.UUID.ToString(true).ToLower() == UUID_RX)
selectedPeripheral.WriteValue(NSData.FromString(command), c, CBCharacteristicWriteType.WithResponse);
}
}
}
}
So my question is, why am I not getting the expected data when the CBPeripheral.UpdatedCharacteristicValue event is called? Occasionally I will get the expected data, but it is quite rare, and I can't seem to find any logical reason or pattern that would explain why this is happening.
AHA! I figured it out!
The problem was that I need to set the notify value for the appropriate characteristics. After doing that, I didn't need to call CBPeripheral.ReadValue(CBCharacteristic).
selectedPeripheral.DiscoveredCharacteristic += (sender, e) =>
{
if (truConnect != null && truConnect.Characteristics != null)
{
foreach (CBCharacteristic c in truConnect.Characteristics)
{
var uuidString = c.UUID.ToString(true).ToLower();
if (uuidString == UUID_RX)
{
rx = c;
}
else if (uuidString == UUID_TX)
{
tx = c;
// set the notify value to true and poof!
// now CBPeripheral.UpdatedCharacteristicValue
// event will be triggered at the appropriate time.
selectedPeripheral.SetNotifyValue(true, tx);
}
else if (uuidString == UUID_MODE)
{
mode = c;
// set to remote command mode
selectedPeripheral.WriteValue(NSData.FromArray(new byte[] { MODE_COMMAND }), mode, CBCharacteristicWriteType.WithResponse);
}
}
}
};
I have a 3 GSM phones and a 3 verizon (CDMA) phones. I have a BB application in which the location listener is set to a 5 minute interval.
For 2 of the verizon phones the application's location update method gets called frequently. For the rest, the location listener gets called at a regular 5 minute interval.
What could be causing this difference in behavior?
public synchronized void locationUpdated(LocationProvider locationProvider, Location location) {
if (enabled) {
if (blackberryProvider != null) {
try {
constructCriteria(GPSInfo.GPS_MODE_CELLSITE);
gpsUpdate();
} catch (LocationException e) {
log stuff//
}
}
}
}
private void gpsUpdate() throws LocationException, InterruptedException {
try {
String gpsMode = null;
if (bbCriteria.getMode() == GPSInfo.GPS_MODE_CELLSITE) {
gpsMode = "cellsiteMode";
}
if (gpsMode == "cellsiteMode" && gpsMode.length() > 0 && bbProvider != null) {
// variable declaration
try {
bbLocation = (BlackBerryLocation) bbProvider.getLocation(10);
} catch (LocationException e) {
bbLocation = null;
}
if (bbLocation != null) {
// do stuff
// store location in the database
}
}
}
}
}
}
private void constructCriteria(final int mode) {
blackberryCriteria = null;
blackberryProvider = null;
blackberryCriteria = new BlackBerryCriteria();
blackberryCriteria.setSatelliteInfoRequired(true, false);
if (mode == GPSInfo.GPS_MODE_CELLSITE) {
setCriteraForCellSite();
}
try {
blackberryProvider = (BlackBerryLocationProvider) LocationProvider.getInstance(blackberryCriteria);
if (iLocationListner == null) {
iLocationListner = new ILocationListner();
blackberryProvider.setLocationListener(iLocationListner, locationInterval == 0 ? 300 : locationInterval, -1, -1);
} else {
blackberryProvider.setLocationListener(iLocationListner, locationInterval == 0 ? 300 : locationInterval, -1, -1);
}
} catch (LocationException lex) {
Logger.log("LocationEventSource constructor", lex);
return;
}
}
You are setting your criteria to update every 300 seconds if locationInterval == 0 or at the default rate (once per second) otherwise. Is this really what you want? Where is locationInterval initialized? How does its value change as the program runs?