西安 網(wǎng)站建設(shè) 培訓(xùn)學(xué)校搜索引擎哪個(gè)好

最近了解WEB大屏顯示。一般像嵌入式這類的,MQTT協(xié)議會(huì)走的多一些,走訂閱和發(fā)布的策略,網(wǎng)上走了一圈之后,目前有幾個(gè)實(shí)現(xiàn)方案。
這里對(duì)比一下幾個(gè)物聯(lián)網(wǎng)協(xié)議,相對(duì)而言MQTT更合適物聯(lián)網(wǎng),其它幾個(gè)協(xié)議不是干這個(gè)的,不過我推薦一下DDS,這玩意還挺好用的。

(ps:最近了解到一個(gè)團(tuán)隊(duì)的實(shí)現(xiàn)方案是tcp。。。什么魔鬼設(shè)計(jì),想的啥呢)
大屏實(shí)現(xiàn)方案
1.買物聯(lián)網(wǎng)網(wǎng)關(guān),附帶WEB大屏服務(wù),這里就不打廣告了,自己找一圈,蠻多有支持的。
優(yōu)勢(shì)是,有些基礎(chǔ)服務(wù)是免費(fèi)的,硬件是現(xiàn)成的,有詳細(xì)的指導(dǎo)文件。
缺點(diǎn)是,高級(jí)一些的服務(wù)是要收費(fèi)的,不合適大批量生產(chǎn),畢竟從底層硬件到前端程序都是人家的,要找到契合自己的網(wǎng)關(guān)也麻煩,比如我遇到的幾個(gè),都不支持MQTT協(xié)議接入。還有就是,數(shù)據(jù)都得去人家服務(wù)器,多少有點(diǎn)膈應(yīng)。

2.買云服務(wù)器,比如阿里云的可視化數(shù)據(jù)服務(wù),我整了個(gè)15天試用。
優(yōu)點(diǎn)是,數(shù)據(jù)都是自己的。
缺點(diǎn)是,要買一堆的配套服務(wù),比如協(xié)議轉(zhuǎn)換,服務(wù)器啥的,死貴死貴的。
3.技術(shù)牛皮,那就自己搭建服務(wù)器,然后寫前端后臺(tái)嵌入式,我能搞,但這玩意費(fèi)勁,要時(shí)間,而且中間肯定有那么兩個(gè)環(huán)節(jié)自己得摸索一段時(shí)間。
優(yōu)點(diǎn),啥都是自己的,可定制化程度高,省錢,只要有個(gè)服務(wù)器就行。
缺點(diǎn),費(fèi)時(shí)費(fèi)力。
4.找個(gè)低代碼平臺(tái),看了一下,網(wǎng)上這種公司有,只是要掏錢
優(yōu)點(diǎn),省時(shí)省力,不花多少錢。
缺點(diǎn),難找合適的。
推薦方案
對(duì)比一圈之后,還是選擇了低代碼平臺(tái),仔細(xì)想想,我是干嵌入式的,后臺(tái)前端這些,對(duì)我來說是行業(yè)外的東西,可以去了解,但不需要深入,關(guān)鍵省錢就行。
私有部署 - 使用教程-免費(fèi)低代碼數(shù)據(jù)可視化平臺(tái)-觸達(dá)云屏 (topthink.com)
上面的方案?jìng)€(gè)人覺得蠻合適,嗶哩嗶哩有教程,作者有個(gè)群,現(xiàn)在對(duì)大家都有技術(shù)支持,可以部署到局域網(wǎng),重點(diǎn)是對(duì)個(gè)人免費(fèi),我覺得不花錢就可以打敗一切了,這些都是他的資料。

服務(wù)器搭建
這部分其實(shí)是抄官方資料了,自己可以去看,我就抄linux搭建部分
全新安裝說明 | 免費(fèi)低代碼數(shù)據(jù)可視化平臺(tái)-觸達(dá)云屏 (chudayun.com)
1,基礎(chǔ)環(huán)境安裝
安裝以下基礎(chǔ)環(huán)境(參考各官網(wǎng))
Nginx
Mysql 5.7+
2,創(chuàng)建/導(dǎo)入數(shù)據(jù)庫
初始化數(shù)據(jù)庫:創(chuàng)建數(shù)據(jù)庫 chudy_data_visual,執(zhí)行 chudy_visual_[版本號(hào)] /doc 目錄下的 初始化SQL文件
3,創(chuàng)建安裝目錄
分別執(zhí)行以下三行命令,創(chuàng)建安裝目錄
cd /
mkdir app
mkdir app/java
4,部署安裝
上傳以下文件夾到服務(wù)器 /app 目錄下
chudy_visual_[版本號(hào)]
filesystem
chudy_designer
上傳 jdk-8u221-linux-x64.tar.gz文件 到服務(wù)器 /app/java目錄下,解壓。
cd /app/java
tar -zxvf jdk-8u221-linux-x64.tar.gz
修改數(shù)據(jù)庫連接配置文件:
/app/chudy_visual_[版本號(hào)] /mgr/config 目錄下 application-dev.properties
修改 數(shù)據(jù)庫端口、名稱、帳號(hào)、密碼 等信息。

5,配置nginx
上傳 chudy_designer 目錄下的 chudy_visual.conf 文件到 服務(wù)器 nginx的 conf.d 目錄下(一般在 /etc/nginx/conf.d/)
重載Nginx配置使配置生效。
防火墻中配置,放行端口 18088
6,啟動(dòng)應(yīng)用
cd /app/chudy_visual_[版本號(hào)]/mgr/
./mgr.sh start
tail -f visual_cms.out
mgr.sh 支持啟動(dòng)、停止、重啟、查看狀態(tài)
./mgr.sh start | stop | restart | status
7,訪問系統(tǒng)
http://IP:18088/
初始帳號(hào) sadmin 密碼 111111
設(shè)備端實(shí)現(xiàn)
目前測(cè)試,我是找了個(gè)溫濕度傳感器在折騰,實(shí)現(xiàn)方案是
傳感器->C#上位機(jī)->MQTT協(xié)議->云屏
這里放出C#的代碼(ps:反正是測(cè)試玩的,不重要)

代碼
using System;
using System.Text;
using System.Windows.Forms;using System.IO.Ports;using System.Net;
using System.Net.Sockets;using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;
using System.Threading.Tasks;
using System.Threading;namespace 開發(fā)快上位機(jī)
{public partial class Form1 : Form{public static IMqttClient _mqttClient;byte[] uart_data = new byte[100];byte uart_addr = 0;byte uart_start = 0;byte uart_time = 0;float wendu, shidu;public Form1(){InitializeComponent();System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;}private void Form1_Load(object sender, EventArgs e){button8.Enabled = false;serialPort1.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);//必須手動(dòng)添加事件處理程序}private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)//串口數(shù)據(jù)接收事件{try{uart_start = 1;uart_time = 0;int ilen = serialPort1.BytesToRead;byte[] bytes = new byte[ilen];serialPort1.Read(bytes, 0, ilen);for (int i = 0; i < ilen; i++){uart_data[uart_addr] = bytes[i];uart_addr++;}/* if (!radioButton6.Checked)//如果接收模式為字符模式{int ilen = serialPort1.BytesToRead;byte[] bytes = new byte[ilen];serialPort1.Read(bytes, 0, ilen);//string str = System.Text.Encoding.Default.GetString(bytes); //xx="中文";//textBox1.AppendText(str);//添加內(nèi)容}else{ //如果接收模式為數(shù)值接收byte data;data = (byte)serialPort1.ReadByte();//此處需要強(qiáng)制類型轉(zhuǎn)換,將(int)類型數(shù)據(jù)轉(zhuǎn)換為(byte類型數(shù)據(jù),不必考慮是否會(huì)丟失數(shù)據(jù)//string str = Convert.ToString(data, 16).ToUpper();//轉(zhuǎn)換為大寫十六進(jìn)制字符串//textBox1.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");//空位補(bǔ)“0” }*/}catch{textBox1.AppendText("串口數(shù)據(jù)接收出錯(cuò),請(qǐng)檢查!\r\n");}}private void button1_Click(object sender, EventArgs e){if (button1.Text == "串口連接"){try{serialPort1.PortName = comboBox1.Text;serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);serialPort1.Open();button1.Text = "斷開連接";button2.Enabled = false;panel2.Enabled = false;comboBox1.Enabled = false;comboBox2.Enabled = false;comboBox3.Enabled = false;comboBox4.Enabled = false;textBox1.AppendText("串口已連接\r\n");}catch{if (serialPort1.IsOpen)serialPort1.Close();button1.Text = "串口連接";button2.Enabled = true;comboBox1.Enabled = true;panel2.Enabled = true;comboBox2.Enabled = true;comboBox3.Enabled = true;comboBox4.Enabled = true;textBox1.AppendText("請(qǐng)檢查串口連接\r\n");}}else if (button1.Text == "斷開連接"){try{serialPort1.Close();button1.Text = "串口連接";button2.Enabled = true;comboBox1.Enabled = true;comboBox2.Enabled = true;comboBox3.Enabled = true;panel2.Enabled = true;comboBox4.Enabled = true;textBox1.AppendText("串口已斷開\r\n");}catch { }}}private void SearchAndAddSerialToComboBox(SerialPort MyPort, ComboBox MyBox){ //將可用端口號(hào)添加到ComboBoxstring Buffer; //緩存string[] MyString = new string[Convert.ToInt32(comboBox3.Text)]; //最多容納20個(gè),太多會(huì)影響調(diào)試效率 MyBox.Items.Clear(); //清空ComboBox內(nèi)容for (int i = 1; i < Convert.ToInt32(comboBox3.Text); i++) //循環(huán){try{ //核心原理是依靠try和catch完成遍歷progressBar1.Value = i * (100 / Convert.ToInt32(comboBox3.Text));Buffer = "COM" + i.ToString();MyPort.PortName = Buffer;MyPort.Open(); //如果失敗,后面的代碼不會(huì)執(zhí)行MyString[i - 1] = Buffer;MyBox.Items.Add(Buffer); //打開成功,添加至下倆列表comboBox1.Text = Buffer.ToString();MyPort.Close(); //關(guān)閉}catch { }}}private void button2_Click(object sender, EventArgs e){textBox1.AppendText("開始自動(dòng)配置串口\r\n");//出錯(cuò)提示textBox1.AppendText("串口掃描\r\n");//出錯(cuò)提示SearchAndAddSerialToComboBox(serialPort1, comboBox1); //掃描并講課用串口添加至下拉列表textBox1.AppendText("端口掃描完畢\r\n");//出錯(cuò)提示textBox1.AppendText("正在配置波特率\r\n");//出錯(cuò)提示comboBox2.Text = comboBox4.Text;serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);progressBar1.Value = 0;textBox1.AppendText("自動(dòng)配置完成\r\n");//出錯(cuò)提示button1_Click(sender, e);}private void button3_Click(object sender, EventArgs e){textBox1.Clear();}private void button5_Click(object sender, EventArgs e){textBox3.Clear();}private void button4_Click(object sender, EventArgs e){System.IO.File.WriteAllText(@"C:\Users\Administrator\Desktop\發(fā)送區(qū)數(shù)據(jù).txt", textBox1.Text);System.IO.File.WriteAllText(@"C:\Users\Administrator\Desktop\接收區(qū)數(shù)據(jù).txt", textBox3.Text);textBox1.AppendText("數(shù)據(jù)保存完成!\r\n");}void uart_send(object sender, EventArgs e,string data){byte[] Data = new byte[1];//作用同上集if (serialPort1.IsOpen)//判斷串口是否打開,如果打開執(zhí)行下一步操作{try{if (data != ""){if (!radioButton6.Checked)//如果發(fā)送模式是字符模式{try{//實(shí)現(xiàn)串口發(fā)送漢字Encoding gb = System.Text.Encoding.GetEncoding("gb2312");byte[] bytes = gb.GetBytes(data);serialPort1.Write(bytes, 0, bytes.Length);}catch{textBox1.AppendText("串口數(shù)據(jù)寫入錯(cuò)誤\r\n");//出錯(cuò)提示serialPort1.Close();button1_Click(sender, e);}}else{for (int i = 0; i < (data.Length - data.Length % 2) / 2; i++)//取余3運(yùn)算作用是防止用戶輸入的字符為奇數(shù)個(gè){Data[0] = Convert.ToByte(data.Substring(i * 2, 2), 16);serialPort1.Write(Data, 0, 1);//循環(huán)發(fā)送(如果輸入字符為0A0BB,則只發(fā)送0A,0B)}if (data.Length % 2 != 0)//剩下一位單獨(dú)處理{Data[0] = Convert.ToByte(data.Substring(data.Length - 1, 1), 16);//單獨(dú)發(fā)送B(0B)serialPort1.Write(Data, 0, 1);//發(fā)送}}}}catch{textBox1.AppendText("串口數(shù)據(jù)寫入錯(cuò)誤\r\n");//出錯(cuò)提示}}}private void button6_Click(object sender, EventArgs e){uart_send(sender,e, textBox3.Text);}private void button9_Click(object sender, EventArgs e){var optionsBuilder = new MqttClientOptionsBuilder().WithTcpServer(textBox2.Text, Convert.ToInt16(textBox4.Text)) // 要訪問的mqtt服務(wù)端的 ip 和 端口號(hào).WithCredentials("admin", "123456") // 要訪問的mqtt服務(wù)端的用戶名和密碼.WithClientId("testclient02") // 設(shè)置客戶端id.WithCleanSession().WithTls(new MqttClientOptionsBuilderTlsParameters{UseTls = false // 是否使用 tls加密});var clientOptions = optionsBuilder.Build();_mqttClient = new MqttFactory().CreateMqttClient();_mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync; // 客戶端連接成功事件_mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync; // 客戶端連接關(guān)閉事件_mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync; // 收到消息事件_mqttClient.ConnectAsync(clientOptions);}/// <summary>/// 客戶端連接關(guān)閉事件/// </summary>/// <param name="arg"></param>/// <returns></returns>private Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg){button8.Enabled = false;button9.Text = "連接";textBox6.AppendText($"客戶端已斷開與服務(wù)端的連接……\n");return Task.CompletedTask;}/// <summary>/// 客戶端連接成功事件/// </summary>/// <param name="arg"></param>/// <returns></returns>private Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg){button8.Enabled = Enabled;button9.Text = "斷開";textBox6.AppendText($"客戶端已連接服務(wù)端……\n");// 訂閱消息主題// MqttQualityOfServiceLevel: (QoS): 0 最多一次,接收者不確認(rèn)收到消息,并且消息不被發(fā)送者存儲(chǔ)和重新發(fā)送提供與底層 TCP 協(xié)議相同的保證。// 1: 保證一條消息至少有一次會(huì)傳遞給接收方。發(fā)送方存儲(chǔ)消息,直到它從接收方收到確認(rèn)收到消息的數(shù)據(jù)包。一條消息可以多次發(fā)送或傳遞。// 2: 保證每條消息僅由預(yù)期的收件人接收一次。級(jí)別2是最安全和最慢的服務(wù)質(zhì)量級(jí)別,保證由發(fā)送方和接收方之間的至少兩個(gè)請(qǐng)求/響應(yīng)(四次握手)。_mqttClient.SubscribeAsync("sub", MqttQualityOfServiceLevel.AtLeastOnce);return Task.CompletedTask;}/// <summary>/// 收到消息事件/// </summary>/// <param name="arg"></param>/// <returns></returns>private Task _mqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg){textBox6.AppendText($"Topic主題=【{arg.ApplicationMessage.Topic}】 消息={Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}");string str = Encoding.UTF8.GetString(arg.ApplicationMessage.Payload);if (str.Contains("on") || str.Contains("off")){textBox6.AppendText("\r\n控制繼電器\r\n");//出錯(cuò)提示byte[] Data = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xc4, 0x0b };//作用同上集if (serialPort1.IsOpen)//判斷串口是否打開,如果打開執(zhí)行下一步操作{try{serialPort1.Write(Data, 0, 8);//循環(huán)發(fā)送(如果輸入字符為0A0BB,則只發(fā)送0A,0B)}catch{textBox1.AppendText("串口數(shù)據(jù)寫入錯(cuò)誤\r\n");//出錯(cuò)提示}}}//textBox6.AppendText($"ApplicationMessageReceivedAsync:客戶端ID=【{arg.ClientId}】接收到消息。 Topic主題=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.Payload)}】 qos等級(jí)=【{arg.ApplicationMessage.QualityOfServiceLevel}】");return Task.CompletedTask;}public void Publish(string topic,string data){var message = new MqttApplicationMessage{Topic = topic,Payload = Encoding.Default.GetBytes(data),QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,Retain = true // 服務(wù)端是否保留消息。true為保留,如果有新的訂閱者連接,就會(huì)立馬收到該消息。};_mqttClient.PublishAsync(message);}private void button8_Click(object sender, EventArgs e){if(textBox8.Text != "")Publish(textBox7.Text,textBox8.Text);}private void button7_Click(object sender, EventArgs e){textBox6.Text = "";textBox8.Text = "";}private void button10_Click(object sender, EventArgs e){textBox6.AppendText("\r\n讀取溫度\r\n");//出錯(cuò)提示byte[] Data = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xc4, 0x0b };//作用同上集if (serialPort1.IsOpen)//判斷串口是否打開,如果打開執(zhí)行下一步操作{try{serialPort1.Write(Data, 0, 8);//循環(huán)發(fā)送(如果輸入字符為0A0BB,則只發(fā)送0A,0B)}catch{textBox1.AppendText("串口數(shù)據(jù)寫入錯(cuò)誤\r\n");//出錯(cuò)提示}}}string zhongliang1 = "{\"value\":";string zhongliang2 = ",\"unit\": \"Kg\",\"min\": 0,\"max\": 100,\"label\": \"11\"}";string wenshidu1 = "[{\"extObj\": {},\"value\": \"";string wenshidu2 = "\",\"prefixText\": \"\",\"suffixText\": \"\",\"descText\": \"\",\"backgroundColor\": \"\",\"icon\": \"\",\"color\": \"\"}]";private void button11_Click(object sender, EventArgs e){string data;data = wenshidu1 + textBox9.Text + wenshidu2;Publish("wendu", data);}private void button12_Click(object sender, EventArgs e){string data;data = wenshidu1 + textBox10.Text + wenshidu2;Publish("shidu", data);}private void button13_Click(object sender, EventArgs e){string data;data = zhongliang1 + textBox11.Text + zhongliang2;Publish("zhongliang", data);}private void timer1_Tick(object sender, EventArgs e){if (uart_start == 1){uart_time++;if (uart_time >= 100){textBox1.AppendText(uart_addr.ToString());//空位補(bǔ)“0”for (int i = 0; i < uart_addr; i++){string str = Convert.ToString(uart_data[i], 16).ToUpper();//轉(zhuǎn)換為大寫十六進(jìn)制字符串textBox1.AppendText("0x" + (str.Length == 1 ? "0" + str : str) + " ");//空位補(bǔ)“0”}if (uart_data[0] == 0x01 && uart_data[1] == 0x03 && uart_data[2] == 0x04){shidu = uart_data[3] << 8 | uart_data[4];wendu = uart_data[5] << 8 | uart_data[6];shidu = shidu / 10;wendu = wendu / 10;string str1,str2;textBox9.Text = wendu.ToString();textBox10.Text = shidu.ToString();button11_Click(sender, e);button12_Click(sender, e);}uart_start = 0;uart_time = 0;uart_addr = 0;}}}}
}
測(cè)試效果

簡化操作,直接搞了幾個(gè)按鍵去發(fā)
當(dāng)然實(shí)際項(xiàng)目不會(huì)這么干,實(shí)際項(xiàng)目的構(gòu)成應(yīng)該是
單片機(jī)接傳感器,以MODBUS/無線等方式提供數(shù)據(jù)訪問接口
linux網(wǎng)關(guān)實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)發(fā),比如NXP的IMX6,上面實(shí)現(xiàn)MQTT客戶端
云服務(wù)器做MQTT服務(wù)器,實(shí)現(xiàn)數(shù)據(jù)轉(zhuǎn)發(fā)
WEB云屏/微信小程序/桌面應(yīng)用等訂閱MQTT數(shù)據(jù)

上面是我的微信和QQ群,歡迎新朋友的加入。