原文http://deli.xmu.me/?p=7,放在Wayhome mm给我提供的空间,现在已经无法访问。
手机芯片厂家常常会更新模组软件版本,在发给客户之前,一般先对AT进行一次测试。手机设计公司收到新的软件版本,烧入Flash, 也要对模组的AT进行一次测试,验证是否正常,比较与上个版本是否有差别。手工一个个验证,乏味,容易对工作失去兴趣。
从http://rubyinstaller.rubyforge.org 可以下载 Ruby One-Click,现在最新版本安装软件为ruby186-27_rc2.exe。安装过程中,把RubyGems也勾上。在windows上用ruby访问串口,可以用win32ole模块,也可以用ruby-serialport,后者是跨平台的。安装ruby-serialport前要做一些准备工作,确认有装Microsoft’s Visual C++ 或Borland’s C++编译器,这里以VC6.0为例。
(1)设置环境变量:
PATH=C:\Program Files\Microsoft Visual Studio\VC98\Bin
INCLUDE=C:\Program Files\Microsoft Visual Studio\VC98\Include
LIB=C:\Program Files\Microsoft Visual Studio\VC98\Lib
(2)安装ruby-serialport:
开始 -> 程序 -> Ruby-186-27 -> RubyGems -> RubyGems Package Manager,输入:
gem install ruby-serialport
如果提示找不到mspdb60.dll, 请到http://www.dll-files.com 找,把它放到
C:\Program Files\Microsoft Visual Studio\VC98\Bin
接下来,就可以写一个测试脚本了。
# filename: test.rb
require 'rubygems'
require 'serialport'
# 0 is mapped to "COM1" on Windows, and 5 is COM6, 115200 is baud rate
sp = SerialPort.new(5, 115200)
sp.write "AT\r\n"
sleep(0.2)
puts sp.read # hopefully "OK" ;-)
注意:如果没有sleep,可能会收不到”OK”响应。
如果你弄了老半天,ruby-serialport也没装得上,那就用用win32ole,美其名曰:Windows Automation。win32ole是Masaki Suketa编写的Ruby扩展,是标准ruby发行版本的一部分。
现在写一个mscomm.rb文件
#! /usr/bin/env ruby
#
# filename: mscomm.rb
# @date 2009.5.16
#
require 'win32ole'
class MSCOMM
def initialize(port)
@serial = WIN32OLE.new("MSCOMMLib.MSComm")
@serial.CommPort = port
@serial.Settings = "115200,N,8,1"
@serial.InputLen = 0
@serial.PortOpen = true
end
def write(str)
@serial.Output = str
end
def read
str = @serial.Input
str
end
def close
@serial.PortOpen = false
end
def serial
@serial
end
end
再写一个简单的测试用例test_comm.rb:
#! /usr/bin/env ruby
#
# filename: test_comm.rb
# @date 2009.5.16
#
require 'mscomm.rb
comm = MSCOMM.new(6)
comm.write("AT\r\n")
sleep(0.2)
puts comm.read
comm.close
和上面的test.rb用法一样,没什么神秘感,是不是很简单呢 🙂
再加个查询信号的AT,在test_comm.rb倒数第二行加上
comm.write(“AT+CSQ?\r\n”)
sleep(0.2)
puts comm.read
要是很多很多…想想,还是写个函数吧.
def exec_cmd(comm, cmd)
comm.write("#{cmd}\r\n")
sleep(0.2)
begin
result = comm.read
result = "Command not support\n" if result.include?("ERROR\n")
rescue
result = "Writing serial port error\n"
end
puts result
end
于是,test_comm.rb中间部分的可以可以写成:
exec_cmd(comm, “AT”)
exec_cmd(comm, “AT+CSQ?”)
可是每添加一个AT… 还是得写一串的exec_cmd(comm… 。Dave Thomas大师说过:”Don’t Repeat Yourself!” ,这是ruby的设计理念。假设所有的AT都放在另外一个文件呢?我们也可以一个个读取出来。不过现在考虑的是暂时放在同个文件,那就定义一个字符串数组:
atcmd = {
‘AT’,
‘AT+CSQ?’,
‘AT+CREG?’
}
也可以这样写:
Atcmd = %w{
AT
AT+CSQ?
AT+CREG?
}
第二种对于添加AT比较方便,但缺点是AT不能出现空格。那么test_comm.rb 现在又可以写为:
atcmd.each {|at| exec_cmd(comm, at) }
可是,有的AT需要花一些时间才有响应。之前都默认是0.2秒,好吧,重新定义:
def exec_cmd(comm, cmd, timeout = 0.2)
…
sleep(timeout)
…
end
于是,就可以这样使用
exec_cmd(comm, “AT+CDV=10000”, 3)
exec_cmd(comm, “AT+CLCC?”)
exec_cmd(comm, “AT+CHV”)
exec_cmd(comm, “AT+CPOF”, 2)
exec_cmd(comm, “AT+CPON”, 5)
像这样的一组AT,具有依赖顺序而每个AT的响应时间又不一样,我们只能根据不同的情况对AT做分类,写不同的测试脚本。
再回头看看 test_commm.rb,如果我们想把输入与输出都放在同一个Excel表格,那该如何写呢?原理一样,依旧用win32ole:
#! /usr/bin/env ruby
#
# filename: excel.rb
# @date 2009.5.16
#
require 'win32ole'
class Excel
def initialize(filename = nil)
@excel = WIN32OLE.new("excel.Application") # create Excel object
@excel.Visible = TRUE
if (filename == nil)
@workbook = @excel.Workbooks.Add() # create new file
else
@workbook = @excel.Workbooks.Open(filename) # open exist file
end
end
def setvalue(pos, data)
@excel.Range(pos).Value = data
end
def save
@excel.Save()
end
def close
@excel.Quit()
end
def excelobj
@excel
end
end # end of class
我们重新写个test_cdma_at.rb
#! /usr/bin/env ruby
#
# test_cdma_at.rb
# @date 2009.5.16
#
require 'mscomm.rb'
require 'excel.rb'
def exec_cmd(comm, cmd)
comm.write("#{cmd}\r\n")
sleep(0.2)
begin
result = comm.read
result = "Command not support\n" if result.include?("ERROR\n")
rescue
result = "Writing serial port error\n"
end
return result
end
atcmd = %w{
AT
AT+CPIN?
AT+CPINC?
AT+CSQ?
AT+CREG=2
AT+CREG?
AT+VMCC?;+VMNC?
AT+CPBS=?
AT+CPBS?
AT+CPBS="ME"
AT+CPBS?
AT+CPBW=3,13544049382,"violet",0
AT+CPBR=3
AT+CMGS=13544049382,"Hello!"
AT+CMGW=,13544049382,"Hi!"
}
comm = MSCOMM.new(6)
excel = Excel.new("d:\\Book1.xls")
i = 1
atcmd.each {|at|
excel.setvalue("a#{i}", at)
excel.excelobj.Range("b#{i}").Value = exec_cmd(comm, at)
i += 1
}
comm.close
excel.save
excel.close
代码不难理解,就是把AT输入放在Excel表格某行的A列,响应写入某行的B列,仅如此而已。
脚本测试也不是万能的,AT本身具有随意性,写脚本意在减轻编码工作量,避免重复机械的劳动。