IOIO locks up during SPI transaction with MCP-4131 any solutions? - android

I am attempting to using an IOIO-RTG board to control a MCP-4131 digital potentiometer via SPI. I'm new to SPI but I believe that I've followed the SPI example. I'm able to set a resistance apparently but IOIO remains stuck afterwards. The only way to continue is to disconnect and reconnect to the board. I note that the SPI example expects a MISO and MOSI pin whereas the pot has a combined SDI/SDO pin. Is this difference the source of my issue?
IOIO RTG
IOIOLIb 0326
Application Firmware 0506
Bootloader Firmware 0402
Hardware Sprk 0020
I've tried to implement asynchronous transactions to not wait for a response but the end result is the same. I've called the highgear function from within the Looper class and outside with no change.
class Looper extends BaseIOIOLooper
{
SpiMaster spi;
protected void setup() throws ConnectionLostException
{
int clkPin = 39;//left side = 36
int misoPin = 38;//left side = 33, not expecting output
int mosiPin = 38;//left side = 35
spi = ioio_.openSpiMaster(new DigitalInput.Spec(misoPin,
Mode.PULL_UP), new DigitalOutput.Spec(mosiPin),
new DigitalOutput.Spec(clkPin),
new DigitalOutput.Spec[] { new DigitalOutput.Spec(40), new DigitalOutput.Spec(37), },
new SpiMaster.Config(Rate.RATE_125k, true, true));
}
public void highgear()
{
byte[] request = new byte[] {0,0,0,0,0,5,5,5};
byte[] response = new byte[4];
try {
SpiMaster.Result result = spi.writeReadAsync(0, request, request.length, 7, response, 0);
} catch (ConnectionLostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The expected outcome is that the MCP with give the desired resistance and the IOIO will be available for further commanding. There are no errors as the board just freezes in it's set configuration.

The shared SDO/SDI pin of the MCP-4131 should not be the problem.
From the datasheet on page 31: "The 8-lead Single Potentiometer devices are pin limited so the SDO pin is multiplexed with the SDI pin (SDI/SDO pin). After the Address/Command (first 6-bits) are received, If a valid Read command has been requested, the SDO pin starts driving the requested read data onto the SDI/SDO pin."
As long as you only write to the digital potentiometer everything should be the same as with other SPI devices.
Have you tried your code with other SPI devices or even without connecting one?

Related

How to scan IBeacon constantly on Android (Xamarin.Android)?

I'm working on a cross-platform (iOS/Android) Xamarin app, in which I need to scan IBeacon devices to assert the distance between them and the phone device.
On iOS, I use the native iOS iBeacon API, which works flawlessly and as expected.
On Android, since Android does not natively support iBeacon, I use a mix of my own code and a library "UniversalBeacon". This approach works, but when it comes to scanning (or "ranging") for Beacons over a period of time, in order to constantly assess the distance of the phone device, the experince proves very unreliable.
I am experiencing that incoming BLE packets come in as expected, but only in intervals.
Roughly summarized: Packets will come in, in a steady stream, for a seemingly random amount of time, before the packets eventually stop coming in entirely. Then, after another seemingly random amount time, packets will start coming in again. This process repeats indefinitely.
So my question is: What is causing this issue? Is it an Android quirk that I somehow have to work around?
Initiating the scan:
_ScanCallback.OnAdvertisementPacketReceived += ScanCallback_OnAdvertisementPacketReceived;
var settings = new ScanSettings.Builder()
.SetScanMode(ScanMode.LowLatency)
.Build();
_Adapter.BluetoothLeScanner.StartScan(null, settings, _ScanCallback);
Callback implementation:
internal class BLEScanCallback : ScanCallback
{
public event EventHandler<BLEAdvertisementPacketArgs> OnAdvertisementPacketReceived;
public override void OnScanFailed([GeneratedEnum] ScanFailure errorCode)
{
base.OnScanFailed(errorCode);
}
public override void OnScanResult([GeneratedEnum] ScanCallbackType callbackType, ScanResult result)
{
base.OnScanResult(callbackType, result);
switch (result.Device.Type)
{
case BluetoothDeviceType.Le:
case BluetoothDeviceType.Unknown:
try
{
var p = new BLEAdvertisementPacket
{
BluetoothAddress = result.Device.Address.ToNumericAddress(),
RawSignalStrengthInDBm = (short)result.Rssi,
Timestamp = DateTimeOffset.FromUnixTimeMilliseconds(result.TimestampNanos / 1000000),
AdvertisementType = (BLEAdvertisementType)result.ScanRecord.AdvertiseFlags,
Advertisement = new BLEAdvertisement
{
LocalName = result.ScanRecord.DeviceName
}
};
if (result.ScanRecord.ServiceUuids != null)
{
foreach (var svc in result.ScanRecord.ServiceUuids)
{
var guid = new Guid(svc.Uuid.ToString());
var data = result.ScanRecord.GetServiceData(svc);
p.Advertisement.ServiceUuids.Add(guid);
}
}
var recordData = result.ScanRecord.GetBytes();
var rec = RecordParser.Parse(recordData);
foreach (var curRec in rec)
{
if (curRec is BLEManufacturerData md)
{
p.Advertisement.ManufacturerData.Add(md);
}
if (curRec is BLEAdvertisementDataSection sd)
{
p.Advertisement.DataSections.Add(sd);
}
}
OnAdvertisementPacketReceived?.Invoke(this, new BLEAdvertisementPacketArgs(p));
}
catch (Exception ex)
{
Debugger.Break();
}
break;
default:
break;
}
}
}
I've read in various articles that this could be caused by Android automatically suspending the scan in order to save power. Whether this is the case is not obvious to me, as there does not seem to be much support on the subject.
I've already tried the following:
Changing ScanMode to LowPower/Balanced - no change
Scanning with a filter set to the specific Beacon I was testing with - filter worked, but no change in regards to the issue
Implementing logic that restarts the scan in set intervals to work around potentional limits for scan duration imposed by Android - did not affect the issue
Using other, more broadly used libraries, such as Shiny.Beacons - same experience
The issue is not caused by the Beacon device itself not advertising correctly - I've made sure of this by scanning it on another device, at the same time as my app. It is the app itself that stops scanning and/or receiving its advertisement packets.
Thanks for your time :)

How to transfer images via Bluetooth (LE) to a desktop application

We are currently trying to implement the transmission of images from a mobile device (in this case an IPhone) to a desktop application. We tried already the Bluetooth Serial plugin which works fine for Android but does not list any devices when scanning for our desktop application.
To cover iOS support (AFAIK iOS only supports BluetoothLE), we reimplemented our desktop application to use BluetoothLE and behave like a peripheral. Also we altered our Ionic application to use BLE plugin.
Now BluetoothLE only supports the transmission of packages with the size of 20 Byte whilst our image is about 500kb big. So we could obviously split our image into chunks and transmit it with the following function (taken from this gist):
function writeLargeData(buffer) {
console.log('writeLargeData', buffer.byteLength, 'bytes in',MAX_DATA_SEND_SIZE, 'byte chunks.');
var chunkCount = Math.ceil(buffer.byteLength / MAX_DATA_SEND_SIZE);
var chunkTotal = chunkCount;
var index = 0;
var startTime = new Date();
var transferComplete = function () {
console.log("Transfer Complete");
}
var sendChunk = function () {
if (!chunkCount) {
transferComplete();
return; // so we don't send an empty buffer
}
console.log('Sending data chunk', chunkCount + '.');
var chunk = buffer.slice(index, index + MAX_DATA_SEND_SIZE);
index += MAX_DATA_SEND_SIZE;
chunkCount--;
ble.write(
device_id,
service_uuid,
characteristic_uuid,
chunk,
sendChunk, // success callback - call sendChunk() (recursive)
function(reason) { // error callback
console.log('Write failed ' + reason);
}
)
}
// send the first chunk
sendChunk();
}
Still this would mean for us that we would have to launch about 25k transmissions which I assume will take a long time to complete. Now I wonder why is that the data transmission via Bluetooth is that handicapped.
If you want to try out L2CAP your could modify your Central desktop app somehow like this:
private let characteristicUUID = CBUUID(string: CBUUIDL2CAPPSMCharacteristicString)
...
Then advertize and publish a L2CAP channel:
let service = CBMutableService(type: peripheralUUID, primary: true)
let properties: CBCharacteristicProperties = [.read, .indicate]
let permissions: CBAttributePermissions = [.readable]
let characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: properties, value: nil, permissions: permissions)
self.characteristic = characteristic
service.characteristics = [characteristic]
self.manager.add(service)
self.manager.publishL2CAPChannel(withEncryption: false)
let data = [CBAdvertisementDataLocalNameKey : "Peripherial-42", CBAdvertisementDataServiceUUIDsKey: [peripheralUUID]] as [String : Any]
self.manager.startAdvertising(data)
In your
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
respective your
func peripheralManager(_ peripheral: CBPeripheralManager, didPublishL2CAPChannel PSM: CBL2CAPPSM, error: Error?) {
offer the PSM value (= kind of socket handle (UInt16), for Bluetooth stream connections):
let data = withUnsafeBytes(of: PSM) { Data($0) }
if let characteristic = self.characteristic {
characteristic.value = data
self.manager.updateValue(data, for: characteristic, onSubscribedCentrals: self.subscribedCentrals)
}
finally in
func peripheralManager(_ peripheral: CBPeripheralManager, didOpen channel: CBL2CAPChannel?, error: Error?)
open an input stream:
channel.inputStream.delegate = self
channel.inputStream.schedule(in: RunLoop.current, forMode: .default)
channel.inputStream.open()
where the delegate could look something like this:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasBytesAvailable:
if let stream = aStream as? InputStream {
...
//buffer is some UnsafeMutablePointer<UInt8>
let read = stream.read(buffer, maxLength: capacity)
print("\(read) bytes read")
}
case ...
}
iOS app with Central Role
Assuming you have something like that in your iOS code:
func sendImage(imageData: Data) {
self.manager = CBCentralManager(delegate: self, queue: nil)
self.imageData = imageData
self.bytesToWrite = imageData.count
NSLog("start")
}
then you can modify your peripheral on your iOS client to work with the L2Cap channel like this:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
...
if let characteristicValue = characteristic.value {
let psm = characteristicValue.withUnsafeBytes {
$0.load(as: UInt16.self)
}
print("using psm \(psm) for l2cap channel!")
peripheral.openL2CAPChannel(psm)
}
}
and as soon as you are notified of the opened channel, open the output stream on it:
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?)
...
channel.outputStream.delegate = self.streamDelegate
channel.outputStream.schedule(in: RunLoop.current, forMode: .default)
channel.outputStream.open()
Your supplied stream delegate might look like this:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasSpaceAvailable:
if let stream = aStream as? OutputStream, let imageData = self.imageData {
if self.bytesToWrite > 0 {
let bytesWritten = imageData.withUnsafeBytes {
stream.write(
$0.advanced(by: totalBytes),
maxLength: self.bytesToWrite
)
}
self.bytesToWrite -= bytesWritten
self.totalBytes += bytesWritten
print("\(bytesWritten) bytes written, \(bytesToWrite) remain")
} else {
NSLog("finished")
}
}
case ...
There is a cool WWDC video from 2017, What's New in Core Bluetooth, see here https://developer.apple.com/videos/play/wwdc2017/712/
At around 14:45 it starts to discuss how L2Cap channels are working.
At 28:47, the Get the Most out of Core Bluetooth topic starts, in which performance-related things are discussed in detail. That's probably exactly what you're interested in.
Finally, at 37:59 you will see various possible throughputs in kbps.
Based on the data shown on the slide, the maximum possible speed with L2CAP + EDL (Extended Data Length) + 15ms interval is 394 kbps.
Please have a look at this comment
The following snippet is taken from there
ble.requestMtu(yourDeviceId, 512, () => {
console.log('MTU Size ok.');
}, error => {
console.log('MTU Size failed.');
});
It is suggesting that you need to request the Mtu after connection and then I think you can break your message into chunks of 512 bytes rather than 20 bytes.
They have done this for android specific issue
First I should say that there are already tons of blog posts and Q&As on the exact same topic, so please read them first.
If you run iPhone 7, you have the LE Data Length Extension. The default MTU is also 185 bytes, which means you can send notifications or write without response commands with 182 bytes of payload. And please make sure you absolutely not use Write With Response or Indications since that will almost stall the transfer. When you run iOS in central mode you are restricted to 30 ms connection interval. Using a shorter connection interval can have benefits, so I would suggest you to run iOS in peripheral mode instead so you from the central side can set a connection interval of something short, say 12 ms. Since iPhone X and iPhone 8, you can also switch to the 2MBit/s PHY to get increased transfer speed. So to answer your actual question why BLE data transfer is handicapped: it's not, at least if you follow best practice.
You also haven't told anything about the system that runs your desktop application. If it supports 2 MBit/s PHY, LE Data Length Extension and a MTU of at least 185, then you should be happy and make sure your connections use all those features. If not, you should still get higher performance if you enable at least one of them.

How to read available text (not ending with new line) from TCP socket using BufferedReader

I'm communication with a server through a tcp socket connection, i'm able to read lines that ends with \n fine, however when the line is not terminated (ends in \n) i'm not able to read it. I tried the following but it didn't work and caused my app to freeze at startup:
private Socket socket;
private BufferedReader input;
public boolean isConnected;
#Override
public void onCreate()
{
try
{
socket = new Socket ("server.ip.add.ress", 23456);
input = new BufferedReader (new InputStreamReader (socket.getInputStream());
handshake();
isConnected = true;
}
catch // Handle IOException and UnknownHostException
}
// custom runnable to read availabe input from the server
private class MyRunnable implements Runnable
{
private volativle String value;
public String getValue()
{
return value;
}
#Override
public void run()
{
int count;
char[] buffer = new char[10]; // expected message 'username: '
try
{
count = input.read (buffer, 0, 10);
if (count > 0) value = new String (buffer);
}
catch // IOException
}
}
// when connection is established with server expect 'username: ' from
// the server and send the user name back to it
public void handshake()
{
MyRunnable runnable = new MyRunnable();
try
{
Thread thread = new Thread (runnable);
thread.start();
thread.join();
String greeting = runnable.getValue();
if (greeting.equals ("username: ")) // Send username back
}
catch // InterruptedException
}
why is it hanging? and how can i read a non terminated line?
Edit:
To clarify: The server sends the greeting message username: immediately after the connection is established with a client, the client wait for the greeting and send back it's username when received (that's what handshake() does), if no handshake the client disconnects otherwise it start listening for incoming messages. Because i need to know if handshake is complete before starting the listener i had to use Thread.join().
The problem: Thanks for the comments and answers below, it turned out that BufferedReader.read() blocks the thread and waits until something is sent from the server and if nothing is being sent it causes the app to hang, Therefor there's no way to find out if the line has ended.
The solution: In my specific situation i just wanted to know if a specific message is sent "username: " so i used read (buffer, 0, 10) to read exactly 10 characters (the length of "username: "), and because it blocks if nothing is sent i used Thread.join (1000) which waits only one second and then if nothing received i disconnect the client.
Why is it hanging?
This is what it is suppose to be. It will block the thread if no data is available to read. This is also why you want to put it in a background thread.
Can it not just return if nothing is available?
What you are looking for is ready(), which will tell you whether there is available data or not.
Indicates whether this reader is ready to be read without blocking.
Returns
true if this reader will not block when read is called, false if unknown or blocking will occur.
But you should be very careful when using this function. Because networking is a lot about timing. The fact that you don't have any data to read at this second doesn't necessary mean that it won't be any data in the next second.
So a better design of the server should be more or less as the following:
If the username is found, return the username
If the username is not found, return an error message to let the client side know that the username is not found
There's no need for the thread. Your goal is to wait until you've read what you've been waiting for. Why not just let read() perform the wait for you?
What you're struggling with is the classic problem of TCP communication: "when do I know that I've got everything the server sent?"
In your case, you're expecting to read bytes until the collection of bytes ends with "username: ". So, change your algorithm to perform 1 byte reads (filling a buffer as you go) until that buffer ends with "username: ".
You can make a more complicated algorithm -- which would be more efficient -- that would attempt to read multiple bytes at a time and append them to a buffer -- performing your check each time. But either strategy is logically equivalent.
I also recommend just using the InputStreamReader. It has various read() methods. I am a bit suspicious about the BufferedInputReader, especially when dealing with data that isn't newline terminated. I'm probably just paranoid. I've just never used it when writing TCP client/server programs, so I'm not sure.

How can Android broadcast BLE local name like in iOS?

I've got an Android app advertising ble broadcast data with a service uuid and local name. The problem is that this "local name" (aka bluetooth device name) is limited to 8 characters, each is a 16-bit unicode representation, thus 2-bytes per character. Anytime I try to change the name of the device to 9 characters long, ble broadcasting fails to start due to error 1 which is
public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
I know the GAP profile broadcast packet is 27 bytes long and 7 are used for headers, thus 20 should remain free for use, not just 16?
Here's the real pickle that tickles my nickel:
When iOS is broadcasting ble advertisement header, I get the full local name of the device as part of the ScanRecord, not just limited to 16 bytes. Only part of the broadcast, I'm not establishing a GATT connection here.
How is iOS able to do this? For example, on my Android, I was able to retrieve a 14-character, 28 bytes long unique id from an iOS advertisement broadcast. 28 bytes is longer than the 27-byte limit imposed by bluetooth 4.0 standard. How is it that Android was able to pick up the full broadcast longer than 27 bytes? And how come my "local name" or device name can only be at most 8 characters or 16 bytes or it won't be able to start broadcasting?
This is my code:
final BluetoothLeAdvertiser advertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
//advertise settings
final AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
settingsBuilder.setConnectable(true);
settingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
//advertise data
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
ParcelUuid uuid = new ParcelUuid(UUID.fromString("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));
dataBuilder.addServiceUuid(uuid);
mBluetoothAdapter.setName("12345678"); //8 characters works, 9+ fails
dataBuilder.setIncludeDeviceName(true);
if (advertiser!=null) {
advertiser.startAdvertising(settingsBuilder.build(), dataBuilder.build(), mAdvertiseCallback);
}
mAdvertiseCallback = new AdvertiseCallback() {
#Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
Log.d(LOGN, "BLE STARTED ADVERTISING BROADCAST "+settingsInEffect);
}
#Override
public void onStartFailure(int errorCode) {
super.onStartFailure(errorCode);
Log.d(LOGN, "BLE ADVERTISING FAILED TO START: "+errorCode);
}
};
Is there a way for Android to include the full local name as part of the broadcast like iOS does?
Thanks!
If you view the overloads for StartAdvertising, you can see there is an additional parameter for the scan response. You can put the local name in the scan response data instead. Remember to remove setIncludeDeviceName from the data builder.
AdvertiseData.Builder scanResponseBuilder = new AdvertiseData.Builder();
mBluetoothAdapter.setName("123456789")
scanResponseBuilder.setIncludeDeviceName(true);
//...
if (advertiser!=null) {
advertiser.startAdvertising(settingsBuilder.build(), dataBuilder.build(), scanResponseBuilder.SetIncludeDeviceName.build(), mAdvertiseCallback);
}

USB bulkTransfer errors after sending nulls

I am using FTDI usb-serial dongle to send binary data from my android device,Asus transformer, to a industrial controller.
I am using code from http://android.serverbox.ch/?p=370&cpage=1 (thanks Manuel Di Cerbo )
It works fine as long as I do not send nulls, but after a null is sent the rest of the data is corrupted.
Questions.
Should it work? and/or
Can anyone confirm what I am seeing?
public byte[] dataframe = new byte[256];
......
......
for(;;){//this is the main loop for transferring
conn.bulkTransfer(epOUT, dataframe, 10, 0);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(mStop){
mStopped = true;
return;
}
l("sent "+dataframe[0]+","+dataframe[1]+","+dataframe[2]);
}
}
This thread resends the data periodically, currently 10 bytes but it will be more, while the main app updates some of the values. Normally most of the bytes would be zero, with some changing data. At the moment my experimental code runs OK if I use non zero values. The log shows the values are still correct at that point so I am assuming the problem is after the bulkTransfer.

Categories

Resources