摘要

这是一个城市智能路灯控制系统的设计,实现功能主要为光线充足时不亮灯,当光线不足时开始检测,如果有人或者有声音便亮起路灯,这是自动模式,设备可通过手机APP切换为手动模式,在手动模式下用户可通过手机APP来实现对路灯的操控。
本项目本身逻辑比较简单,硬件部分也是很常见的设计,与之前做过的项目大同小异,本质上还是传感器采集信息然后单片机负责进行判断和开关电路。因此这个项目的重点其实在于Mqtt的使用,之前的几个物联网设计中,手机端的实现主要是依靠第三方的点灯科技的接口,这种方式实现联网终究可玩性还是太低了,况且对于一个硬件电子爱好者来说,实现物联网仅局限于云智能也太拉跨了,因此学习了AndroidStudio开发App从而实现Mqtt的通信协来控制设备实现物联网,我个人认为这一个流程的实现才是真正的物联网开发。


一、原件清单

  • Esp8266NodeMCU*1
  • 人体热释红外传感器*1
  • 音量传感器*1
  • 光敏电阻传感器模块*1
  • SSD1306显示屏*1
  • 杜邦线*1
  • 5V一路继电器
  • 18650锂电池*2
  • 三极管*1
  • 杜邦线 若干
    硬件连接.png

二、硬件连接

nodeMCU ssd1306 光敏传感器 人体热释传感器 声音传感器 5V继电器 LED灯泡 NPN三极管 18650电源
D6 SDA
D7 SCL
D0 DO
D2 OUT
D8 OUT
D2 基极
IN 集电极
VCC VCC VCC VCC VCC DC+ 发射极
GND GND GND GND GND DC-
COM1 +
- -
NO +

三、原理图&流程图

Mqtt城市智能路灯.png
总体设计原理图

Mqtt城市智能路灯软件流程图.png
软件流程图


四、程序代码

硬件程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include "SSD1306Wire.h"
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
WiFiClient espClient;
PubSubClient client (espClient);
const char* wifissid="test";
const char* password="12345678";
const char* mqtt_server="xxx.xxx.xxx.xxx";
const char* mqtt_id="isNineSun_ESP";
const char* Mqtt_sub_topic="pub_ywt";
const char* Mqtt_pub_topic="sub_ywt";
long lastMsg=0;


int MIC = 4; // D0 麦克风引脚连接开发板数字接口 2
int LED = 0; // LED 正极连接开发板数字接口3
int HC = 15;
int D_in=16;
int VALOR; // DO变量值
int LEDDO; // LED变量
int HoneWai;
int Lighting;
bool auto_mode=1,led_mode=1;

static const uint8_t wu_you[][32]={0x00,0x80,0x40,0x40,0x42,0x20,0x42,0x10,0x42,0x0C,0xC2,0x03,0x7E,0x00,0x42,0x00,0xC2,0x3F,0x42,0x40,0x42,0x40,0x42,0x40,0x40,0x40,0x40,0x70,0x00,0x00,0x00,0x00,/*无0*/
0x04,0x04,0x04,0x02,0x04,0x01,0x84,0x00,0xE4,0xFF,0x3C,0x09,0x27,0x09,0x24,0x09,0x24,0x09,0x24,0x49,0x24,0x89,0xE4,0x7F,0x04,0x00,0x04,0x00,0x04,0x00,0x00,0x00,/*有0*/};
static const uint8_t ren[][32]={0x00,0x80,0x00,0x40,0x00,0x20,0x00,0x10,0x00,0x0C,0x00,0x03,0xC0,0x00,0x3F,0x00,0xC0,0x00,0x00,0x03,0x00,0x0C,0x00,0x10,0x00,0x20,0x00,0x40,0x00,0x80,0x00,0x00,/*人0*/};
static const uint8_t sheng[][32]={0x04,0x80,0x14,0x60,0xD4,0x1F,0x54,0x02,0x54,0x02,0x54,0x02,0x54,0x02,0xDF,0x03,0x54,0x02,0x54,0x02,0x54,0x02,0x54,0x02,0xD4,0x03,0x14,0x00,0x04,0x00,0x00,0x00,/*声0*/};
static const uint8_t guang[][32]={0x40,0x80,0x40,0x80,0x42,0x40,0x44,0x20,0x58,0x18,0xC0,0x07,0x40,0x00,0x7F,0x00,0x40,0x00,0xC0,0x3F,0x50,0x40,0x48,0x40,0x46,0x40,0x40,0x40,0x40,0x78,0x00,0x00,/*光0*/};


const uint8_t image1[] PROGMEM ={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0x01,0x00,0x00,0x00,0x00,0x00,0xF0,0xFC,0x01,0x00,0x00,0xF8,0xFF,0x3F,0xFF,0xFF,0x7F,0x00,0x00,0xF8,0xFF,0x7F,0xFF,0xF1,0x7F,0x00,0x00,0xF8,0xFF,0x7F,0xFF,0xE4,0x7F,0x00,0x00,0xF8,0xFF,0x7F,0x7F,0x9E,0x7F,0x00,
0x00,0x78,0xCE,0x79,0x60,0x3E,0x00,0x00,0x00,0x7E,0xCE,0x79,0x0C,0xBC,0x39,0x00,0x00,0x7F,0xCE,0x79,0x9C,0x99,0x7B,0x00,0x00,0x7F,0xCE,0x79,0xDC,0xA3,0x7B,0x00,0x00,0x7F,0xCE,0x79,0xFF,0xBF,0x7F,0x00,0x00,0x7F,0xCE,0x79,0xFF,0xBF,0x3F,0x00,0x00,0x7E,0xCE,0x79,0xFF,0xF6,0x3F,0x00,0x00,0x78,0xCE,0x79,
0xDF,0xF6,0x1F,0x00,0x00,0x78,0xCE,0x79,0xDE,0xF6,0x0F,0x00,0x00,0xF8,0xFF,0x7F,0xDE,0xF6,0x07,0x00,0x00,0xF8,0xFF,0x7F,0xFF,0xF6,0x3F,0x00,0x00,0xF8,0xFF,0x7F,0xFF,0xB6,0x3F,0x00,0x00,0xF8,0xFF,0x3F,0xFF,0xBF,0x7F,0x00,0x00,0xE0,0xFF,0x1F,0xDE,0xBF,0x7B,0x00,0x00,0x00,0x00,0x00,0xDC,0xBF,0x7B,0x00,
0x00,0x00,0x00,0x00,0x1C,0x80,0x7B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x3E,0x70,0x00,0x00,0x00,0x00,0x9E,0xC3,0x3F,0xF0,0x1C,0x38,0x00,0x00,0x9E,0xFB,0x3F,0xF0,0x1E,0x3C,0x00,0x00,0x9E,0xFF,0x3F,0xF0,0x1E,0x3C,0x00,0x00,0x9E,0xFF,0x3F,
0xF0,0x3E,0x3E,0x00,0x00,0x9E,0xDF,0x3D,0xF0,0xFE,0x3F,0x00,0x00,0x9E,0x0F,0x3C,0xF0,0xFE,0x3F,0x00,0x00,0x9E,0x67,0x3C,0xF0,0xFE,0x3F,0x00,0x00,0x9E,0xF3,0x3F,0xF0,0xFE,0x3F,0x00,0x00,0x86,0xF3,0x3F,0xF0,0x3E,0x1E,0x00,0x00,0x32,0xC3,0x3F,0xF0,0x1E,0x1E,0x00,0x00,0x70,0x00,0x1F,0xFC,0x1C,0x1E,0x00,
0x00,0x70,0x70,0x40,0xFF,0x01,0x1E,0x00,0x00,0xFE,0xFF,0x77,0xFF,0x77,0x0C,0x00,0x00,0xFF,0xFF,0x3F,0xFB,0xFF,0x01,0x00,0x00,0xFF,0xFF,0x03,0xE0,0xFF,0x0F,0x00,0x00,0xFF,0x1F,0x38,0xEE,0xFF,0x1F,0x00,0x00,0x78,0x00,0x78,0xCE,0xF0,0x3F,0x00,0x00,0x70,0x00,0x7C,0x9E,0x80,0x7C,0x00,0x00,0xF0,0xFF,0x7F,
0x9E,0x00,0x7F,0x00,0x00,0xF0,0xFF,0x7F,0x9C,0x00,0x7F,0x00,0x00,0xF0,0xFF,0x3F,0xC0,0x00,0x3F,0x00,0x00,0x40,0x00,0x00,0x60,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*C:\Users\NineSun\Desktop\未标题-1.bmp0*/
};
const uint8_t image2[] PROGMEM ={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x38,0x70,0x00,0x00,0x00,0xC0,0x79,0x00,0x78,0xF8,0x00,0x00,0x00,0xCE,0x7B,0x00,0x78,0xFE,0x00,0x00,0x00,0xCF,0x7B,0x80,0xFF,0xFF,0x3F,0x00,0x00,0xCF,0x7B,0x80,0xFF,0xF8,0x3F,0x00,0x00,0xCF,0x7B,0xBC,0x7F,0xF2,0x3F,0x00,0x00,0xCF,0x7B,0xBE,0x3F,0xEF,0x3F,0x00,
0x00,0xCF,0x7B,0x3E,0x30,0x1F,0x00,0x00,0x00,0xCF,0x7B,0x3E,0x0E,0xDE,0x1D,0x00,0x00,0xFF,0xFF,0x3F,0xCE,0xCC,0x3D,0x00,0x00,0xFF,0xFF,0x3F,0xEF,0xD1,0x3D,0x00,0x00,0xFF,0xFF,0xBF,0xFF,0xDF,0x3F,0x00,0x00,0xFF,0xFF,0xBF,0xFF,0xDF,0x1F,0x00,0x00,0xFF,0xFF,0xBF,0x7F,0xFB,0x1F,0x00,0x00,0xCF,0x7B,0x80,
0x7F,0xFB,0x0F,0x00,0x00,0xCF,0x7B,0x00,0x6F,0xFB,0x07,0x00,0x00,0xCF,0x7B,0x00,0x6F,0xFB,0x03,0x00,0x80,0xCF,0x7B,0x80,0x7F,0xFB,0x1F,0x00,0x80,0xCF,0x7B,0x80,0xFF,0xDF,0x1F,0x00,0x00,0xC7,0x7B,0x80,0xFF,0xDF,0x3F,0x00,0x00,0xC7,0x7B,0x00,0xEF,0xDF,0x3D,0x00,0x00,0xC0,0x79,0x00,0xEE,0xDF,0x3D,0x00,
0x00,0x00,0x70,0x00,0x0E,0xC0,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01,0x1F,0x78,0x00,0x00,0x00,0x00,0xCF,0xE1,0x1F,0x7C,0x0E,0x1E,0x00,0x00,0xCF,0xFF,0x1F,0x7C,0x1F,0x1E,0x00,0x00,0xCF,0xFF,0x1F,0x7C,0x1F,0x3E,0x00,0x00,0xCF,0xFF,0x1F,
0x7C,0xFF,0x1F,0x00,0x00,0xCF,0xFF,0x1E,0x7C,0xFF,0x1F,0x00,0x00,0xCF,0x07,0x1E,0x7C,0xFF,0x1F,0x00,0x00,0xCF,0x3B,0x1E,0x7C,0xFF,0x1F,0x00,0x00,0xCF,0xF9,0x1F,0x7C,0xFF,0x1F,0x00,0x00,0xC3,0xF9,0x1F,0x7C,0x1F,0x1F,0x00,0x00,0xB9,0xE1,0x1F,0x7C,0x1F,0x0F,0x00,0x00,0xBC,0x80,0x0F,0x7F,0x0E,0x0F,0x00,
0x00,0x3C,0x38,0xA0,0xFF,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xBF,0xFF,0x3F,0x06,0x00,0x80,0xFF,0xFF,0xBF,0xFF,0xFF,0x00,0x00,0x80,0xFF,0xFF,0x01,0xF0,0xFF,0x07,0x00,0x80,0xFF,0x1F,0x1C,0xF7,0xFF,0x0F,0x00,0x00,0x3D,0x00,0x3E,0x6F,0xFC,0x1F,0x00,0x00,0x3C,0xFC,0x3F,0x4F,0x40,0x3F,0x00,0x00,0xFC,0xFF,0x3F,
0x5F,0x80,0x3F,0x00,0x00,0xFC,0xFF,0x3F,0x4E,0x80,0x3F,0x00,0x00,0xF8,0xFF,0x1F,0x60,0x80,0x1F,0x00,0x00,0xF0,0xFF,0x07,0x30,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*C:\Users\NineSun\Desktop\未标题-1.bmp0*/
};


const int I2C_ADDR = 0x3c; // oled屏幕的I2c地址
#define SDA_PIN 12 // SDA引脚,默认gpio12
#define SCL_PIN 13 // SCL引脚,默认gpio13

SSD1306Wire oled(I2C_ADDR, SDA_PIN, SCL_PIN);


void setup(){
pinMode(MIC, INPUT); // 麦克风输入口
pinMode(LED, OUTPUT); // LED输出口
pinMode(HC, INPUT); // 红外输入
pinMode(D_in, INPUT); // 光敏输入
pinMode(2,OUTPUT);

Serial.begin(115200);

setup_wifi();
client.setServer(mqtt_server,1883);
client.setCallback(callback);

oled.init();
oled.flipScreenVertically(); // 设置屏幕翻转
oled.setContrast(255); // 设置屏幕亮度
drawRect(); // 测试屏幕显示
oled.clear();
oled.display(); // 清除屏幕
}


void loop(){
if(!client.connected()){
reconnect();
}
client.loop();


if(auto_mode){
oled.clear();
Auto_Mode();
}
else{
oled.clear();
oled.drawFastImage(30, 0, 64, 64, image2);
oled.display();
manual_mode();
delay(200);
}

}

void Auto_Mode(){
oled.setFont(ArialMT_Plain_16); // 设置字体
oled.println("AUTO");
oled.drawFastImage(4 + 20*1, 21*0, 16, 16, sheng[0]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*1, 21*1, 16, 16, ren[0]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*1, 21*2, 16, 16, guang[0]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*0, 21*0, 16, 16, wu_you[VALOR]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*0, 21*1, 16, 16, wu_you[HoneWai]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*0, 21*2, 16, 16, wu_you[!Lighting]); // 将要显示的文字写入缓存
oled.drawFastImage(45, 0, 64, 64, image1);
oled.display(); // 将缓存里的文字在屏幕上显示
delay(500);
oled.display();


VALOR = digitalRead(MIC); // 数字读取DO
HoneWai = digitalRead(HC);
Lighting = digitalRead(D_in);

if(Lighting==HIGH){
oled.clear();
if (VALOR == HIGH||HoneWai == HIGH){
oled.drawFastImage(4 + 20*1, 21*0, 16, 16, sheng[0]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*1, 21*1, 16, 16, ren[0]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*1, 21*2, 16, 16, guang[0]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*0, 21*0, 16, 16, wu_you[VALOR]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*0, 21*1, 16, 16, wu_you[HoneWai]); // 将要显示的文字写入缓存
oled.drawFastImage(4 + 20*0, 21*2, 16, 16, wu_you[!Lighting]); // 将要显示的文字写入缓存

oled.drawFastImage(45, 0, 64, 64, image1);

oled.display(); // 将缓存里的文字在屏幕上显示
digitalWrite(LED, HIGH);
delay(5000);
}
else{
digitalWrite(LED, LOW);
}
}
else{
digitalWrite(LED, LOW);
}
}

void manual_mode(){
digitalWrite(LED, led_mode?HIGH:LOW);
}

//开机动画
void drawRect(void) {
for (int16_t i=0; i<oled.getHeight()/2; i+=2) {
oled.drawRect(i, i, oled.getWidth()-2*i, oled.getHeight()-2*i);
oled.display();
delay(50);
}
}


/*****************************wifi程序*****************************************************************/
void setup_wifi(){
Serial.println();
Serial.print("Connecting to");
Serial.println(wifissid);
WiFi.begin(wifissid,password);
while(WiFi.status()!=WL_CONNECTED){
delay(500);
Serial.print(".");
}
Serial.println(" ");
Serial.println("WiFi connected!");
Serial.println("IP Address:");
Serial.println(WiFi.localIP());
}
void callback(char* topic,byte* payload,unsigned int length){
String msg="";
String Led_set="";
String auto_set="";
Serial.println("Message arrived{");
Serial.println(topic);
Serial.println("}");
for (int i=0;i<length;i++){
msg+=(char)payload[i];
}
Serial.println(msg);
if(msg.indexOf("led")){
Led_set = msg.substring(msg.indexOf("led\":")+5,msg.indexOf("}"));
led_mode = Led_set.toInt();
}
if(msg.indexOf("auto")){
auto_set = msg.substring(msg.indexOf("auto\":")+6,msg.indexOf("}"));
auto_mode = auto_set.toInt();
}
}
void reconnect(){
while(!client.connected()){
Serial.println("Attempting MQTT connection...");
if(client.connect(mqtt_id)){
Serial.println("Connected!");
client.publish(Mqtt_pub_topic,"设备已上线");

client.subscribe(Mqtt_sub_topic,1);
}else{
Serial.println("Failed,rc=");
Serial.println(client.state());
Serial.println("try again in 5 seconds");
delay(50000);
}
}
}
  • 代码中的中文汉字用到了一个叫 PCtoLCD2002 的软件,这个软件可以很方便的将汉字转化成对应的字形码来输出,这里用的是16*16的字宽字长;**
  • 关于Mqtt通信协议的获取的相关函数写在了wifi相关里面;
  • 关于const char* mqtt_server="xxx.xxx.xxx.xxx";这句话中的xxx.xxx.xxx.xxx应该填入你的物联网服务器公网地址,关于如何搭建物联网服务器,我有计划重新开一篇帖子来说,非常简单,如果你是学生的话更能用几十块钱的价格买到非常便宜的云服务器,平时学习使用是足够了的。
  • client.setServer(mqtt_server,1883);一句中的1883是服务器端口号,通常设置为1883;

接下来是软件方面的代码,软件开发使用AndroidStudio进行,首先需要添加MQTT的依赖库,然后app还需要获取一些必要的权限,这些都是必须要配置的:

首先,在bulid.gradle 里面最后的依赖那里复制这段代码(在那一排“implementation”后面加)

1
2
3
//导入Mqtt环境依赖
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.1'
implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'

然后软件点击右上角的sync now,进行同步,建议科学上网,就能正常加载包了;

然后,需要配置app需要的一些权限:找到AndroidManifest.xml文件,在这个文件中加入:

1
2
3
4
5
6
7
8
<!-- Permissions the Application Requires -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!--<uses-permission android:name="android.permission.READ_PHONE_STATE" />-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

为防止有的同学完全没接触过as开发,特别说明,这段直接添加到原代码的 <application之前就可以了,就在前面几行,很容易找到。

最后,还是在这个文件中,在代码末尾</activity>这句话的后面,紧跟着插入一段

1
2
3
<!-- Mqtt Service -->
<service android:name="org.eclipse.paho.android.service.MqttService">
</service>

来引入Mqt的服务。
完成之后,前期的配置就完成了可以愉快的写代码了(≧∇≦)ノ。
程序逻辑代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
package com.example.mqtt_study;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Switch;

import org.eclipse.paho.android.service.MqttAndroidClient;
import org.eclipse.paho.client.mqttv3.IMqttActionListener;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;

public class MainActivity extends AppCompatActivity {

MqttAndroidClient mqttAndroidClient;//建立Client客户端实例

final String serverUri = "tcp://101.43.207.139:1883";//服务器地址

String clientId = "isNineSun_android";//客户端标识,可以随便起,保证不重复即可
final String subscriptionTopic = "sub_ywt";//订阅的主题
final String publishTopic = "pub_ywt";

//UI变量
private ImageButton button1,button2;
private boolean light,auto;
private TextView text;
private ImageView image;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

UI_init();
Mqtt_init();

button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
light=!light;
String publishMsg="{\"set_led\":"+(light?"1":"0")+"}";
publishMessage(publishMsg);
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
auto=!auto;
String publishMsg="{\"set_auto\":"+(auto?"1":"0")+"}";
publishMessage(publishMsg);
}
});


}

void UI_init(){
button1 = findViewById(R.id.but_1);
button2 = findViewById(R.id.but_2);
}

public void Mqtt_init(){
//新建Client实例
//clientId = clientId + System.currentTimeMillis();//在clientId即客户端标识后面加上时间戳来保证唯一性
mqttAndroidClient = new MqttAndroidClient(getApplicationContext(), serverUri, clientId);
//新建回调函数
mqttAndroidClient.setCallback(new MqttCallbackExtended() {
@Override
//连接完成
public void connectComplete(boolean reconnect, String serverURI) {
}
//失去连接
@Override
public void connectionLost(Throwable cause) {
}
//信息送达
@Override
public void messageArrived(String topic, MqttMessage message) throws Exception {
String msg = new String(message.getPayload());
//String T_val=msg.substring(msg.indexOf("{temperature:"),msg.indexOf("}"));
makeToast(msg);
text.setText(msg);
image.setImageAlpha(1);
}
//推送完成
@Override
public void deliveryComplete(IMqttDeliveryToken token) {

}
});

//连接设置、是否自动重连、是否清理Session
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttConnectOptions.setCleanSession(false);


//正式开始建立连接
try {
//addToHistory("Connecting to " + serverUri);
mqttAndroidClient.connect(mqttConnectOptions, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
makeToast("连接成功");
subscribeToTopic();
}

@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
makeToast("连接失败");
}
});


} catch (MqttException ex){
ex.printStackTrace();
}
}

public void subscribeToTopic(){
try {
//开始订阅
mqttAndroidClient.subscribe(subscriptionTopic, 0, null, new IMqttActionListener() {
@Override
public void onSuccess(IMqttToken asyncActionToken) {
makeToast("成功订阅"+subscriptionTopic);
}

@Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
makeToast("未能订阅"+subscriptionTopic);
}
});

} catch (MqttException ex){
ex.printStackTrace();
}
}

public void publishMessage(String publishMessage){

try {
MqttMessage message = new MqttMessage();
message.setPayload(publishMessage.getBytes());
mqttAndroidClient.publish(publishTopic, message);
if(!mqttAndroidClient.isConnected()){
}
} catch (MqttException e) {
System.err.println("Error Publishing: " + e.getMessage());
e.printStackTrace();
}
}

private void makeToast(String str){
Toast.makeText(MainActivity.this,str,Toast.LENGTH_SHORT).show();
}

}

软件的布局文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible">
<ImageView
android:layout_width="wrap_content"
android:layout_height="20sp"
android:src="@drawable/online"
android:alpha="0">

</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="">

</TextView>
</LinearLayout>
<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:layout_marginTop="200dp"
android:id="@+id/but_1"
android:backgroundTint="@color/white"
android:src="@drawable/power">


</ImageButton>

<ImageButton
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:id="@+id/but_2"
android:backgroundTint="@color/white"
android:src="@drawable/swt">

</ImageButton>
</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

程序代码我用了自己写的模板来节省开发时间,如果有人对我的模板感兴趣可以去我的github页面去拿,欢迎使用Mqtt模板代码


五、实物图

实物展示:
硬件.png
软件.png


总结

这次设计的硬件方面与之前的物联网设计大同小异,但是在Mqtt的配置方面学到了很多,也加深了对互联网概念的理解,以后有机会希望还能更深入更底层的去做物联网相关的开发,除此之外,AndroidStudio的开发我暂时也只是初窥门径而已,希望能更深入的掌握这个强大的工具,之后有机会我可能会做一些更偏软或者纯软的项目来加深对as开发的掌握。