前些时间写做了两款用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 = ;
private readonly byte SUM_END = Convert.ToByte('*');
private readonly byte SUM_START = Convert.ToByte('$');
private readonly byte NUM_0_ASCII = Convert.ToByte('');
private readonly byte NUM_A_ASCII = Convert.ToByte('A' - );
#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 = ;
if (Integer <= )
{
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 = ;
int res = ;
int i;
for (i = ; (array[i] != SUM_END) && (i < MAX_LENGTH); i++ )
{
sum ^= array[i];
} if (i != MAX_LENGTH)
res = (Integer2Char((sum >> )) << ) | 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[])
{
case "$WIMWV" :
direction = float.Parse(strArray[]);
speed = float.Parse(strArray[]);
char[] chArray = strArray[].ToCharArray();
speedStatus = (chArray[] == 'A' ? true : false);
break; case "$WIMTA" :
airTemp = float.Parse(strArray[]);
break; case "$WIMTS" :
soilTemp = float.Parse(strArray[]);
break; case "$WIMHU" :
humidity = float.Parse(strArray[]);
break; case "$WIMMB" :
barometer = float.Parse(strArray[]);
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 == )
{
if (strArray[].Length == ) //长度正常
{
byte[] array = Encoding.Default.GetBytes(strArray[]);
int check = CheckSum(Encoding.Default.GetBytes(str));
if (check != )
{
if (check == ((array[] << ) | array[]))
{
res = true;
}
}
}
}
return res;
}
}
}