# Angular+Mqtt+php+Mariadb ## [先參照前篇完成Angular呼叫PHP,MariaDB](https://hackmd.io/@109213067/BJBlg0Mio/https%3A%2F%2Fhackmd.io%2FbGorCTyyT-6MsC1cXhoRIQ%3Fview) ## 情境 * Angular主機發送和顯示訊息(ip 192.168.1.245) * Mqtt主機接收訊息(ip 192.168.1.244) ## 流程 * 發送訊息: Angular->PHP->Mqtt * 顯示訊息: Mqtt->Angular->php->Mariadb(存到資料庫) ## 防火牆設定 * Mqtt主機開啟1883和9001 port `sudo firewall-cmd --permanent --add-port=1883/tcp` `sudo firewall-cmd --permanent --add-port=9001/tcp` ## 1. 發送訊息 ### 安裝php&apache * [先照前一篇的步驟完成安裝](https://hackmd.io/bGorCTyyT-6MsC1cXhoRIQ?view) ### mqtt設定 * Mqtt主機更改mosquitto設定檔 `$ sudo vim /etc/mosquitto/mosquitto.conf` ```=1 listener 1883 192.168.1.244 ``` ### php連接mqtt * [關閉SELinux(可能會阻擋連線)](https://blog.xuite.net/tolarku/blog/195633562-CentOS+%E9%97%9C%E9%96%89+selinux#) sudo vim /etc/sysconfig/selinux 找到 SELINUX=enforcing 然後修改為 SELINUX=disabled 要重新開機 reboot /restart 後才會套用 * 安裝mqtt-client `$ composer require php-mqtt/client` * 在/var/www/html下新增connectMqtt.php ```php=1 <?php header('Access-Control-Allow-Origin: *'); header('Content-Type: application/json; charset=UTF-8'); header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS'); header('Access-Control-Max-Age: 3600'); header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Headers, Authorization, X-Requested-With'); header('Cache-Control: no-cache, must-revalidate'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); header('Access-Control-Allow-Credentials: true'); $data = json_decode(file_get_contents("php://input")); if (!isset($data->message)) { echo json_encode(['success' => 0, 'message' => 'some fields are empty!']); exit; }; $message = $data->message; require('vendor/autoload.php'); use \PhpMqtt\Client\MqttClient; use \PhpMqtt\Client\ConnectionSettings; $server = '192.168.1.244'; // mqtt server ip $port = 1883; $clientId = rand(5, 15); $username = 'emqx_user'; $password = null; $clean_session = false; $connectionSettings = new ConnectionSettings(); $connectionSettings ->setUsername($username) ->setPassword(null) ->setKeepAliveInterval(60) // Last Will 設定 ->setLastWillTopic('emqx/test/last-will') ->setLastWillMessage('client disconnect') ->setLastWillQualityOfService(1); $mqtt = new MqttClient($server, $port, $clientId); $mqtt->connect($connectionSettings); // 發布 (topic_name, context) $mqtt->publish('Rocky', $message, 0); //echo $mqtt; // 訂閱 (topic_name) //$mqtt->subscribe('Rocky', function ($topic, $message) { //printf("Received message on topic [%s]: %s\n", $topic, $message); //}, 0); // 持續訂閱 //$mqtt->loop(true); // 放開連線 $mqtt->disconnect(); ?> ``` ### angular呼叫php * data.service.ts新增函式呼叫connectMqtt.php ```c#=1 sendMqtt(text: Object){ return this.http.post('http://localhost/connectMqtt.php',text); } ``` * app.component.ts新增函式呼叫service的sendMqtt函式 ```c#=1 sendMqtt(message: string) { const text = {message:message}; this.dataService.sendMqtt(text).subscribe(response => { alert('send text to Mqtt!'); window.location.reload(); }); } ``` * app.component.html新增按鈕呼叫ts的sendMqtt函式 ```html=1 <input #sender placeholder="send message"/> <button (click)="sendMqtt(sender.value);sender.value=''">send</button> ``` ### 測試連線 * 一台主機輸入要送出的訊息,另一台主機當作訂閱者 ![](https://i.imgur.com/UYWtIh3.png) * 送出訊息 ![](https://i.imgur.com/HbvfrpU.png) ### 用app-routing.ts和新增元件把Todo,Mqtt分隔開來 * app-routing.ts內容: ```c#=1 import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { TodoComponent } from './todo/todo.component' import { ConnectMqttComponent } from './connect-mqtt/connect-mqtt.component' const routes: Routes = [ {path:'todo',component:TodoComponent}, {path:'mqtt',component:ConnectMqttComponent}, ] @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { } ``` * app.component.html內容 ```html=1 <button routerLink="/todo">Todolist</button> <button routerLink="/mqtt">Mqtt</button> <router-outlet></router-outlet> ``` * app.component.ts內容 ```c#=1 import { Component, NgModule, OnInit} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ ngOnInit() { } title="app"; } ``` * `ng g c todo`新增todo元件 * `ng g c connnect-mqtt`新增connect-mqtt元件 * todo.components.html內容: ```html=1 <h1>Todo List</h1> <router-outlet></router-outlet> <h2>Create Job</h2> <input #myTitle placeholder="job title"/> <input #myDate placeholder="job date"/> <input #myContent placeholder="job content"/> <button (click)="addJob(myTitle.value,myDate.value,myContent.value); myTitle.value='';myDate.value='';myContent.value=''">Create</button> <table> <tr><td>num</td><td>title</td><td>date</td><td>content</td><td>done</td></tr> <tr *ngFor="let i of myJob"><td>{{i.id}}</td><td>{{i.title}}</td><td>{{i.date}}</td><td>{{i.content}}</td><td>{{i.done}}</td><td><button (click)="removeJob(i.id)">Delete</button></td><app-edit-job [theJob]="i"></app-edit-job></tr> </table> <input #searcher placeholder="job id"/> <button (click)="searchJob(searcher.value)">search</button> <button onClick="window.location.reload()">reset</button><br> ``` * todo.component.ts內容: ```c#=1 import { Component } from '@angular/core'; import { DataService } from '../data.service'; @Component({ selector: 'app-todo', templateUrl: './todo.component.html', styleUrls: ['./todo.component.css'] }) export class TodoComponent { data:any; constructor(private dataService: DataService) {} ngOnInit() { this.dataService.getData().subscribe((data) => { this.data = data; }); } // title = 'firstapp'; // defined filter words //search = ""; // defiend job //allJobs = [ //{title:"hello",date: new Date("2023-01-01"),content:"say hello",done:false}, //{title:"goodbye",date:new Date("2023-01-02"),content:"say goodbye",done:false}, //] // display job to html get myJob() { //if (this.search=="") { return this.data; //return this.allJobs; //} //let filterJobs = this.data.filter(function(data:job) { //return (data.title === filter || data.content === filter); //}); //return this.data; } // function addJob addJob(title: string, date:string, content: string) { if (title=="" || date=="Invalid Date" || content=="") { alert("please fill in all filed!"); return; } const job = {title:title,date:date,content:content}; this.dataService.addData(job).subscribe(response=>{ //console.log(response); alert('add job success!'); window.location.reload(); }); } searchJob(filter: string) { const job = {keywords:filter}; this.dataService.searchData(job).subscribe((data) => { this.data = data; }) } // function removeJob removeJob(id: string) { const job = {id:id}; this.dataService.deleteData(job).subscribe(response=>{ alert('delete job success!'); window.location.reload(); }); } convertDate(date: string) { return new Date(date); } } ``` * connect-mqtt.component.html內容: ```html=1 <h1>Mqtt</h1> <router-outlet></router-outlet> <input #sender placeholder="send message"/> <button (click)="sendMqtt(sender.value);sender.value=''">send</button> ``` * connect-mqtt.component.ts內容: ```c#=1 import { Component } from '@angular/core'; import { DataService } from '../data.service'; @Component({ selector: 'app-connect-mqtt', templateUrl: './connect-mqtt.component.html', styleUrls: ['./connect-mqtt.component.css'] }) export class ConnectMqttComponent { constructor(private dataService: DataService) {} ngOnInit() { } // title = 'Mqtt'; sendMqtt(message: string) { const text = {message:message}; this.dataService.sendMqtt(text).subscribe(response => { alert('send text to Mqtt!'); //window.location.reload(); }); } } ``` ## 2. 接收訊息 ### 安裝mqtt套件 ``` npm install ngx-mqtt ``` ### 設定mqtt連線 * app.module.ts內容: ```c#=1 import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { EditJobComponent } from './edit-job/edit-job.component'; import { HttpClientModule } from '@angular/common/http'; import { DataService } from './data.service'; import { MqttModule } from 'ngx-mqtt'; import { ConnectMqttComponent } from './connect-mqtt/connect-mqtt.component'; import { TodoComponent } from './todo/todo.component'; @NgModule({ declarations: [ AppComponent, EditJobComponent, ConnectMqttComponent, TodoComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule, MqttModule.forRoot({ hostname: '192.168.1.244', port: 9001, protocol:'ws', path: '/mqtt' }), ], providers: [DataService], bootstrap: [AppComponent], }) export class AppModule { } ``` ### 修改mqtt設定檔 `sudo vim /etc/mosquitto/mosquitto.conf` * 修改內容如下: ```=1 listener 1883 192.168.1.244 protocol mqtt listener 9001 protocol websockets ``` ### php連線Maraidb * 資料庫新增資料表mqttText,欄位有id(auto_increment)和message ```sql=1 CREATE TABLE `mqttText` (`id` int(10) NOT NULL PRIMARY KEY auto_increment,`messsage` varchar(50) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ``` * 在/var/www/html新增storeMqtt.php,內容如下: ```php=1 <?php header('Access-Control-Allow-Origin: *'); header('Content-Type: application/json; charset=UTF-8'); header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS'); header('Access-Control-Max-Age: 3600'); header('Access-Control-Allow-Headers: Content-Type,Access-Control-Allow-Headers, Authorization, X-Requested-With'); header('Cache-Control: no-cache, must-revalidate'); header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); header('Access-Control-Allow-Credentials: true'); $servername = "localhost"; $username = "kenny"; $password = "Kenny061256"; $dbname = "test"; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // $data = json_decode(file_get_contents("php://input")); //echo json_encode($data->message); if (!isset($data->content)) { echo json_encode(['success' => 0, 'message' => 'some fields are empty!']); exit; }; // Check connection if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } $message = $data->content; $sql = "insert into `mqttText`(message)values("; $sql .= "'".$message."')"; //$conn->execute($sql); $conn->query($sql); $conn->close(); ?> ``` ### 修改Angular程式 * data.service.ts內容: ```c#=1 storeMqtt(text:Object){ return this.http.post('http://localhsot/storeMqtt.php',text); } ``` * connect-mqtt.component.html內容: ```html=1 <h1>Mqtt</h1> <router-outlet></router-outlet> <input #sender placeholder="send message"/> <button (click)="sendMqtt(sender.value);sender.value=''">send</button> <h2>Message</h2> <table> <tr><td>No.</td><td>Text</td></tr> <tr *ngFor="let text of message;let i = index"><td>{{i+1}}</td><td>{{text}}</td></tr> </table> ``` * connect-mqtt.component.ts內容: ```c#=1 import { Component } from '@angular/core'; import { DataService } from '../data.service'; import { MqttService } from 'ngx-mqtt'; @Component({ selector: 'app-connect-mqtt', templateUrl: './connect-mqtt.component.html', styleUrls: ['./connect-mqtt.component.css'] }) export class ConnectMqttComponent { message:string[]=[]; constructor(private dataService: DataService,private mqttService: MqttService) {} ngOnInit() { this.mqttService.connect(); this.mqttService.observe('Rocky').subscribe((message)=>{ this.message.push(message.payload.toString()); const text = {content:message.payload.toString()}; //console.log(text); this.dataService.storeMqtt(text).subscribe(response=>{ }); }); } // title = 'Mqtt'; sendMqtt(message: string) { const text = {message:message}; this.dataService.sendMqtt(text).subscribe(response => { alert('send text to Mqtt!'); //window.location.reload(); }); } } ```