車間資訊採集與控制
# Smart之車間資訊採集與控制
# 1. 說明
本範例採用MQTT通訊協議,採集源於設備的數據資訊,通過MQTT向指定主題發佈資訊,接收端接收資訊並顯示。
在生產車間中,我們會關心車間設備的運行參數以及車間的環境情況,這樣就可以及時瞭解車間設備的運行情況以及車間的運行情況,然後及時做出相關預警資訊。本硬體設備範例使用I2C,ModbusRTU,模擬針腳線等採集連線的硬體的採集數據資訊,然後通過MQTT協議來傳輸資訊,在MQTT客戶端上連線到中介軟體,訂閱Motor/#
主題就可獲取到採集的所有數據資訊。
通過本範例學習,可以掌握MQTT的基本通訊原理,並結合Arduino開發板實現數據採集顯示和控制的功能。
# 2. 零件連線圖
本示例由於線路連線較為複雜,連線圖可能無法清晰展示實際的連線情況,此處將各個硬體的連線使用表格進行羅列。
將使用的感測器及相關硬體分別連線至Arduino Mega2560,首先將W5100網路擴充套件板接入Arduino Mega2560並通過網線連線至區域網中,然後按照下方的表格列出的介面進行對照連線。其中繼電器的COM
、NO
端需串聯接入至電動機的交流電路中。
噪聲感測器 | Arduino Mega2560 | |
---|---|---|
5V | -> | 5V |
GND | -> | GND |
TXD | -> | RX1 |
RXD | -> | TX1 |
功率因數盒 | TTL-RS485轉換器 | |
---|---|---|
A | -> | A |
B | -> | B |
TTL-RS485轉換器 | Arduino Mega2560 | |
---|---|---|
VCC | -> | 5V |
GND | -> | GND |
TXD | -> | RX2 |
RXD | -> | TX2 |
PT100 | MAX31865 | |
---|---|---|
red1 or red2 | -> | RTD+ |
red2 or red1 | -> | F+ |
white | -> | F- or RTD- |
MAX31865 | Arduino Mega2560 | |
---|---|---|
VCC | -> | 5V |
GND | -> | GND |
CLK | -> | 13 |
SDO | -> | 12 |
SDI | -> | 11 |
CS | -> | 10 |
BH1750 | Arduino Mega2560 | |
---|---|---|
VCC | -> | 5V |
GND | -> | GND |
SCL | -> | SCL |
SDA | -> | SDA |
霍爾感測器 | Arduino Mega2560 | |
---|---|---|
VCC | -> | 5V |
GND | -> | GND |
D0 | 2 |
壓電陶瓷振動感測器 | Arduino Mega2560 | |
---|---|---|
+ | -> | 5V |
- | -> | GND |
S | -> | A0 |
夏普GP2Y1010AU0F粉塵感測器 | Arduino Mega2560 | |
---|---|---|
1 | -> | 5V(兩者間串聯150Ω電阻,pin 1和電阻之間並聯220μF電容 電容長腳端與pin1相連,短腳端接至Arduino GND腳) |
2 | -> | GND |
3 | -> | 7 |
4 | -> | GND |
5 | -> | A1 |
6 | -> | 5V |
MQ135 | Arduino Mega2560 | |
---|---|---|
VCC | -> | 5V |
AOUT | -> | A2 |
GND | -> | GND |
DHT11 | Arduino Mega2560 | |
---|---|---|
+ | -> | 5V |
- | -> | GND |
OUT | -> | 8 |
繼電器 | Arduino Mega2560 | |
---|---|---|
DC+ | -> | 5V |
DC- | -> | GND |
IN | -> | 9 |
# 3. 使用零件
序號 | 硬體名稱 | 數量 |
---|---|---|
1 | Arduino Mega 2560開發板 | 1 |
2 | W5100網路擴充套件板 | 1 |
3 | peacefair PZEM-016功率因數檢測盒(ModbusRTU通訊) | 1 |
4 | 噪聲感測器(TTL介面) | 1 |
5 | PT100貼片式熱電阻感測器(三線) | 1 |
6 | MAX31865 RTD至數字輸出轉換器 | 1 |
7 | BH1750光線感測器 | 1 |
8 | 霍爾感測器測速模組 | 1 |
9 | 銣鐵硼磁鐵塊 | 1 |
10 | 模擬壓電陶瓷震動感測器(帶壓電陶瓷) | 1 |
11 | DHT11溫濕度感測器 | 1 |
12 | MQ-135空氣質量感測器 | 1 |
13 | 夏普GP2Y1014AU粉塵感測器(附帶150Ω電阻以及220μF電解電容器) | 1 |
14 | 插排(無線) | 1 |
15 | TTL轉RS485轉換器(具有TX、RX針腳的型別) | 1 |
16 | 插頭(無線) | 1 |
17 | RV1.5線纜2米,三種不同顏色 | 1 |
18 | 單路繼電器 | 1 |
19 | 220V電動機 | 1 |
20 | 杜邦線 | 若干 |
# 4. Arduino流程圖
# 5. Arduino程式
使用Arduino IDE 編譯並上傳以下Arduino程式。使用該程式需匯入ModbusMaster
、Adafruit_MAX31865
、DHT
、PubSubClient
等相關庫。
#include <ModbusMaster.h>
#include <SoftwareSerial.h>
#include <Adafruit_MAX31865.h>
#include <Wire.h> //BH1750 IIC Mode
#include <math.h>
#include <DHT.h>
#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
// MQTT 宣告 ========================================================
EthernetClient ethClient;
PubSubClient client(ethClient);
#define dhtPin 8 //讀取DHT11 Data
#define POWERPIN 9 //設定電源訊號輸出針腳
#define dhtType DHT11 //選用DHT11
#define STRING_LEN 128
DHT dht(dhtPin, dhtType); // Initialize DHT sensor
const String MQTTClientName = "Motor"; //定義MQTT客戶端名稱
char Topic[100]; //設定主題變數
// 設定網路IP地址 (網路擴充卡 MAC 可自行修改 +1 避免衝突)
byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0x94, 0xA3 };
IPAddress ip(192, 168, 0, 234);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
// Use software SPI: CS, DI, DO, CLK
Adafruit_MAX31865 thermo = Adafruit_MAX31865(10, 11, 12, 13);
// The value of the Rref resistor. Use 430.0 for PT100 and 4300.0 for PT1000
#define RREF 430.0
// The 'nominal' 0-degrees-C resistance of the sensor
// 100.0 for PT100, 1000.0 for PT1000
#define RNOMINAL 100.0
//噪聲感測器TTL-> Serial1
//const int SSRxPin = 6; // Recieve pin for software serial (Rx on RS485 adapter)
//const int SSTxPin = 7; // Send pin for software serial (Tx on RS485 adapter)
//SoftwareSerial modbusNoiseSerial(SSRxPin, SSTxPin);
//電能感測器TTL->Serial2
//const int SSRxEPin = 4; // Recieve pin for software serial (Rx on RS485 adapter)
//const int SSTxEPin = 5; // Send pin for software serial (Tx on RS485 adapter)
//SoftwareSerial modbusElectricSerial(SSRxEPin, SSTxEPin);
// instantiate ModbusMaster object
ModbusMaster nodeNoise;
ModbusMaster nodeElectric;
int BH1750address = 0x23; //setting i2c address
byte buff[2];
int Rotationpin = 2; //定義霍爾計數器的引腳為D2
unsigned long duration=0; //定義duration變數為無符號長整數型變數
int i = 0;
//粉塵感測器變數
#define measurePin A1//輸出引腳連線模擬口A1
#define ledPin 7 //LED引腳連線數字口9
unsigned int samplingTime = 280;//根據前面分析採樣時間為280,所以這裡為280
unsigned int deltaTime = 40;//測量完后脈衝需要繼續保持,保持時間為320-280=40
unsigned int sleepTime = 9680;//LED脈衝週期為10毫秒,故此處為10000-320=9680
float voMeasured = 0;
float calcVoltage = 0;
float dustDensity = 0;
//MQ135 變數初始設定 針腳為A2
const int gasSensor =2;
//噪聲感測器初始化
void noiseSetup(){
// initialize Modbus communication baud rate
Serial1.begin(9600);
// communicate with Modbus slave ID 1 over Soft Serial
nodeNoise.begin(1, Serial1);
}
//噪聲感測器讀取數據
void noiseRead(){
uint8_t j, result;
uint16_t data;
// set word 0 of TX buffer to least-significant word of counter (bits 15..0)
//node.setTransmitBuffer(0, lowWord(i));
// set word 1 of TX buffer to most-significant word of counter (bits 31..16)
//node.setTransmitBuffer(1, highWord(i));
// slave: write TX buffer to (2) 16-bit registers starting at register 0
//result = node.writeMultipleRegisters(0, 2);
// slave: read (6) 16-bit registers starting at register 2 to RX buffer
result = nodeNoise.readHoldingRegisters(0, 1);
// do something with data if read is successful
if (result == nodeNoise.ku8MBSuccess)
{
Serial.println("Noise Sensor Via ModbusRTU");
data = nodeNoise.getResponseBuffer(0);
Serial.print(data * 0.1);
Serial.println("dB");
//發佈主題
char noise[20];
dtostrf(data * 0.1,3,1,noise);
String NoiseTopic = MQTTClientName + "/noise";
NoiseTopic.toCharArray(Topic,100);
client.publish(Topic, noise);
}
}
//電能感測器初始化
void ElectricSetup(){
// initialize Modbus communication baud rate
Serial2.begin(9600);
// communicate with Modbus slave ID 1 over Soft Serial
nodeElectric.begin(1, Serial2);
}
void ElectricRead(){
static uint32_t i;
uint8_t j, result;
uint16_t data[10];
//i++;
// set word 0 of TX buffer to least-significant word of counter (bits 15..0)
//node.setTransmitBuffer(0, lowWord(i));
// set word 1 of TX buffer to most-significant word of counter (bits 31..16)
//node.setTransmitBuffer(1, highWord(i));
// slave: write TX buffer to (2) 16-bit registers starting at register 0
//result = node.writeMultipleRegisters(0, 2);
// slave: read (6) 16-bit registers starting at register 2 to RX buffer
//result = node.readHoldingRegisters(0, 5);
result = nodeElectric.readInputRegisters(0,9);
// do something with data if read is successful
if (result == nodeElectric.ku8MBSuccess)
{
for (j = 0; j < 9; j++)
{
data[j] = nodeElectric.getResponseBuffer(j);
//Serial.println(data[j] * 0.1);
}
//電壓值
Serial.print("U:");
Serial.print(data[0] * 0.1);
Serial.println("V");
char voltage[20];
dtostrf(data[0] * 0.1,3,1,voltage);
String voltageTopic = MQTTClientName + "/voltage";
voltageTopic.toCharArray(Topic,100);
client.publish(Topic, voltage);
//電流測量
Serial.print("I:");
Serial.print((data[2] * 65536 + data[1]) * 0.001);
Serial.println("A");
char current[20];
dtostrf((data[2] * 65536 + data[1]) * 0.001,3,3,current);
String currentTopic = MQTTClientName + "/current";
currentTopic.toCharArray(Topic,100);
client.publish(Topic, current);
//功率測量
Serial.print("P:");
Serial.print((data[4] * 65536 + data[3]) * 0.1);
Serial.println("W");
//電能測量
Serial.print("E:");
Serial.print((data[6] * 65536 + data[5]) * 1);
Serial.println("Wh");
}
}
//MAX31865感測器初始化
void MAX31865Setup() {
thermo.begin(MAX31865_3WIRE); // set to 2WIRE or 4WIRE as necessary
}
//MAX31865讀取溫度
void MAX31865Read() {
uint16_t rtd = thermo.readRTD();
Serial.println("Adafruit MAX31865 PT100 Sensor Test!");
Serial.print("RTD value: "); Serial.println(rtd);
float ratio = rtd;
ratio /= 32768;
Serial.print("Ratio = "); Serial.println(ratio,8);
Serial.print("Resistance = "); Serial.println(RREF*ratio,8);
Serial.print("Temperature = "); Serial.println(thermo.temperature(RNOMINAL, RREF));
char surftemp[20];
dtostrf(thermo.temperature(RNOMINAL, RREF),3,2,surftemp);
String surftempTopic = MQTTClientName + "/surftemp";
surftempTopic.toCharArray(Topic,100);
client.publish(Topic, surftemp);
// Check and print any faults
uint8_t fault = thermo.readFault();
if (fault) {
Serial.print("Fault 0x"); Serial.println(fault, HEX);
if (fault & MAX31865_FAULT_HIGHTHRESH) {
Serial.println("RTD High Threshold");
}
if (fault & MAX31865_FAULT_LOWTHRESH) {
Serial.println("RTD Low Threshold");
}
if (fault & MAX31865_FAULT_REFINLOW) {
Serial.println("REFIN- > 0.85 x Bias");
}
if (fault & MAX31865_FAULT_REFINHIGH) {
Serial.println("REFIN- < 0.85 x Bias - FORCE- open");
}
if (fault & MAX31865_FAULT_RTDINLOW) {
Serial.println("RTDIN- < 0.85 x Bias - FORCE- open");
}
if (fault & MAX31865_FAULT_OVUV) {
Serial.println("Under/Over voltage");
}
thermo.clearFault();
}
Serial.println();
}
//BH1750設定
void BH1750Setup() {
Wire.begin();
}
//BH1750 讀取
int BH1750_Read(int address) //
{
int i=0;
Wire.beginTransmission(address);
Wire.requestFrom(address, 2);
while(Wire.available()) //
{
buff[i] = Wire.read(); // receive one byte
i++;
}
Wire.endTransmission();
return i;
}
//BH1750初始化
void BH1750_Init(int address)
{
Wire.beginTransmission(address);
Wire.write(0x10);//1lx reolution 120ms
Wire.endTransmission();
}
//BH1750循環程式碼
void BH1750_Loop() {
int i;
uint16_t val=0;
BH1750_Init(BH1750address);
delay(200);
if(2==BH1750_Read(BH1750address))
{
Serial.println("---BH1750 Light Sensor---");
val=((buff[0]<<8)|buff[1])/1.2;
Serial.print(val,DEC);
Serial.println("[lx]");
//發佈亮度訂閱
char light[20];
itoa(val,light,DEC);
String lightTopic = MQTTClientName + "/light";
lightTopic.toCharArray(Topic,100);
client.publish(Topic, light);
}
}
//讀取振動數據
void viberateRead() {
int val;
Serial.println("---Viberation Sensor----");
val=analogRead(0); //將模擬壓電陶瓷震動感測器連線到模擬介面0
Serial.print("Vibration is ");
Serial.println(val,DEC);//通過通訊埠列印讀取到的模擬值
//發佈振動壓感數據
char viberate[20];
itoa(val,viberate,DEC);
String viberateTopic = MQTTClientName + "/viberate";
viberateTopic.toCharArray(Topic,100);
client.publish(Topic, viberate);
}
//初始化設定計數引腳
void rotationSetup() {
pinMode(Rotationpin, INPUT); //設定引腳為輸入模式
}
//讀取轉速
void rotationRead() {
duration=0;
for(i=0;i<5;i++){
duration += pulseIn(Rotationpin, HIGH);
duration += pulseIn(Rotationpin, LOW);
}
Serial.println("Rotation Calculator");
Serial.println(duration);
duration = duration/5;
Serial.println(duration);
duration=1000000/duration;
Serial.print(" f is ");
Serial.print(duration);
Serial.println(" HZ");
duration = duration*60;
Serial.print(" v is ");
Serial.print(duration);
Serial.println(" r/min");
char rotation[20];
itoa(duration,rotation,DEC);
String rotationTopic = MQTTClientName + "/rotation";
rotationTopic.toCharArray(Topic,100);
client.publish(Topic, rotation);
}
//粉塵感測器初始化
void dustSetup() {
pinMode(ledPin, OUTPUT);
pinMode(A1, INPUT);
}
//粉塵感測器讀取
void dustRead() {
Serial.println("Dust Sensor");
digitalWrite(ledPin, LOW);
delayMicroseconds(samplingTime);
voMeasured = analogRead(measurePin);
delayMicroseconds(deltaTime);
digitalWrite(ledPin, HIGH);
delayMicroseconds(sleepTime);
calcVoltage = voMeasured * (5.0 / 1024);
dustDensity = 5000*calcVoltage/29 - 3000/29;
if (dustDensity < 0) {
dustDensity = 0.00;
}
Serial.print(dustDensity);
Serial.print("μg/m3\n");
char dust[20];
dtostrf(dustDensity,10,2,dust);
String dustTopic = MQTTClientName + "/dust";
dustTopic.toCharArray(Topic,100);
client.publish(Topic, dust);
}
//讀取MQ135
void MQ135Read() {
float voltage;
voltage = getVoltage(gasSensor);
Serial.println(voltage);
char AQI[20];
dtostrf(voltage,6,10,AQI);
String AQITopic = MQTTClientName + "/AQI";
AQITopic.toCharArray(Topic,100);
client.publish(Topic, AQI);
}
float getVoltage(int pin){
return (analogRead(pin) * 0.004882814);
// This equation converts the 0 to 1023 value that analogRead()
// returns, into a 0.0 to 5.0 value that is the true voltage
// being read at that pin.
}
void DHTSetup() {
dht.begin();//啟動DHT
}
void DHTRead() {
Serial.println("DHT Sensor");
float h = dht.readHumidity(); //讀取濕度
float t = dht.readTemperature(); //讀取攝氏溫度
if (isnan(h) || isnan(t)) {
Serial.println("無法從DHT感測器讀取!");
return;
}
//溫度經由埠傳出,字尾加 0x13
Serial.print(t);
Serial.println("℃");
//Serial.write( 0x13 );
// delay(500);
//濕度經由埠傳出,字尾加 0x66
Serial.print(h);
Serial.println("%RH");
//Serial.write( 0x66 );
char tmp[20];
char hum[20];
dtostrf(t,3,2,tmp);
dtostrf(h,3,2,hum);
String tmpTopic = MQTTClientName + "/tmp";
String humTopic = MQTTClientName + "/hum";
tmpTopic.toCharArray(Topic,100);
client.publish(Topic, tmp);
humTopic.toCharArray(Topic,100);
client.publish(Topic, hum);
}
// 接收MQTT主題后呼叫運行程式
void callback(char* topic, byte* payload, unsigned int length) {
String mapre01; // 預定接收的主題1 /power
String mapres; // 實際從 MQTT Broker 接收到的主題
String information; // 實際從 MQTT Broker 接收到的主題內容
String LEDOpen="1";
//將 char[] 轉為 String charToStringL(需轉換的 Char陣列, 轉換后的 String)
mapre01 = "Motor/power"; //電源 的主題
charToStringL(topic, mapres); //實際從 MQTT Broker 接收到的主題
for (int i = 0; i < length; i++) { //轉換 MQTT Broker 接收到的主題內容為字串
information = information + (char)payload[i];
}
if (mapres != mapre01) { // 不顯示發佈的主題
Serial.print("接收訊息 " + mapres + " [");
Serial.print(information);
Serial.print("] ");
}
if (mapres == mapre01) {
if (information == LEDOpen) {
Serial.println("打開 LED");
digitalWrite(POWERPIN, HIGH); //PIN 8輸出高電平,LED點亮
}
else {
Serial.println("關閉 LED");
digitalWrite(POWERPIN, LOW); //PIN 8輸出低電平,LED熄滅
}
}
delay(500); //延時500ms
Serial.println();
}
//將 char[] 轉為 String 自定函式
void charToStringL(const char S[], String & D)
{
byte at = 0;
const char *p = S;
D = "";
while (*p++) {
D.concat(S[at++]);
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
//設定 MQTT ========================================================
client.setServer("192.168.0.113", 1883); // 連線 MQTT Broker
client.setCallback(callback); // 設定從 MQTT Broker讀取主題之後,自動運行之副程式
Ethernet.begin(mac, ip, gateway, subnet); // 啟動網路
Serial.println("網路已經開通");
delay(1500);
pinMode(POWERPIN, OUTPUT); // 設定腳位 PIN 6為輸出模式
noiseSetup();
ElectricSetup();
MAX31865Setup();
BH1750Setup();
rotationSetup();
dustSetup();
DHTSetup();
}
void loop() {
// put your main code here, to run repeatedly:
if (!client.connected()) { // 如果未聯機 MQTT Broker 將重新聯機
reconnect();
}
client.loop();
noiseRead();
ElectricRead();
MAX31865Read();
BH1750_Loop();
viberateRead();
rotationRead();
dustRead();
MQ135Read();
DHTRead();
delay(2000);
}
void reconnect() {
// 一直循環直到連上 MQTT Broker
char MQTTClient[30];
MQTTClientName.toCharArray(MQTTClient,30);
while (!client.connected()) {
Serial.print("正在連線 MQTT Broker...");
if (client.connect(MQTTClient,"demo","123456")) {
Serial.println("MQTT Broker 已經連線上");
client.subscribe("Motor/power");
} else {
Serial.print("聯機失敗, rc=");
Serial.print(client.state());
Serial.println("五秒鐘之後再聯機");
delay(5000);
}
}
}
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# 6. 設計明細
開啟Smart智慧控制平臺,分別加入下插圖之控制元件。或者通過點選功能表欄[檔案]
-[打開專案]
選擇專案打開該範例。
①:TiPanel元件,控制元件名稱為iPanel1
。
②:TSwitchButton元件,控制元件名稱為sbPump
。
③:TiPanel元件,控制元件名稱為iPanel2
。
④:TiAnalogOutput元件,控制元件名稱從上到下依次為aoVoltage
、aoCurrent
、aoSurfTemp
、aoViberate
、aoRotate
。
⑤:TiPipe元件,顯示為綠色的管道,其控制元件名稱從左至右為iPipe1
、iPipe2
、iPipe3
。
⑥:TAXStandard元件,控制元件名稱為Warn
。
⑦:TiMotor元件,控制元件名稱為iMotor
。
⑧:TAXStandard元件,控制元件名稱為AXStandard1
。
⑨:TTimer元件,控制元件名稱為Timer1
。
⑩:TAbClock元件,控制元件名稱為AbClock1
。
(11):TMQTTClient元件,控制元件名稱為MQTTClient1
。
(12):TiPanel元件,控制元件名稱為iPanel3
。
(13):TiAnalogOutput元件,控制元件名稱從上到下依次為aoNoise
、aoTmp
、aoHum
、aoLight
、aoAQI
、aoDust
。
Main窗體屬性設定
Caption
:主窗體標題,設定為車間資訊採集與控制
。ClientHeight
:窗體客戶區高度=600
。ClientWidth
:窗體客戶區寬度=800
。
①iPanel1屬性設定
Height
:設定控制元件顯示高度,設定為600
。Width
:設定控制元件顯示寬度,設定為800
。TitleText
:設定顯示的標題,設定為車間資訊採集與控制
。
②sbPump屬性設定
Height
:設定控制元件顯示的高度,設定為100
。Width
:設定控制元件顯示的寬度,設定為100
。SwitchOff
:設定IsChecked
=False
時顯示的圖片。點選屬性右側的[...]
按鈕,打開檔案上傳界面,點選[Load...]
從檔案瀏覽器中選擇對應的圖片檔案上傳,返回該界面下,待顯示出圖片後點擊[OK]
載入圖片。
SwitchOn
:設定IsChecked
=True
時顯示的圖片。點選屬性右側的[...]
按鈕,打開檔案上傳界面,點選[Load...]
從檔案瀏覽器中選擇對應的圖片檔案上傳,返回該界面下,待顯示出圖片後點擊[OK]
載入圖片。
③iPanel2屬性設定
Height
:設定控制元件顯示高度,設定為282
。Width
:設定控制元件顯示寬度,設定為226
。TitleText
:設定顯示的標題,設定為電動機運行狀態
。
④aoVoltage,aoCurrent,aoSurfTemp,aoViberate,aoRotate屬性設定
Height
:設定控制元件高度,設定為30
。Width
:設定控制元件寬度,設定為140
。UnitsText
:設定顯示的單位名稱。aoVoltage
此處設定為V
。aoCurrent
此處設定為A
。aoSurfTemp
此處設定為℃
。aoRotate
此處設定為r/m
。
⑥Warn屬性設定
- 使用滑鼠右鍵單擊該控制元件,在右鍵選項中選擇
[編輯]
按鈕,打開圖示選擇器,在該選擇器中選擇如圖所示的圖示,點選[確定]
以切換為對應的圖示。
- 使用滑鼠右鍵單擊該控制元件,在右鍵選項中選擇
⑦iMotor屬性設定
- 使用滑鼠右鍵點選該控制元件,在彈出的右鍵功能表選項中選擇
[Edit]
,打開編輯器,按照圖示中的樣式設定,設定完成後點選[OK]
按鈕。
- 使用滑鼠右鍵點選該控制元件,在彈出的右鍵功能表選項中選擇
⑧AXStandard1屬性設定
- 使用滑鼠右鍵單擊該控制元件,在右鍵選項中選擇
[編輯]
按鈕,打開圖示選擇器,在該選擇器中選擇如圖所示的圖示,點選[確定]
以切換為對應的圖示。
- 使用滑鼠右鍵單擊該控制元件,在右鍵選項中選擇
⑩AbClock1屬性設定
ClockOption
:設定時鐘顯示的型別,設定為coDateTime
。
(11)MQTTClient1屬性設定
BrokerHostName
:設定連線的MQTT服務端的IP地址,設定為實際使用的MQTT服務端地址,例如設定為192.168.0.113
。BrokerPort
:設定連線的服務埠號,設定為1883
。ClientID
:設定連線到的客戶端的名稱,設定為SmartClient
。Password
:設定連線到的客戶端使用的登錄密碼。Topic
:設定訂閱的主題名稱,設定為Motor/#
,該中訂閱方式可以訂閱位於Motor/
下的所有主題資訊。UserName
:設定使用者名稱稱。
(12)iPanel3屬性設定
Height
:設定控制元件顯示高度,設定為328
。Width
:設定控制元件顯示寬度,設定為230
。TitleText
:設定顯示的標題,設定為車間狀態
。
(13)aoNoise,aoTmp,aoHum,aoLight,aoAQI,aoDust屬性設定
Height
:設定控制元件高度,設定為30
。Width
:設定控制元件寬度,設定為144
。UnitsText
:設定顯示的單位名稱。aoNoise
此處設定為dB
。aoTmp
此處設定為℃
。aoHum
此處設定為%RH
。aoLight
此處設定為lx
。aoDust
此處設定為μg/m^3
。
# 7. 程式設計
# 7.1. 程式初始設定
該程式無初始設定。
# 7.2. 事件設定
- (11)MQTTClient1-OnPublishReceived事件
當接收來自於指定主題的訂閱資訊時,解析並顯示。
procedure TMyHandler.MQTTClientPublishReceived;
begin
if ATopic = 'Motor/noise' then
begin
FThis.aoNoise.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/voltage' then
begin
FThis.aoVoltage.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/current' then
begin
FThis.aoCurrent.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/surftemp' then
begin
FThis.aoSurfTemp.Value := StrToFloat(APayload);
if (StrToFloat(APayload) > 70) and (FThis.sbPump.Enabled = True) then
begin
FThis.sbPump.IsChecked := False;
FThis.MQTTClient.PublishStr('Motor/power','0');
FThis.sbPump.Enabled := False;
FThis.Warn.Visible := True;
end;
if (StrToFloat(APayload) < 50) and (FThis.sbPump.Enabled = False) then
begin
FThis.sbPump.Enabled := True;
FThis.Warn.Visible := False;
end;
end;
if ATopic = 'Motor/light' then
begin
FThis.aoLight.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/viberate' then
begin
FThis.aoViberate.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/rotation' then
begin
if APayload = '-60' then
FThis.aoRotate.Value := 0
else
FThis.aoRotate.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/dust' then
begin
FThis.aoDust.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/AQI' then
begin
FThis.aoAQI.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/tmp' then
begin
FThis.aoTmp.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/hum' then
begin
FThis.aoHum.Value := StrToFloat(APayload);
end;
if ATopic = 'Motor/power' then
begin
if APayload = '1' then
begin
FThis.iMotor.FanOn := True;
FThis.iPipe1.FlowOn := True;
FThis.iPipe2.FlowOn := True;
FThis.iPipe3.FlowOn := True;
end
else
begin
FThis.iMotor.FanOn := False;
FThis.iPipe1.FlowOn := False;
FThis.iPipe2.FlowOn := False;
FThis.iPipe3.FlowOn := False;
end;
end;
end;
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
- ②sbPump-OnSwitch事件
切換開關的狀態時觸發該事件,向Motor/power
發送開關的資訊。
procedure TMyHandler.sbPumpSwitch;
begin
if FThis.sbPump.IsChecked then
begin
FThis.MQTTClient.PublishStr('Motor/power','1');
end
else
begin
FThis.MQTTClient.PublishStr('Motor/power','0');
end;
end;
2
3
4
5
6
7
8
9
10
11
- ⑨Timer1-OnTimer事件
定時器的觸發事件,該事件僅會觸發一次。用於初始化設備狀態。
procedure TMyHandler.Timer1Timer;
begin
FThis.Timer1.Enabled := False;
FThis.MQTTClient.PublishStr('Motor/power','0');
end;
2
3
4
5
# 8. 運行結果
使用滑鼠點選工具欄運行(Run),測試運行結果。
通過工具欄儲存,將程式儲存為 sdb 專案檔案。
當程式啟動后,電動機運行狀態與車間狀態處顯示對應的採集數據資訊,點選水泵開關
處的開關,指示燈亮,對應的狀態資訊會發生相應的變化。