寫在前面
有小伙伴跟我說:

為了兌現承諾,這次給大家安排一個小項目案例,不管你是小白,還是有一定基礎,都可以參與進來,一起把這個項目做出來。
項目需求
1、建立一個室內空氣質量檢測系統,要求電腦軟件+單片機硬件,編程語言及硬件可以自行選擇。
2、系統分為三部分:數據采集部分、數據傳輸部分、系統軟件交互部分。
3、需求檢測的數據:IAQ(Indoor Air Quality)、溫度、濕度、PM2.5、CO2、CO。
4、數據傳輸可以選擇串口、以太網、WIFI、藍牙中的一種。
5、當檢測到空氣質量過差時,系統需要給出相應的解決方案(如:提示開窗通風等)。
協議分析
這個項目其實涉及到單片機開發,但這塊並不是上位機的范疇。在確定好上位機與單片機之間的通信協議之后,雙方都按照這個協議來開發,最終進行對接就可以了。這個協議一般是由單片機開發人員制定,本例協議制定如下:
- 單片機發送的數據格式:#TXXX,YYY,S@
1、#后面的大寫英文T表示對應的參數類型,具體對應關系如下:
序號 | 參數名稱 | 參數代號 |
---|---|---|
1 | 煙霧 | S |
2 | 溫度 | T |
3 | 濕度 | H |
4 | PM2.5 | P |
5 | 二氧化碳 | C |
6 | 一氧化碳 | O |
2、XXX:代表檢測數值,占三個字符,例如010就是10。
3、YYY:代表該數組的限值,占三個字符,例如010就是10。
4、符號@前的S表示報警狀態,1表示限值報警,0表示正常
- 上位機進行限值設置發送數據格式:&SXXX$
1、&號后面的大寫英文T表示對應的參數類型,對應關系同上。
2、XXX代表設置的限值數據,占三個字符
3、發送報文以$結束。
界面設計
根據項目需求,並結合協議設計界面如下:

本案例的一個關鍵知識點在於自定義組合控件的使用。
如果讓你來設計這個界面,你是否會考慮組合控件應用,如果全部用的是系統控件,那么代碼會寫得很復雜,而且容易出錯,更重要的是擴展性會很差——如果需求發生變化,會需要改很多東西。
這里把參數的一系列狀態及操作做成一個自定義控件,然后將相關的功能抽象成屬性及事件。
序號 | 名稱 | 讀寫 | 類型 |
---|---|---|---|
1 | 參數名稱 | 讀寫 | 屬性 |
2 | 實際值 | 讀寫 | 屬性 |
3 | 報警狀態 | 讀寫 | 屬性 |
4 | 設定反饋 | 讀寫 | 屬性 |
5 | 設定值 | 只讀 | 屬性 |
6 | 設置操作 | --- | 事件 |
實時通信
這里采用串口通信,針對SerialPort封裝一個串口類,這里會涉及到串口通信、鎖處理、委托技術,核心為串口接收事件,代碼如下:
private void MyCom_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(50);
int ByteToRead = MyCom.BytesToRead;
lock (Lock)
{
//定義一個字節數組
byte[] rcv = new byte[ByteToRead];
//讀取緩沖區里的數據放到字節數組中
MyCom.Read(rcv, 0, ByteToRead);
//這里如果想把字節數組給到主線程,需要使用委托
ShowMsg?.Invoke(rcv);
}
}
參數設置功能:
public bool SetValue(int Speed,Type type)
{
StringBuilder stringBuilder = new StringBuilder("&");
stringBuilder.Append(type.ToString());
stringBuilder.Append(Speed.ToString().PadLeft(3, '0'));
stringBuilder.Append("$");
lock (Lock)
{
try
{
this.MyCom.Write(stringBuilder.ToString());
}
catch (Exception)
{
return false;
}
return true;
}
}
主界面功能
有了以上的基礎之后,主界面的功能開發就很容易了。
主界面的功能主要是兩部分內容,第一部分內容是數據解析,將解析結果賦值給控件的屬性進行顯示,第二部分內容是通用的參數設置,將6個Monitor控件的參數設置事件都綁定同一個事件,然后在該事件里調用SetValue方法即可。
一般我們軟件開發完成后,都應該先自我測試一下,便於及時發現問題,減少調試時間。
對於串口通信,可以我們通過虛擬串口來進行測試,仿真數據發送與數據接收,測試結果如下:
