RxAndroidBLE already connected and can't send data to IoT device - java

I have an IoT device with BLE on it and also I have a smartphone which support BLE protocol.
I am using RxAndroidBle: com.polidea.rxandroidble2:rxandroidble:1.11.1
The problem is to communicate each other. I have established connection:
#OnClick(R.id.connectButton)
void onConnectButton() {
if (rxBleDevice == null) {
if (myViewModel.getMacAddress().getValue() != null) {
if (!myViewModel.getMacAddress().getValue().isEmpty()) {
// get BLE device
rxBleDevice = SampleApplication.getRxBleClient(this.getActivity())
.getBleDevice(myViewModel.getMacAddress().getValue());
// establish connection
connectionObservable = rxBleDevice.establishConnection(false)
.takeUntil(disconnectTriggerSubject);
// .compose(ReplayingShare.instance());
/*
reason: no instance(s) of type variable(s) T exist so that ReplayingShare<T> conforms to
ObservableTransformer<? super RxBleConnection, ? extends R
*/
statusTextView.setText(R.string.connected);
}
}
} else {
triggerDisconnect();
statusTextView.setText(R.string.disconnected);
}
}
and then I just use the connectionObservable to send data like this:
if (rxBleDevice != null) {
// if (isConnected()) {
final Disposable disposable = connectionObservable
.firstOrError()
.flatMap(rxBleConnection -> rxBleConnection.writeCharacteristic(uuid, HexString.hexToBytes(data)))
.subscribe(
bytes -> onWriteSuccess(bytes),
throwable -> onWriteFailure(throwable)
);
compositeDisposable.add(disposable);
// }
}
The error what I always got is:
Already connected to device with MAC address EA:A5:34:E6:28:2E, but if i try to isConnected() always says that they are not connected. Is there a way to send data every 300 ms to IoT device?
Full stack trace below.
I/VideoFragment: Write error:
com.polidea.rxandroidble2.exceptions.BleAlreadyConnectedException: Already connected to device with MAC address EA:A5:34:E6:28:2E
at com.polidea.rxandroidble2.internal.RxBleDeviceImpl$1.call(RxBleDeviceImpl.java:84)
at com.polidea.rxandroidble2.internal.RxBleDeviceImpl$1.call(RxBleDeviceImpl.java:72)
at io.reactivex.internal.operators.observable.ObservableDefer.subscribeActual(ObservableDefer.java:33)
at io.reactivex.Observable.subscribe(Observable.java:12284)
at io.reactivex.internal.operators.observable.ObservableTakeUntil.subscribeActual(ObservableTakeUntil.java:38)
at io.reactivex.Observable.subscribe(Observable.java:12284)
at io.reactivex.internal.operators.observable.ObservableElementAtSingle.subscribeActual(ObservableElementAtSingle.java:37)
at io.reactivex.Single.subscribe(Single.java:3666)
at io.reactivex.internal.operators.single.SingleFlatMap.subscribeActual(SingleFlatMap.java:36)
at io.reactivex.Single.subscribe(Single.java:3666)
at io.reactivex.Single.subscribe(Single.java:3652)
at com.example.automotive.Fragments.VideoFragment$1.onMove(VideoFragment.java:275)
at io.github.controlwear.virtual.joystick.android.JoystickView$2.run(JoystickView.java:860)
at android.os.Handler.handleCallback(Handler.java:914)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7560)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
isConnected method:
private boolean isConnected() {
return rxBleDevice.getConnectionState() == RxBleConnection.RxBleConnectionState.CONNECTED;
}

Is there a way to send data every 300 ms to IoT device?
Of course there is. If there is no external source of the data to send one could use code similar to:
bleDevice.establishConnection(false)
.flatMap(rxBleConnection ->
Observable.interval(300, TimeUnit.MILLISECONDS)
.flatMap(ignored -> rxBleConnection.writeCharacteristic(uuid, HexString.hexToBytes(data)))
)
.subscribe(
ignored -> {},
error -> { /* log or something */ }
);
The above assumes nothing else is subscribing to bleDevice.establishConnection(false) at the same time.
I think what you wanted to ask is why you get this exception and how to live with it. This exception was introduced to protect users from calling a stateful BLE transmission from multiple places in the code and messing it up. There is a wiki page about it.
You can share a Observable<RxBleConnection by using RxReplayingShare for instance. Then you will not get BleAlreadyConnectedException. You have tried that but apparently commented out the line because the compiler couldn't find out what is the type of object it will replay/share. Perhaps specifying it with ReplayingShare.<RxBleConnection>instance() would help?

Related

Connect to pusher in android app, multiple or once?

In my app (streaming series, movies) I have a section for users that can set Reminder for the series or movies. And I implement Pusher to receive server message for reminding data.
Is it true that I connect to channel for each item in the reminder list?? or I should connect to the pusher once and in the pusher event get related series/ movies message?(Server-side implemented pusher for each reminder list items, should we change server-side implementation or I can connect to pusher for each items? )
This is my Implementation for pusher:
public Pusher getPusher() throws Exception {
if (pusher == null) {
HttpAuthorizer auth = new HttpAuthorizer(BuildConfig.PUSHER);
HashMap<String, String> authHeader = new HashMap<>();
authHeader.put("Authorization", SharedPref.INSTANCE.read(AUTH_TOKEN, ""));
auth.setHeaders(authHeader);
PusherOptions option = new PusherOptions();
option.setCluster(BuildConfig.PUSHER_CLUSTER);
option.setAuthorizer(auth);
pusher = new Pusher(BuildConfig.PUSHER_KEY, option);
pusher.subscribePrivate("private-app_ch." + serialId, new PrivateChannelEventListener() {
#Override
public void onAuthenticationFailure(String s, Exception e) {
Timber.i("pusher onAuthenticationFailure " + e.getMessage());
}
#Override
public void onSubscriptionSucceeded(String s) {
Timber.i("pusher onSubscriptionSucceeded: " + s);
}
#Override
public void onEvent(String s, String s1, String result) {
Timber.i("pusher onEvent" + s + ":" + s1);
Timber.i("pusher onEvent" + result);
}
}, "App\\Events\\AppBroadcastEvent");
}
return pusher;
}
The best practice for this would be to maintain one connection to Channels but make a subscription for each item in the reminder list.
So you would call pusher.subscribePrivate for each item in the reminder list and then on the server side publish to each individual Channel when a reminder needs to be sent.
For example if a user wanted to be reminded about 'Stranger Things' and 'Orange is the new black' you would subscribe to both:
pusher.subscribePrivate("private-app_ch.strangerthings"
and
pusher.subscribePrivate("private-app_ch.orangeisthenewblack"
Your server would then publish reminders about 'Stranger Things' to the Stranger things channel and OISTNB to the OISTNB channel and so on.
This way only relevant updates are sent to the client (server-side filtering). If you only subscribe to one channel the client will get messages they may not want updates about and you would have to filter these out on the client side.
This is also explained here: https://support.pusher.com/hc/en-us/articles/360025398514-Should-i-subscribe-to-lots-of-Channels-
One additional point that is worth considering is that Channels will only maintain an active connection when the app is open. The connection will be closed when the app is backgrounded/closed. This means for reminders to be sent the user would always have to be in your app. You may want to consider also sending push notifications when the app is closed so the user does not miss reminders.

Netty Correlating Request and Responses

I want to write a proxy for a TCP binary protocol. I’m using the HexDump example in Netty’s repo as a guide.
https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/proxy
This works fine. But I sometimes want to modify the response based on the original request.
Looking around it seems that using the inbound channels AttributeMap could be the place to store such request details. (Some more details below)
io.netty.util.AttributeMap
But while it sort of works sometimes one request overwrites the details of another request.
This makes sense, Netty is asynchronous and you can’t really guarantee when somethings going to happen.
So I was wondering how can I reliably correlate each request with is response. Note I can’t
change the protocol, this might have been one way to pass details between request and response.
Thanks for your insight.
HexDumpFrontendHandler
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws InterruptedException {
…
ctx.channel().attr(utils.REQUEST_ATTRIBUTE).set(requestDetails);
…
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
final Channel inboundChannel = ctx.channel();
// Start the connection attempt.
Bootstrap b = new Bootstrap();
b.group(inboundChannel.eventLoop())
.channel(ctx.channel().getClass())
.handler(new HexDumpBackendHandler(inboundChannel))
.option(ChannelOption.AUTO_READ, false);
ChannelFuture f = b.connect(remoteHost, remotePort);
outboundChannel = f.channel();
f.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
// connection complete start to read first data
inboundChannel.read();
} else {
// Close the connection if the connection attempt has failed.
inboundChannel.close();
}
});
}
HexDumpBackendHandler
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
…
RequestDetails requestDetails = inboundChannel.attr(utils.REQUEST_ATTRIBUTE).getAndRemove();
…
}
My solution (work around?) to this was the following. The protocol I was working with couldn't guarantee a unique identifier per request globally but it did uniquely identify request's within a tcp connection.
So the following combination allowed me to create a ConcurrentHashMap with the following as the key
host + ephemeral port + identifier local to the connection
This work for my case. I'm sure their other ways to solve it within the Netty framework itself

Internet Connection Check behaving weirdly

I've been needing a way to check if the user has Internet. I used this approach:
public class InternetCheck extends AsyncTask<Void, Void, Boolean> {
private Consumer mConsumer;
public interface Consumer {
void accept(Boolean internet);
}
public InternetCheck(Consumer consumer) {
mConsumer = consumer;
execute();
}
#Override
protected Boolean doInBackground(Void... voids) {
try {
Socket sock = new Socket();
sock.connect(new InetSocketAddress("8.8.8.8", 53), 1500);
sock.close();
Log.w("INTERNET CHECK", "has Internet");
return true;
} catch (IOException e) {
Log.w("INTERNET CHECK", "NO Internet");
return false;
}
}
#Override
protected void onPostExecute(Boolean internet) {
mConsumer.accept(internet);
}
}
... the following way:
new InternetCheck(hasInternet -> { /* do something with boolean response */ });
However, it seems like it isn't as robust as one would think: sometimes (not so often) my phone is connected to WiFi and yet this method returns false.
What are the possible scenarios/diagnostics as of why this behaviour might happen?
My personal experience is that it seems to happen when my phone has my application open and is connected to a WiFi. Then, the phone goes to sleep and I move places and open it back up to the application on a new WiFi connection. The check returns false despite my phone displaying that it clearly has established the new WiFi connection (since it was a saved network).
However, this is not the only way this method seems to have failed. Another developer had it happen while he didn't change his WiFi connection.
Wifi connections are disconnected when the phone goes to sleep. It takes time for the connection to be re-established when the phone wakes up again.
Your 1.5 second connection timeout is too short to accommodate that extra delay.
Also, the DNS server, or the intermediate network, may simply be busy. Again, the connection timeout is too short.
Before inventing your own way to check internet connectivity, you should read these:
Android Developer Guide: Determine and monitor the connectivity status
StackOverflow: Android check internet connection

How to let netty channel.writeAndFlush() throws Exceptions when no TCP ACK response

I am developing an IM server with the Netty4 frame. Meanwhile I used the method named channel.writeAndFlush() to send messages to the client. However, when the socket of the client on the mobile phone shutting down unusually such as turning off the network connections or turning on the airplane mode on the device, the netty4 frame counld not find that the corresponding channel being inactive. Moreover, the ChannelGroupFuture returned by the writeAndFlush() method reports the sending result success with the method ChannelGroupFuture.isSuccess().
So, why the ChannelGroupFuture didn't return me the sending is failed without throwing any exception?
ChannelGroupFuture future = connectionService.sendMessageToUser(msgBase, toUid).sync();
future.addListeners(new ChannelGroupFutureListener(){
#Override
public void operationComplete(ChannelGroupFuture future)
throws Exception {
if(future.isDone() && future.isSuccess()){
chatMessageService.saveSentChatMessage(msgBase);
} else if(!future.isSuccess()){
chatMessageService.saveUnsentChatMessage(msgBase);
}
});
public ChannelGroupFuture writeAndFlush(Object message, ChannelMatcher matcher) {
if (message == null) {
throw new NullPointerException("message");
}
if (matcher == null) {
throw new NullPointerException("matcher");
}
if(matcher instanceof AttributeChannelMatcher){
Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(1);
AttributeChannelMatcher<T> attributeMatcher = (AttributeChannelMatcher<T>) matcher;
Channel c = nonServerChannelMap.get(attributeMatcher.getAttributeKeyValue());
futures.put(c, c.writeAndFlush(safeDuplicate(message)));
ReferenceCountUtil.release(message);
return new DefaultChannelGroupFuture(this, futures, executor);
}else{
Map<Channel, ChannelFuture> futures = new LinkedHashMap<Channel, ChannelFuture>(size());
for (Channel c : nonServerChannelMap.values()) {
if (matcher.matches(c)) {
futures.put(c, c.writeAndFlush(safeDuplicate(message)));
}
}
ReferenceCountUtil.release(message);
return new DefaultChannelGroupFuture(this, futures, executor);
}
}
You can't. TCP writes are asynchronous with respect to the application. They don't wait for ACKs before they return. There is a send buffer on the sending side and a receive buffer on the receiving side. All this means that it could take several writes and quite a few seconds before you detect a broken connection.

Publishing commands to device in IBM IoT using MQTT in Java

I am currently trying to publish a command to a specific topic in the IBM IoT Foundation MQTT Broker using a Java web application. My application is already able to listen to device events and act on them, however publishing commands to the device is a problem. I know for sure that my device is listening to the proper topic for commands, so what could be the problem? More specifically, here is the command I call to publish to the topic (from my Java app):
publish("iot-2/cmd/" + MQTTUtil.getDefaultCmdId() + "/fmt/json", rawJSONCommand, false, 0);
System.out.println("Finished sending command!");
Where the "publish" method is defined as follows:
public void publish(String topic, String message, boolean retained, int qos) { // check if client is connected
if (isMqttConnected())
{
// create a new MqttMessage from the message string
MqttMessage mqttMsg = new MqttMessage(message.getBytes());
// set retained flag
mqttMsg.setRetained(retained);
// set quality of service
mqttMsg.setQos(qos);
try {
System.out.println("About to send!");
client.publish(topic, mqttMsg);
System.out.println("Finished sending!"); }
catch (MqttPersistenceException e)
{ e.printStackTrace(); }
catch (MqttException e)
{ e.printStackTrace(); } }
else {
System.out.println("Connection lost!"); connectionLost(null);
} }
All that happens is that I enter the method, I get "About to send!" printed on my console as the code specifies, and then the actual 'client.publish(topic, mqttMsg)' call blocks my program indefinitely.. Eventually, after blocking for a while, I get the following error:
org.eclipse.paho.client.mqttv3.internal.ClientState checkForActivity SEVERE: a:2uwqwc:<MY_APP_NAME>: Timed out as no write activity, keepAlive=60,000 lastOutboundActivity=1,452,646,209,624 lastInboundActivity=1,452,646,149,303 time=1,452,646,329,628 lastPing=0
Thanks for the help!
If you are publishing from an application, are you specifying the device type and device id?
myAppClient.publishCommand(deviceType, deviceId, "stop", data);
Refer to section in documentation about publishing commands to connected devices.
https://docs.internetofthings.ibmcloud.com/java/java_cli_app.html

Categories