记录《用于物联网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");
}*/
}
三、实物效果
四、心得
按教材的代码码好后,arduino收不到订阅的主题,callback没反应,两天调试,试了各种方法,总算是通过把订阅函数重新放到不同的地方实现了遥控点灯的效果,但是为什么还不清楚。