物联网方案之“Android+MQTT+Arduino+以太网盾板”(三.远程遥控)

记录《用于物联网Arduino 项目开发实用案例解析》第六章 物联网模式:遥控的实践过程

一、android部分

1.MainActivity.java

package com.lxm.lightingcontrolsystem;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import org.eclipse.paho.client.mqttv3.MqttException;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView lightIcon = (ImageView) findViewById(R.id.light_icon);
        lightIcon.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    new MQTTClient().publishToMQTT();
                } catch (MqttException e) {
                    Log.e(TAG, "onClick: " + e.getMessage());
                }
            }
        });
    }
}

2.MQTTClient.java

package com.lxm.lightingcontrolsystem;
/**
 * Created by lxm on 2018/12/22.
 */

import android.util.Log;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;



public class MQTTClient {
    private static final String TAG = "MQTTClient";
    private String mqttBroker = "tcp://iot.eclipse.org:1883";
    private String mqttTopic = "codifythings/lightcontrol_lxm";//主题名可以自己定,一致就行
    private String deviceId = "androidClient";
    private String messageContent = "SWITCH";


    public void publishToMQTT()throws MqttException {
        Log.i(TAG, "publishToMQTT: Setting Connection Options");
        MqttConnectOptions options=new MqttConnectOptions();
        options.setCleanSession(true);

        Log.i(TAG, "connectToMQTT: Creating New Client");
        MqttClient client = new MqttClient(mqttBroker, deviceId, new MemoryPersistence());
        client.connect(options);

        client.setCallback(new MqttEventCallback());
        client.subscribe(mqttTopic,0);

        Log.i(TAG, "publishToMQTT: Publishing to Topic");
        MqttMessage mqttMessage = new MqttMessage(messageContent.getBytes());
        //mqttMessage.setQos(2);0或者2都可以被arduino接受到
        mqttMessage.setQos(0);
        client.publish(mqttTopic, mqttMessage);


        Log.i(TAG, "publishToMQTT: Publishing Complete");
        Log.i(TAG, "subscribeToMQTT: Subscribing to Topic");
        Log.i(TAG, "publishToMQTT: Disconnecting from MQTT");
        client.disconnect();
    }
    private class MqttEventCallback implements MqttCallback {
        @Override
        public void connectionLost(Throwable throwable) {

        }
        @Override
        public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

        }

        @Override
        public void messageArrived(String topic, final MqttMessage msg) throws Exception {
            String sensorMessage = new String(msg.getPayload());
            Log.i(TAG, "New Message Arrived from Topic-" + topic+sensorMessage);

        }
    }
}

二、Arduino部分

#include 
#include 
#include 


//1.网络连接所需变量及函数
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress staticIP(192, 168, 31, 159);
EthernetClient client;
const int ledPin = 3;
long int lastMsg = 0;
int tryTimes = 1;

void connectToInternet()
{
  //最初使用的是以下注释的代码,DHCP第一次一般都连不上,静态ip连接的一般都不能用
  /*if (Ethernet.begin(mac) == 0)
    {
    Serial.print("[ERROR] Failed to Configure Ethernet using DHCP");
    Ethernet.begin(mac, staticIP);
    }*/
  //最终用多次连接实现DHCP,试5次,不行再静态连接,一般3次就连上了
  while (Ethernet.begin(mac) == 0 && tryTimes <= 5)
  {
    Serial.print("[ERROR] Failed to Configure Ethernet using DHCP ");
    Serial.print(tryTimes);
    Serial.println("times,Try again 5s later;");
    tryTimes = tryTimes + 1;
    delay(5000);
  }
  //静态连接Ethernet.begin(mac, staticIP)的返回值是void,没法判断连接的是否合理,留疑???
  if (tryTimes >= 6) {
    Ethernet.begin(mac, staticIP);
  }
  Serial.println("[INFO] Connection Successsful");
  Serial.println("");
  printConnectionInformation();
  Serial.println("-------------------------");
  Serial.println("");
}
void printConnectionInformation()
{
  Serial.print("[INFO] IP Address: ");
  Serial.println(Ethernet.localIP());
  Serial.print("[INFO] Subnet Mask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("[INFO] Gateway: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("[INFO] DNS: ");
  Serial.println(Ethernet.dnsServerIP());
}

//2.MQTT数据发布
char server[] = {"iot.eclipse.org"};
//char server[] = {"test.mosquitto.org"};这个服务器连接不上
int port = 1883;
char topic[] = {"codifythings/lightcontrol_lxm"};


void callback(char* topic, byte* payload, unsigned int length)
{
  //打印回调的内容方法一
  /*Serial.print("Message arrived [");
    Serial.print(topic);   // 打印主题信息
    Serial.print("] ");
    for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]); // 打印主题内容
    }
    Serial.println();*/
  //打印回调的内容方法二,为提高遥控的效率,调试好后就将所有的串口输出都注释掉了,能节省1秒的反应时间,下同
  //String payloadContent =String((char*)payload);
  //Serial.println("[INFO]Payload:"+payloadContent);
  turnLightOnOff();
}

void turnLightOnOff()
{
  if (digitalRead(ledPin) == LOW)
  {
    //Serial.println("[INFO]Turning lights on");
    //Serial.println(digitalRead(ledPin));
    digitalWrite(ledPin, HIGH);
  }
  else
  {
    //Serial.println("[INFO]Turning lights off");
    //Serial.println(digitalRead(ledPin));
    digitalWrite(ledPin, LOW);
  }
}

PubSubClient pubSubClient(server, port, callback, client);

//在CSDN其他示例中有这样的reconnect重连函数,意思是arduino在setup后会断开与MQTT服务器的连接,从而导致订阅的主题接收不到。
//故需要保持其与服务器的不间断连接。我的试验表明,在loop中加了这个函数后,会一直在重连,偶尔会接收到订阅的主题
void reconnect() {
  while (!pubSubClient.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (pubSubClient.connect("arduinoClient")) {
      Serial.println(".");
      //Serial.println("connected");
      // 连接成功时订阅主题
      pubSubClient.subscribe(topic);      
    } else {
      Serial.print("failed, rc=");
      Serial.print(pubSubClient.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void setup() {
  Serial.begin(9600);
  connectToInternet();
  pinMode(ledPin, OUTPUT);
  Serial.println("[INFO]Connecting to MQTT Broker");
  if (pubSubClient.connect("arduinoClient2"))
  {
    Serial.println("[INFO]Connection to MQTT Broker Successful");
    //以下两行按教材放在这里的话,试验时接收不到订阅的主题,必须放到最后面才行,反应时间内网约2秒,手机4g外网3秒
    /*pubSubClient.subscribe(topic);
    Serial.println("[INFO]Subscribed to MQTT Broker Successful");*/    
  }
  else
  {
    Serial.println("[INFO]Connection to MQTT Broker Failed");
  }
  digitalWrite(ledPin, HIGH);
  //必须放到这里,或者在loop函数中再订阅
  pubSubClient.subscribe(topic);
  Serial.println("[INFO]Subscribed to MQTT Broker Successful");
}

void loop() {

  pubSubClient.loop();
  //在loop中重新订阅主题也是能保证小灯能反应android 的点击事件的,但会增加约1秒的循环负担
  //pubSubClient.subscribe(topic);

  //以下是CSDN中找到的其他参考程序,不断重连。以及5秒重新发布新消息的功能,供参考
  //由于我的Android程序也订阅了这个主题,故这里发布的消息其也能收的到,在logi中。  
    /*if (!pubSubClient.connected()) {
    reconnect();
    }  */

  /*long now = millis();
    if (now - lastMsg > 5000) {
    lastMsg = now;
    pubSubClient.publish(topic,"Test Message");
    }*/
}

三、实物效果

物联网方案之“Android+MQTT+Arduino+以太网盾板”(三.远程遥控)_第1张图片物联网方案之“Android+MQTT+Arduino+以太网盾板”(三.远程遥控)_第2张图片物联网方案之“Android+MQTT+Arduino+以太网盾板”(三.远程遥控)_第3张图片

四、心得

按教材的代码码好后,arduino收不到订阅的主题,callback没反应,两天调试,试了各种方法,总算是通过把订阅函数重新放到不同的地方实现了遥控点灯的效果,但是为什么还不清楚。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(物联网方案之“Android+MQTT+Arduino+以太网盾板”(三.远程遥控))