NMEA协议 上位机 C# (转)
前些时间写做了两款用NMEA协议的上位机,在这里做一个总结和记录。和大家分享,也为了以后不会忘记。
NMEA协议总体来说,相对简单,是气象上比较成熟的协议。
主要有以下几个参数及其格式:
- 风速和风向;
- 空气温度;
- 土壤温度;
- 湿度;
- 大气压;
注:
其中效验部分主要采用的 异或效验:即从$后第一个字符到’*’前一个字符进行异或。所得到的数据高4位,低4位的ASCII码。
<CR>: ‘\r’
<LF>: ‘\n’
波特率:4800
刷新频率:1Hz
在上位机的设计中,所有的接收方式都是被动的。所以处理上非常好做,只需要读取特定的数据位就可以,将其转为数据即可。
由于每一段数据都是以’\n’结尾,所以串口中断serialPortX_DataReceived()中直接采用serialPortX.ReadLine()读取数据就可以了。读取后根据协议分析其数据就可以了。以下是我的代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 气象监控_NMEA { class NMEA { #region 变量 public float speed = 0f; public float direction = 0f; public float airTemp = 0f; public float soilTemp = 0f; public float humidity = 0f; public float barometer = 0f; public bool speedStatus = false; private const int MAX_LENGTH = 24; private readonly byte SUM_END = Convert.ToByte('*'); private readonly byte SUM_START = Convert.ToByte('$'); private readonly byte NUM_0_ASCII = Convert.ToByte('0'); private readonly byte NUM_A_ASCII = Convert.ToByte('A' - 10); #endregion #region 属性 public float GetSpeed { get { return speed; } set { speed = value; } } public float GetDirection { get { return direction; } set { direction = value; } } public bool GetStatus { get { return speedStatus; } set { speedStatus = value; } } public float GetAirTemp { get { return airTemp; } set { airTemp = value; } } public float GetSoilTemp { get { return soilTemp; } set { soilTemp = value; } } public float GetHumidity { get { return humidity; } set { humidity = value; } } public float GetBarometer { get { return barometer; } set { barometer = value; } } #endregion #region 子函数 /// <summary> /// 数字转ASCII /// </summary> /// <param name="Integer">单个位整数</param> /// <returns>ASCII</returns> public byte Integer2Char(int Integer) { byte lcv_ch = 0; if (Integer <= 9) { lcv_ch = Convert.ToByte(Integer + NUM_0_ASCII); } else if ((Integer >= 0x0A) && (Integer <= 0x0F)) { lcv_ch = Convert.ToByte(Integer + NUM_A_ASCII); } return lcv_ch; } #endregion /// <summary> /// sum效验 /// </summary> /// <param name="array">效验数组</param> /// <returns>效验值,字符被拆分为两个ASCII码整和为一个Int,高位在int高8位,低后</returns> public int CheckSum(byte[] array) { byte sum = 0; int res = 0; int i; for (i = 1; (array[i] != SUM_END) && (i < MAX_LENGTH); i++ ) { sum ^= array[i]; } if (i != MAX_LENGTH) res = (Integer2Char((sum >> 4)) << 8) | Integer2Char(sum & 0xF); return res; } /// <summary> /// 从接收到的字符串中,取出有用数据 /// </summary> /// <param name="str">接收到的字符串</param> public void DataProcess(string str) { char[] chSplit = {',',}; string[] strArray = str.Split(chSplit); switch (strArray[0]) { case "$WIMWV" : direction = float.Parse(strArray[1]); speed = float.Parse(strArray[3]); char[] chArray = strArray[5].ToCharArray(); speedStatus = (chArray[0] == 'A' ? true : false); break; case "$WIMTA" : airTemp = float.Parse(strArray[1]); break; case "$WIMTS" : soilTemp = float.Parse(strArray[1]); break; case "$WIMHU" : humidity = float.Parse(strArray[1]); break; case "$WIMMB" : barometer = float.Parse(strArray[3]); break; default: break; } } /// <summary> /// 接收到的数据正确性判断 /// </summary> /// <param name="str">接收到的字符串</param> /// <returns>效验正常返回true</returns> public bool ReceiveCheck(string str) { bool res = false; char[] chSplit = {'*',}; string[] strArray = str.Split(chSplit); if (strArray.Length == 2) { if (strArray[1].Length == 3) //长度正常 { byte[] array = Encoding.Default.GetBytes(strArray[1]); int check = CheckSum(Encoding.Default.GetBytes(str)); if (check != 0) { if (check == ((array[0] << 8) | array[1])) { res = true; } } } } return res; } } }