Try to connect to MQTT Server with a Broadcast Receiver when WiFi is connected (Paho) - mqtt

I have a Broadcast receiver that checks WIFI_STATE_CHANGE to see if I have connected to a certain WiFi network. For example if I am coming home, I want a certain MQTT message to be sent. The problem I have is that it connects and sends the MQTT message, only when run the app the first time.
Process:
If I build the application and run it on the device and it recognised my home WiFi it sends the message.
I turn off Wifi from the device, and turn it back on again.
I get "Failure" which is a message when the MQTT connection to the server could not be established.
What I would need is that after I reconnect to the network, instead of "Failure" to get "Connected" but somehow it never happens...what could be wrong?
PS. I think it has to do with the fact that when WiFi is detected, the Broadcast Receiver runs the connection code, although Internet is not available at that point of time (obtaining IP etc.)
Here is the code of the Broadcast receiver:
package me.app.comehomedemo;
import ...
public class SynchronizeBroadcastReceiver extends BroadcastReceiver {
MqttAndroidClient client;
static String MQTTHOST = "myhost";
static String USERNAME = "myusername";
static String PASSWORD = "mypassword";
static String topicStr = "/topic/mac/control";
static String payload = "1";
#Override
public void onReceive(final Context context, Intent intent) {
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (info.isConnected()) {
WifiManager wifiManager = ( WifiManager ) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ip = wifiInfo.getIpAddress();
Toast.makeText(context, String.valueOf(ip), Toast.LENGTH_SHORT).show();
String ssid = wifiInfo.getSSID();
if (ssid.equals("\"mySSID\"")) {
String clientId = MqttClient.generateClientId();
client = new MqttAndroidClient(context.getApplicationContext(), MQTTHOST, clientId);
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(USERNAME);
options.setPassword(PASSWORD.toCharArray());
// options.setAutomaticReconnect(true);
try {
IMqttToken token = client.connect(options);
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
// We are connected
Toast.makeText(context, "Connected", Toast.LENGTH_SHORT).show();
try {
client.publish(topicStr, payload.getBytes(), 0, false);
} catch (MqttException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
// Something went wrong e.g. connection timeout or firewall problems
Toast.makeText(context, "Failure", Toast.LENGTH_SHORT).show();
}
});
} catch (MqttException e) {
e.printStackTrace();
}
Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
MediaPlayer mp = MediaPlayer.create(context.getApplicationContext(), notification);
mp.start();
}
}
}
}

I have managed to solve it by waiting 2 seconds and then running the task. Used this solution and it worked. I had to wait for the Internet connection to get ready!

Since waiting 2 seconds has solved your problem, then it might be that the Wifi broadcast comes too early, before there is a connection established (like DHCP gives your phone IP and establishes the routes) for the MQTT connect and publish packets to be properly delivered.
But what happens if some other user needs to wait 10 and not 2 seconds?
My suggestion is to set the automatic reconnect option in MqttConnectOptions and then use the connection callback to publish the needed info to the broker and finally disconnect in publish callback:
private IMqttActionListener mConnectCallback = new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken token) {
try {
client.publish(topicStr, new MqttMessage(payload.getBytes()), null, mPublishCallback);
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void onFailure(IMqttToken token, Throwable ex) {
}
};
private IMqttActionListener mPublishCallback = new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken token) {
// TODO disconnect
}
#Override
public void onFailure(IMqttToken token, Throwable ex) {
}
};
MqttAndroidClient client = new MqttAndroidClient(context, MQTTHOST, "my_id");
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(USERNAME);
options.setPassword(PASSWORD.toCharArray());
options.setAutomaticReconnect(true);
client.connect(options, null, mConnectCallback);

Related

How to implement auto-reply between MQTT client

I want to implement a MQTT client which can auto-reply when it get a message.
for example:
client1 send a "request" message to client2 (by topic "/toClient2").
client2 get the "request" message and reply a "response" message to client1 (by topic "/toClient1").
I use org.eclipse.paho.client.mqttv3 v1.2.5 and mosquitto as broker to implement it. And it works when client1 send 1 request to client2 in one time. (client2 can reply "response" correctly)
But I found a problem that if client1 sent a lot (e.g. 100, 10000) "request" Continuously to client2, client2 can't publish message or receive message.
Then client2 would disconnect.
my code is here:
package test;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Client implements MqttCallback {
private final int id;
private MqttClient mqttClient;
private int qos = 1;
private int numGet = 0;
private int numSend = 0;
public Client(int id) {
this.id = id;
String HOST = "tcp://127.0.0.1:1883";
MqttConnectOptions options = new MqttConnectOptions();
try {
mqttClient = new MqttClient(HOST, String.valueOf(id), new MemoryPersistence());
options.setCleanSession(true);
options.setConnectionTimeout(10);
options.setKeepAliveInterval(20);
options.setMaxInflight(1000);
mqttClient.setCallback(this);
mqttClient.connect(options);
System.out.println("Connected to MQTT Broker");
} catch (MqttException e) {
e.printStackTrace();
}
}
private void subscribe(String topic) {
try {
mqttClient.subscribe(topic, qos);
} catch (Exception e) {e.printStackTrace();}
}
public void publish(String topic, String payload) {
try {
boolean retained = false;
mqttClient.publish(topic, payload.getBytes(), qos, retained);
} catch (Exception e) {e.printStackTrace();}
}
#Override
public void connectionLost(Throwable cause) {}
#Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println(topic + " get: " + message);
if (id == 2)
publish("/toClient1", "Response");
numGet++;
}
#Override
public void deliveryComplete(IMqttDeliveryToken token) { numSend++; }
public static void main(String[] args) throws InterruptedException {
Client client1 = new Client(1);
Client client2 = new Client(2);
String topic1 = "/toClient1";
String topic2 = "/toClient2";
client1.subscribe(topic1);
client2.subscribe(topic2);
int n = 100;
for (int i = 0; i < n; i++) {
client1.publish(topic2, "request");
}
Thread.sleep(1000);
System.out.println("Client1 sent messages: " + client1.numSend);
System.out.println("Client2 sent messages: " + client2.numSend);
}
}
I got the output like this:
Connected to MQTT Broker
Connected to MQTT Broker
/toClient2 get: request
/toClient1 get: Response
Client1 sent messages: 100
Client2 sent messages: 0
1月 29, 2023 1:02:10 下午 org.eclipse.paho.client.mqttv3.internal.ClientState checkForActivity
严重: 2: Timed out as no activity, keepAlive=20,000,000,000 lastOutboundActivity=6,831,960,648,000 lastInboundActivity=6,811,951,900,600 time=6,851,960,579,800 lastPing=6,831,960,650,700
等待来自服务器的响应时超时 (32000)
at org.eclipse.paho.client.mqttv3.internal.ExceptionHelper.createMqttException(ExceptionHelper.java:31)
at org.eclipse.paho.client.mqttv3.internal.ClientState.checkForActivity(ClientState.java:747)
at org.eclipse.paho.client.mqttv3.internal.ClientComms.checkForActivity(ClientComms.java:818)
at org.eclipse.paho.client.mqttv3.internal.ClientComms.checkForActivity(ClientComms.java:804)
at org.eclipse.paho.client.mqttv3.TimerPingSender$PingTask.run(TimerPingSender.java:79)
at java.base/java.util.TimerThread.mainLoop(Timer.java:556)
at java.base/java.util.TimerThread.run(Timer.java:506)
I suspect that I should not do publish() in public void messageArrived(String topic, MqttMessage message) callback, so I try to use a queue to enqueue the "request" message and use a new Thread to consume the queue and do publish(). But it get the same result.

spring websocket : SubProtocolWebSocketHandler

I have stability problems with Spring web-sockets. Sometimes I have a similar exception to that describe in https://jira.spring.io/browse/SPR-12812.
The patch is not available, then I have implemented my own code with a custom SubProtocolWebSocketHandler.
public class WBSSubProtocolWebSocketHandler extends SubProtocolWebSocketHandler {
private static final Logger LOG = LoggerFactory.getLogger(WBSSubProtocolWebSocketHandler.class);
public WBSSubProtocolWebSocketHandler(MessageChannel clientInboundChannel, SubscribableChannel clientOutboundChannel) {
super(clientInboundChannel, clientOutboundChannel);
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
LOG.debug("WebSocket Connection closed for client with session ID {}", session.getId());
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// WebSocketHandlerDecorator could close the session
// https://jira.spring.io/browse/SPR-12812
if (!session.isOpen()) {
LOG.warn("WebSocket Connection established for client with session ID {} was closed.", session.getId());
return;
}
super.afterConnectionEstablished(session);
LOG.debug("WebSocket Connection established for client with session ID {}", session.getId());
}
#Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
super.handleTransportError(session, exception);
LOG.warn("WebSocket transport error for client with session ID {}", session.getId());
}
#Override
public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception {
super.handleMessage(session, message);
LOG.debug("Websocket incoming message ({}) from client with session ID {}", message.getPayload().toString(), session.getId());
}
#Override
public void handleMessage(Message message) throws MessagingException {
super.handleMessage(message);
LOG.debug("Websocket incoming message : {}, header {}", message);
}
}
Now my problem is to reproduce the exception to see if the problem is solved. I tried various ways, but without success. I can not reproduce the closing of the connection. Does anyone have an idea?
We also have a second problem. The client application (angularjs application) sometimes reports that the socket-web connection is lost. But I do not understand why because on the server I have no error / warning in the logs.
How can I identify the problem and reproduce it?

c# server and android client,connectivity

i am trying to develop an application in c# which acts as a server for an android phone.i am using 32feet.net for bluetooth in c# and i have a server running in android, which simply sends a socket to server. the server running in pc need to listen the connection and display ,the status of connection. all these things are base for my project. the server code is as shown :
namespace testserver
{
class Program
{
static void Main(string[] args)
{
BluetoothClient bc = new BluetoothClient();
BluetoothDeviceInfo[] dev;
BluetoothDeviceInfo td=null;
Guid id = new Guid("{00112233-4455-6677-8899-aabbccddeeff}");
// Console.WriteLine(id.ToString());
// Console.Read();
dev = bc.DiscoverDevices();
foreach (BluetoothDeviceInfo d in dev)
{
if (d.DeviceName == "ST21i")//my phone name
{
td=d;
break;
}
}
try
{
BluetoothAddress addr = td.DeviceAddress;
BluetoothListener bl = new BluetoothListener(addr, id);
bl.Start();
if (bl.AcceptSocket() != null)
Console.WriteLine("Success");
}
catch (Exception e)
{
Console.WriteLine("Exception : "+e.Message);
Console.Read();
}
}
}
}
and here is my android code :
public class MainActivity extends Activity {
BluetoothAdapter adapter;
BluetoothDevice bd;
BluetoothSocket sock;
OutputStream ostr;
int REQUEST_ENABLE_BT;
String str="5C:AC:4C:DD:CC:0D";
private static final UUID id=UUID.fromString("00112233-4455-6677-8899- aabbccddeeff");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
adapter=BluetoothAdapter.getDefaultAdapter();
if (!adapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
Button b = (Button) findViewById(R.id.button1);
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "clicked button", Toast.LENGTH_LONG).show();
try
{
bd=adapter.getRemoteDevice(str); Toast.makeText(getApplicationContext(),"Server is running at "+bd.getName().toString()+"...", Toast.LENGTH_LONG).show();
sock=bd.createInsecureRfcommSocketToServiceRecord(id); sock.connect();
ostr=sock.getOutputStream();
ostr.write(0);
}
catch(Exception e)
{
Toast.makeText(getApplicationContext(),e.getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
}
my problems are :
1) in pc i am getting an exception, the requested address is not valid in its context(so that server cant run )
2)in phone, the service discovery failed( because of unavailability of server)
how can i correct the server and run the program ?
i changed the bluetooth listener object's creation from
BluetoothListener bl = new BluetoothListener(addr, id); to
BluetoothListener bl = new BluetoothListener(id); and everything worked fine..

Tried to read incoming SMS content but getting Error in Blackberry

Hi friends i am trying to read incoming sms but getting warning like this . Invocation of questionable method: java.lang.String.(String) found in: mypackage.MyApp$ListeningThread.run()
Here is my code is
public class MyApp extends UiApplication {
//private ListeningThread listener;
public static void main(String[] args) {
MyApp theApp = new MyApp();
theApp.enterEventDispatcher();
}
public MyApp() {
invokeAndWait(new Runnable() {
public void run() {
ListeningThread listener = new ListeningThread();
listener.start();
}
});
pushScreen(new MyScreen());
}
private static class ListeningThread extends Thread {
private boolean _stop = false;
private DatagramConnection _dc;
public synchronized void stop() {
_stop = true;
try {
_dc.close(); // Close the connection so the thread returns.
} catch (IOException e) {
System.err.println(e.toString());
}
}
public void run() {
try {
_dc = (DatagramConnection) Connector.open("sms://");
for (;;) {
if (_stop) {
return;
}
Datagram d = _dc.newDatagram(_dc.getMaximumLength());
_dc.receive(d);
String address = new String(d.getAddress());
String msg = new String(d.getData());
if(msg.startsWith("START")){
Dialog.alert("hello");
}
System.out.println("Message received: " + msg);
System.out.println("From: " + address);
System.exit(0);
}
} catch (IOException e) {
System.err.println(e.toString());
}
}
}
}
Please correct me where i am wrong.Is possible give me some code to read incoming sms content in blackberry.
A few points about your code:
That invokeAndWait call to launch a thread makes no sense. It doesn't harm, but is kind of waste. Use that method only to perform UI related operations.
You should try using "sms://:0" as param for Connector.open. According to the docs, a parameter with the form {protocol}://[{host}]:[{port}] will open the connection in client mode (which makes sense, since you are on the receiving part), whereas not including the host part will open it in server mode.
Finally, if you can't get it working, you could use instead the third method specified in this tutorial, which you probably have already read.
The error you quoted is complaining about the use of the String constructor that takes a string argument. Since strings are immutable in Java-ME, this is just a waste. You can use the argument string directly:
Invocation of questionable method: java.lang.String.(String) found in: mypackage.MyApp$ListeningThread.run()
//String address = new String(d.getAddress());
String address = d.getAddress();
// getData() returns a byte[], so this is a different constructor
// However, this leaves the character encoding unspecified, so it
// will default to cp1252, which may not be what you want
String msg = new String(d.getData());

Connection being made, but content is unable to be retrieved from web service

public class ConsumeFactoryThread extends Thread {
private String url;
private HttpConnection httpConn;
private InputStream is;
private CustomMainScreen m;
private JSONArray array;
public ConsumeFactoryThread(String url, CustomMainScreen m){
System.out.println("Connection begin!");
this.url = url;
this.m = m;
}
public void finished(){
m.onFinish(array);
}
public void run(){
myConnectionFactory connFact = new myConnectionFactory();
ConnectionDescriptor connDesc;
connDesc = connFact.getConnection(url);
System.out.println("Connection factory!");
if(connDesc != null)
{
System.out.println("Connection not null!");
httpConn = (HttpConnection) connDesc.getConnection();
is = null;
try
{
final int iResponseCode = httpConn.getResponseCode();
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
System.out.println("Connection in run!");
// Get InputConnection and read the server's response
InputConnection inputConn = (InputConnection) httpConn;
try {
is = inputConn.openInputStream();
System.out.println("Connection got inputstream!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
byte[] data = null;
try {
data = IOUtilities.streamToBytes(is);
System.out.println("Connection got data!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String result = new String(data);
System.out.println("Connection Data: "+result);
try {
array = new JSONArray(result);
//finished();
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
catch(IOException e)
{
System.err.println("Caught IOException: " + e.getMessage());
}
}
}
}
I'm using the blackberry torch 9800 simulator and hardware device for testing.
In the simulator I cannot retrieve the data over wifi, even though the connection to wifi is found. It works when the mobile network is enabled.
Now, when I replace my web service with the Twitter api, I get the data regardless of transport type. I tried adding ;deviceside=false to my url, but nothing. It's not https or anything.
I just want my web service accessed! I know nothing about this mds,bis,bes,bis_b junk.
EDIT:
Jeez. I'm realizing it may be my site. Not using the web service and just retrieving the page, www.example.com, I get nothing. But, google.com or any other site I use retrieves the html. Am I missing headers!?!
Try appending ;interface=wifi to the end of your URL, this will force the simulator to use your simulated Wi-Fi connection, which is your PC's network connection.
You will need to have setup Wi-Fi on the simulator by going to Manage Connections->Set Up Wi-Fi Network, then connect to Default WLAN Network.

Resources