From: Allen J Mitchell Date: 2002-03-07T12:43:28+09:00 Subject: RE: Ruby Serial Ports under Windows This is a multi-part message in MIME format. ------=_NextPart_000_000D_01C1C568.EBA5B220 Content-Type: text/plain; charset="windows-1255" Content-Transfer-Encoding: 7bit David, I had the same need and have been using win32ole to access the mscomm serial com object which comes with visual basic. I am enclosing a copy of the code I wrote.... consider it an alpha, It's almost midnight here so I will send an example of it's use tomorrow. Please do not put into general circulation yet, I need to spend some more time on it. Hope it helps... Let me know. Ignore the PacketizeData class, I'm still working on that. :) -----Original Message----- From: David Wende [mailto:DWende@lynxpn.com] Sent: Wednesday, March 06, 2002 11:38 AM To: ruby-talk ML Subject: Ruby Serial Ports under Windows I've started using Ruby a week ago as my first OOP language - very impressed. I need to access the COM1 serial port under Windows. Can this be done? Thanks David Wende ------=_NextPart_000_000D_01C1C568.EBA5B220 Content-Type: application/octet-stream; name="MScomm.rb" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="MScomm.rb" require 'win32ole' require 'thread.rb' class MSComm include WIN32OLE::VARIANT # Handshake Constants CONST_comNone =3D 0 # No handshaking. CONST_comXonXoff =3D 1 # XOn/XOff handshaking. CONST_comRTS =3D 2 # Request-to-send/clear-to-send handshaking. CONST_comRTSXOnXOff =3D 3 # Both request-to-send and XOn/XOff = handshaking. # OnComm Constants CONST_comEvSend =3D 1 # Send event. CONST_comEvReceive =3D 2 # Receive event. CONST_comEvCTS =3D 3 # Change in clear-to-send line. CONST_comEvDSR =3D 4 # Change in data-set ready line. CONST_comEvCD =3D 5 # Change in carrier detect line. CONST_comEvRing =3D 6 # Ring detect. CONST_comEvEOF =3D 7 # End of file. # Error Constants CONST_comEventBreak =3D 1001 # Break signal received CONST_comEventFrame =3D 1004 # Framing error CONST_comEventOverrun =3D 1006 # Port overrun CONST_comEventRxOver =3D 1008 # Receive buffer overflow CONST_comEventRxParity =3D 1009 # Parity error CONST_comEventTxFull =3D 1010 # Transmit buffer full CONST_comEventDCB =3D 1011 # Unexpected error retrieving Device = Control Block (DCB) for the port # InputMode Constants CONST_comInputModeText =3D 0 # (Default) Data is retrieved through = the Input property as text. CONST_comInputModeBinary =3D 1 # Data is retrieved through the Input = property as binary data. @@clsid =3D "{648A5600-2C6E-101B-82B6-000000000014}" @@progid =3D "MSCOMMLib.MSComm" def initialize @dispatch =3D WIN32OLE.new @@progid @lastargs =3D nil @handlers =3D {} @we =3D nil @mutex =3D Mutex.new end def AboutBox() ret =3D @dispatch._invoke(-552, [],[]) @lastargs =3D WIN32OLE::ARGV ret end #---------------------------------------------------------- # onEvent allows you to specify individual Proc routines # for events as specified above. # ie. # onEvent ( [MSComm::CONST_comEvReceive], Proc.new { | e, mscomm | = print "Receive happened" } # onEvent ( [MSComm::CONST_comEvReceive,MSComm::CONST_comEvSend], = Proc.new { | e, mscomm | print "Receive or send happened" } # # Note: that the proc must take a single argument which # is the MSComm object itself. #---------------------------------------------------------- def onEvent ( eventList, handler =3D nil ) raise "Invalid Event Array" if !eventList.kind_of?( Array ) raise "Invalid Handler" if !( handler.nil? || handler.kind_of?( Proc ) = ) raise "Invalid Handler - should handle two arguments ( event, mscomm = )." if handler.kind_of?( Proc ) && handler.arity !=3D 2 @mutex.synchronize do eventList.each { |e| @handlers[e] =3D handler } end end def CommPort @dispatch.CommPort end def baud @dispatch.Settings.split(",")[0] end def parity @dispatch.Settings.split(",")[1] end def dataBits @dispatch.Settings.split(",")[2] end def stopBits @dispatch.Settings.split(",")[3] end def Handshaking @dispatch.Handshaking end def Handshaking=3D ( hsMode ) raise "Invalid handshaking value (#{hsMode})" if = [CONST_comNone,CONST_comXonXoff,CONST_comRTS,CONST_comRTSXOnXOff].index( = hsMode ) =3D=3D nil @dispatch.Handshaking =3D hsMode end def RThreshold @dispatch.RThreshold end def RThreshold=3D ( numOfChars ) @dispatch.RThreshold =3D numOfChars end def SThreshold @dispatch.SThreshold end def SThreshold=3D ( numOfChars ) @dispatch.SThreshold =3D numOfChars end def open ( settings ) close if self.PortOpen s =3D settings.split(":") s2 =3D s[1].split(",") # valid comm port? port =3D %w{ COM1 COM2 COM3 COM4 COM5 COM6 COM7 COM8 COM9 COM10 COM11 = COM12 COM13 COM14 COM15 COM16 }.index( s[0].upcase ) raise "Invalid COM Port (#{s[0].upcase})" if port.nil? raise "Invalid Baud (#{s2[0].upcase})" if %w{ 110 300 600 1200 2400 = 9600 14400 19200 28800 38400 56000 128000 256000 }.index( s2[0].upcase ) = =3D=3D nil raise "Invalid Parity (#{s2[1].upcase})" if %w{ E M N O S }.index( = s2[1].upcase ) =3D=3D nil raise "Invalid Databits (#{s2[2].upcase})" if %w{ 4 5 6 7 8 }.index( = s2[2].upcase ) =3D=3D nil raise "Invalid Stopbits (#{s2[3].upcase})" if %w{ 1 1.5 2 }.index( = s2[3].upcase ) =3D=3D nil begin @dispatch.CommPort =3D port+1 @dispatch.Settings =3D s[1] @dispatch.PortOpen =3D true @we =3D WIN32OLE_EVENT.new( @dispatch ) @we.on_event { |*args| self.onEventHandler( *args ) } true rescue false end end def close @dispatch.PortOpen =3D false @we =3D nil end def Break @dispatch.Break end def Break=3D ( onOff ) @dispatch.Break =3D onOff end def CommID @dispatch.CommID end def PortOpen @dispatch.PortOpen end def InBufferSize @dispatch.InBufferSize end def InBufferSize=3D ( size ) @dispatch.InBufferSize =3D size end def InBufferCount @dispatch.InBufferCount end def Input @dispatch.Input end def Name @dispatch.Name end def InputLen @dispatch.InputLen end def InputLen=3D ( size ) @dispatch.InputLen =3D size end def InputMode @dispatch.InputMode end def InputMode=3D ( mode ) raise "Invalid input mode (#{mode})" if = [CONST_comInputModeText,CONST_comInputModeBinary].index( mode ) =3D=3D = nil @dispatch.InputMode =3D mode end def setInputToText self.InputMode =3D CONST_comInputModeText end def setInputToBinary self.InputMode =3D CONST_comInputModeBinary end def isTextMode? self.InputMode =3D=3D CONST_comInputModeText end def isBinaryMode? self.InputMode =3D=3D CONST_comInputModeBinary end def Output=3D ( out ) @dispatch.Output =3D out end def OutBufferCount @dispatch.OutBufferCount end def OutBufferSize @dispatch.OutBufferSize end def OutBufferSize=3D ( size ) @dispatch.OutBufferSize =3D size end def CDHolding @dispatch.CDHolding end def CTSHolding @dispatch.CTSHolding end def DSRHolding @dispatch.DSRHolding end def DTREnable @dispatch.DTREnable end def DTREnable=3D ( onOff ) @dispatch.DTREnable =3D onOff end def EOFEnable @dispatch.EOFEnable end def EOFEnable=3D ( onOff ) @dispatch.EOFEnable =3D onOff end def RTSEnable @dispatch.RTSEnable end def RTSEnable=3D ( onOff ) @dispatch.RTSEnable =3D onOff end def onEventHandler ( *args ) raise "Invalid Event Notification #{args[0]}" if args[0] !=3D = "OnComm" handler =3D nil e =3D @dispatch.CommEvent @mutex.synchronize do handler =3D @handlers[e] handler =3D @handlers["*"] if handler.nil? end handler.call( e, self ) if !handler.nil? end end class PacketizeData def initialize ( beginPacket, endPacket, onCompletePacket, = onOutOfBoundsData ) @bp =3D beginPacket @ep =3D endPacket @onCP =3D onCompletePacket @onOBD =3D onOutOfBoundsData @data =3D "" end def clear @data =3D nil end def addData ( data ) @data +=3D data while ( @data.length ) if ( @bp.nil? ) then #simply search for the end of the packet p =3D @data.index( @ep ) if ( !p.nil? ) then @onCP.call( @data[0,p] ) @data =3D @data[p+@ep.length,-1] else return end else # could be a partial match end end end end ------=_NextPart_000_000D_01C1C568.EBA5B220--