使用GSM调制解调器通过AT命令发送Unicode消息(如波斯语和阿拉伯语)的C#实现

9

我正在使用C#.Net和AT指令为GSM调制解调器(D-Link DWM-156)开发应用程序。 我遇到了一个发送Unicode消息的问题(例如用波斯语或阿拉伯语编写的消息)。 这是我的程序核心:

SerialPort GSMPort = new SerialPort();

GSMPort.PortName = "COM6";
GSMPort.BaudRate = 9600;
GSMPort.Parity = Parity.None;
GSMPort.DataBits = 8;
GSMPort.StopBits = StopBits.One;
GSMPort.Handshake = HandShake.RequestToSend;
GSMPort.DtrEnable = true;
GSMPort.RtsEnable = true;

GSMPort.Open();

GSMPort.Write("AT\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGF=1\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGS=\"" + destinationNumber + "\"\r\n");
Thread.Sleep(1000);
GSMPort.Write(shortMessage+ "\x1A");

对于英语和ASCII字母,它可以很好地工作。我已经阅读了这篇文章,并且可以使用AT命令在Hyperterminal中发送Unicode消息:

AT [Enter]
OK
AT+CSCS="UCS2" or AT+CSCS="HEX" [Enter] ---> We have to convert our message to hex 
OK
AT+CMGF=1 [Enter]
OK
AT+CMGS="destinationNumber" [Enter]
> 0633064406270645002006450631062D06280627 ---> The hex format of our message (سلام مرحبا)
+CMGS: 139
OK

由于这些命令已经正常工作,我在我的应用程序中添加了GSMPort.Write("AT+CSCS=\"UCS2\"\r");并尝试发送0645 0631 062D 0628 0627(مرحبا)或0633 0644 0627 0645(سلام)。

GSMPort.Write("AT\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CSCS=\"UCS2\"\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGF=1\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGS=\"" + destinationNumber + "\"\r\n");
Thread.Sleep(1000);
GSMPort.Write("0633064406270645" + "\x1A");

与Hyperterminal中的AT命令不同的是,目标设备无法接收到正确的字母。在C#代码和Hyperterminal代码中,这些AT命令有何区别?如果您能帮助我解决这个问题,我将不胜感激。


附加信息: 正如您所知,我通过Hyperterminal发送Unicode消息(如波斯字母)并使用AT命令没有任何问题。在下面的代码中,我尝试发送“سلام”,“0633064406270645”是其十六进制形式。但是我在手机上收到的是□□□□。

GSMPort.Encoding = UnicodeEncoding.GetEncoding(1256);
GSMPort.Write("AT\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CSCS=\"UCS2\"\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGF=1\r");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGS=\"" + destinationNumber + "\"\r\n");
Thread.Sleep(1000);
GSMPort.Write("0633064406270645" + "\x1A");

在超级终端环境中,另一方面,一切都很顺利,我收到了“سلام”。
AT [Enter]
OK
AT+CSCS="UCS2"
OK
AT+CMGF=1 [Enter]
OK
AT+CMGS="destinationNumber" [Enter]
> 0633064406270645 [Ctrl+Z]
+CMGS: 139
OK

我注意到在超级终端的属性中,如果我勾选“发送行以换行符结尾”(它在属性->设置-> ASCII设置中,请参见下图),我将遇到与C#代码中相同的问题。 我认为“发送行以换行符结尾”在GSMPort.Write中默认启用? 如果是,则如何禁用它?

1
在您的AT+CMGS命令中,您使用了writeline以及\r\n。但是在AT脚本中,我只看到了一个\r。 - rene
是的,WriteLine在消息中会产生额外的行([Enter])。 - Yasser Mohseni
1
是的,它支持Unicode。我在我的问题中已经提到,我可以在Hyperterminal环境中使用AT命令发送Unicode消息。此外,**AT+CSCS=?的结果是+CSCS: ("IRA", "GSM", "HEX", "PCCP437", "8859-1", "UCS2", "UCS2_0X81")**。这意味着该调制解调器支持Hex(“HEX”)和Unicode(“UCS2”)。请阅读本文 - Yasser Mohseni
2
在开始发送之前添加以下行,以便在调试窗口中查看您的调制解调器正在告诉您的内容:GSMPort.DataReceived += (s, e) => { Debug.WriteLine( GSMPort.ReadLine()); }; - rene
各位好,我已经在我的问题中添加了一些额外的信息,如果您也能检查一下它们,我将不胜感激。 - Yasser Mohseni
显示剩余3条评论
3个回答

10

我终于找到了解决这个问题的方法。就像我在问题的“附加信息”部分所说的那样,使用带有行尾换行符的发送行导致了C#中的SerialPort与用于发送Unicode消息的hyperterminal中的AT命令之间的不匹配。我只是用\n代替了\r的行尾换行符。修改后的代码如下所示:

GSMPort.Write("AT\n");
Thread.Sleep(1000);
GSMPort.Write("AT+CSCS=\"UCS2\"\n");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGF=1\n");
Thread.Sleep(1000);
GSMPort.Write("AT+CMGS=\"" + destinationNumber + "\"\n");
Thread.Sleep(1000);
GSMPort.Write("0633064406270645" + "\x1A"); 

不要改变 SerialPort.EncodingSerialPort.NewLine 属性的值。它们的默认值已经足够了,只需设置 AT+CSCS="UCS2" 以使用 Unicode 格式发送信息。


它帮了我,所以我标记为有用的,但我仍然有一个问题:您是否将所有消息都转换为HEX,包括ASCII或UNICODE?如果您能让我知道最终做了什么,我会非常感激。 - Masoud
@Masoud,我尝试使用上面的代码发送阿拉伯短信,但是我收到了“响应接收不完整”的错误。你能帮我吗? - Abdulsalam Elsharif

4

首先检查您的调制解调器是否支持unicode,然后将代码更改为以下内容:
我们必须为Unicode消息指定正确的DCS(数据编码方案),即0x08。

我们可以通过将AT + CSMP命令的第四个参数更改为“8”来设置此值:

AT + CSMP = 1,167,0,8

    GSMPort.Write("AT\r");
    Thread.Sleep(1000);
    GSMPort.Write("AT+CSCS=\"UCS2\"\r");
    Thread.Sleep(1000);
    GSMPort.Write("AT+CMGF=1\r");
    Thread.Sleep(1000);
    GSMPort.Write("AT+CSMP=1,167,0,8\r"); //AT+CSMP=1,167,0,8
    Thread.Sleep(1000);
    GSMPort.WriteLine("AT+CMGS=\"" + destinationNumber + "\"\r\n");
    Thread.Sleep(1000);
    GSMPort.WriteLine("0633064406270645" + "\x1A");

我可以在Hyperterminal环境中使用AT命令发送Unicode消息,所以我确定调制解调器支持Unicode。感谢您的解决方案,我尝试过GSMPort.Write("AT+CSMP=1,167,0,8\r");但是它没有改变任何东西。在Hyperterminal中使用AT+CSMP?这个结构是+AT+CSMP=1,71,0,0,但它也不起作用。我还尝试了AT+CSMP=1,71,0,8 - Yasser Mohseni
如果您使用超级终端命令发送短信,是否正常工作?如果是,那么您的命令队列是什么? - saeed
请查看我的问题。我描述了我在超级终端中使用的AT命令序列。如果您需要有关我的调制解调器及其状态的进一步信息,请告诉我。 - Yasser Mohseni
谢谢,对我来说,AT+CSMP=1,167,0,8AT+CSCS="UCS2"部分最重要,然后是正确的编码方式,即将消息发送为我所描述的AT HEX UCS2/UTF-16,即将字符串编码为UTF-16BE,然后将每个字节格式化为ASCII HEX字符,即U+1F408变成"D83D DC08",发送到调制解调器。 - drott

2

SerialPort 的默认编码方式为 Encoding.ASCII。要支持您使用的字符集,请将 SerialPort.Encoding 设置为支持该字符集的编码方式(如 Encoding.UTF32),或者使用 SerialPort.Write(char[], int, int) 并以任何您喜欢的方式将 Unicode 字符串转换为字节。


感谢您的回答。我已经将GSMPort.Encoding的编码更改为Encoding.UTF8,Encoding.Unicode和Encoding.UTF32。但是这些更改都没有成功解决问题:GSMPort.Write("AT+CSCS="UCS2"\r"); GSMPort.Encoding = Encoding.Unicode; - Yasser Mohseni
在调用 Write 之前,请确保先设置 Encoding - Peter Ritchie
是的,在所有的“写入”操作之前,我已经提前调用了GSMPort.Encoding,但仍然无法正常工作。 - Yasser Mohseni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接