rs232 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +40 -0
  2. data/lib/rs232.rb +144 -0
  3. data/rakefile.rb +11 -0
  4. data/test/test_rs232.rb +19 -0
  5. metadata +79 -0
data/README ADDED
@@ -0,0 +1,40 @@
1
+ ##
2
+ #
3
+ # RS232
4
+ #
5
+ # Ruby interface to Windows Serial Port API
6
+ #
7
+ # author hugo benichi
8
+ # email hugo.benichi@m4x.org
9
+ # copyright 2012 hugo benichi
10
+ # version 0.1.0
11
+ #
12
+ ##
13
+
14
+ installation:
15
+
16
+ run in the root directory
17
+ rake gem_install
18
+
19
+ it will compile the gem library and produce a .gem package for ruby
20
+ it will then install the .gem automatically
21
+
22
+ usage:
23
+
24
+ cf test/test_rs232.rb
25
+
26
+ first, require the gem with
27
+ require 'rs232'
28
+
29
+ then instance an object of RS232, passing an address, and optionally parameters to configure
30
+ serial_port = RS232.new 'COM1'
31
+
32
+ the default parameters are
33
+ baud rate: 9600
34
+ 8 bits
35
+ 1 bit stop bit
36
+ no parity bit
37
+ control flow hardware (RTS/CTS)
38
+ delimiters "\r\n"
39
+
40
+ you can directly use the RS232 instance with the 'write', 'read', and 'query' commands
@@ -0,0 +1,144 @@
1
+
2
+ class RS232
3
+
4
+ require 'ffi'
5
+
6
+ attr_accessor :report, :delimiter
7
+ attr_reader :count, :error
8
+
9
+ # serial port object constructor
10
+ # also sets the parameters and timeout properties through an hash argument
11
+ def initialize address, params = {}
12
+ mode = Win32::GENERIC_READ | Win32::GENERIC_WRITE
13
+ type = Win32::OPEN_EXISTING
14
+ attr = Win32::FILE_ATTRIBUTE_NORMAL
15
+ @serial = Win32::CreateFileA( address, mode, 0, nil, type, attr, nil)
16
+ @error = Win32.error_check
17
+ puts "RS232 >> got file handle 0x%.8x for com port %s" % [@serial, address]
18
+ DCB.new.tap do |p|
19
+ p[:dcblength] = DCB::Sizeof
20
+ Win32::GetCommState @serial, p
21
+ p[:baudrate] = params[:baudrate] || 9600
22
+ p[:bytesize] = params[:bytesize] || 8
23
+ p[:stopbits] = params[:stopbits] || DCB::ONESTOPBIT
24
+ p[:parity] = params[:parity] || DCB::NOPARITY
25
+ Win32::SetCommState @serial, p
26
+ @error = Win32.error_check
27
+ end
28
+ CommTimeouts.new.tap do |timeouts|
29
+ timeouts[:read_interval_timeout] = params[:read_interval_timeout] || 50
30
+ timeouts[:read_total_timeout_multiplier] = params[:read_total_timeout_multiplier] || 50
31
+ timeouts[:read_total_timeout_constant] = params[:read_total_timeout_constant] || 10
32
+ timeouts[:write_total_timeout_multiplier] = params[:write_total_timeout_multiplier] || 50
33
+ timeouts[:write_total_timeout_constant] = params[:write_total_timeout_constant] || 10
34
+ Win32::SetCommTimeouts @serial, timeouts
35
+ @error = Win32.error_check
36
+ end
37
+ grow_buffer 128
38
+ @count = FFI::MemoryPointer.new :uint, 1
39
+ @report = false
40
+ @delimiter = params[:delimiter] || "\r\n"
41
+ end
42
+
43
+ # writes a string to the Serial port and happens the delimiter characters stores in @delimiters
44
+ def write string
45
+ command = "%s%s" % [string.chomp, @delimiter]
46
+ grow_buffer command.length
47
+ @buffer.write_string command
48
+ Win32::WriteFile @serial, @buffer, command.length, @count, nil
49
+ @error = Win32.error_check
50
+ puts "write count %i" % @count.read_uint32 if @report
51
+ end
52
+
53
+ # read a string from the Serial port
54
+ def read
55
+ Win32::ReadFile @serial, @buffer, @buflen, @count, nil
56
+ @error = Win32.error_check
57
+ puts "read count %i" % @count.read_uint32 if @report
58
+ @buffer.read_string
59
+ end
60
+
61
+ # write and read helper method
62
+ def query string
63
+ write sring
64
+ read
65
+ end
66
+
67
+ # close the Com port
68
+ def stop
69
+ Win32::CloseHandle @serial
70
+ @error = Win32.error_check
71
+ end
72
+
73
+ # increase the buffer size for reading and writing
74
+ def grow_buffer size
75
+ @buffer = FFI::MemoryPointer.new :char, @buflen = size if @buffer.nil? || @buflen < size
76
+ end
77
+
78
+ # wraps the native Windows API functions for file IO and COMM port found in kernel32.dll
79
+ module Win32
80
+ extend FFI::Library
81
+ ffi_lib 'kernel32.dll'
82
+ [
83
+ [ :GetLastError, [], :uint32],
84
+ [ :CreateFileA, [:pointer, :uint32, :uint32, :pointer, :uint32, :uint32, :pointer], :pointer],
85
+ # CreateFile first argument is a const char*
86
+ # Windows can decide to read it as a C string (1 char = 1 byte) or a unicode string (1 char = 2 byte)
87
+ # the real dll functions are actually CreateFileA for the C strings and CreateFileW for unicode
88
+ # I strongly suggest to use CreateFileA since FFI will automatically write a C string from Ruby string
89
+ [ :CloseHandle, [:pointer], :int],
90
+ [ :ReadFile, [:pointer, :pointer, :uint32, :pointer, :pointer], :int32],
91
+ [ :WriteFile, [:pointer, :pointer, :uint32, :pointer, :pointer], :int32],
92
+ [ :GetCommState, [:pointer, :pointer], :int32],
93
+ [ :SetCommState, [:pointer, :pointer], :int32],
94
+ [ :GetCommTimeouts, [:pointer, :pointer], :int32],
95
+ [ :SetCommTimeouts, [:pointer, :pointer], :int32],
96
+ ].each{ |sig| attach_function *sig }
97
+ def self.error_code
98
+ err = self::GetLastError()
99
+ "error code: %i | 0x%.8x" % [err,err]
100
+ end
101
+ def self.error_check
102
+ self::GetLastError().tap{ |err| puts "error: %i | 0x%.8x" % [err,err] if err != 0 }
103
+ end
104
+ GENERIC_READ = 0x80000000 # consts from Windows seven sdk:
105
+ GENERIC_WRITE = 0x40000000 # extract with
106
+ OPEN_EXISTING = 3 # grep -i "generic_read" *.h
107
+ FILE_ATTRIBUTE_NORMAL = 0x00000080 # from the /Include directory
108
+ end
109
+
110
+ # this struct is used by windows to configure the COMM port
111
+ class DCB < FFI::Struct
112
+ layout :dcblength, :uint32,
113
+ :baudrate, :uint32,
114
+ :flags, :uint32, # the :flag uint32 is actually a bit fields compound with structure
115
+ :wreserved, :uint16, # uint32 fBinary :1;
116
+ :xonlim, :uint16, # uint32 fParity :1;
117
+ :xofflim, :uint16, # uint32 fParity :1;
118
+ :bytesize, :uint8, # uint32 fOutxCtsFlow :1;
119
+ :parity, :uint8, # uint32 fOutxDsrFlow :1;
120
+ :stopbits, :uint8, # uint32 fDtrControl :2;
121
+ :xonchar, :int8, # uint32 fDsrSensitivity :1;
122
+ :xoffchar, :int8, # uint32 fTXContinueOnXoff :1;
123
+ :errorchar, :int8, # uint32 fOutX :1;
124
+ :eofchar, :int8, # uint32 fInX :1;
125
+ :evtchar, :int8, # uint32 fErrorChar :1;
126
+ :wreserved1, :uint16 # uint32 fNull :1;
127
+ # uint32 fRtsControl :2;
128
+ # uint32 fAbortOnError :1;
129
+ # uint32 fDummy2 :17;
130
+ Sizeof = 28 # this is used to tell windows the size of its own struct, not sure why it is necessary (different Windows version)
131
+ ONESTOPBIT = 0
132
+ NOPARITY = 0
133
+ end
134
+
135
+ # this structure is used to set timeout properties of the opened COM ports
136
+ class CommTimeouts < FFI::Struct
137
+ layout :read_interval_timeout, :uint32,
138
+ :read_total_timeout_multiplier, :uint32,
139
+ :read_total_timeout_constant, :uint32,
140
+ :write_total_timeout_multiplier, :uint32,
141
+ :write_total_timeout_constant, :uint32
142
+ end
143
+
144
+ end
@@ -0,0 +1,11 @@
1
+ task :test_global do ruby "test/test_rs232.rb" end
2
+ task :test_local do ruby "-Ilib test/test_rs232.rb" end
3
+
4
+ task :gem_build do sh "gem build rs232.gemspec" end
5
+ task :gem_install => :gem_build do
6
+ gemfile = Dir.new("./").entries.select{ |f| f =~ /rs232-[\d]+\.[\d]+\.[\d]+.gem/ }.sort[-1]
7
+ puts "installing %s" % gemfile
8
+ #sh "gem install --local %s" % gemfile
9
+ end
10
+
11
+ task :default => :test_local
@@ -0,0 +1,19 @@
1
+ require 'rs232'
2
+
3
+ t = RS232.new 'COM1'
4
+
5
+ t.write '?:V'
6
+ puts t.read
7
+
8
+ t.write 'Q:'
9
+ puts t.read
10
+
11
+ t.write 'H:1-'
12
+
13
+ #$stdin.gets
14
+
15
+ #t.write 'M:1+P10000'
16
+ #t.write 'G:'
17
+
18
+ puts "end of program"
19
+ t.stop
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rs232
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Hugo Benichi
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-07-10 00:00:00 +09:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: ffi
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Allows to script access to the serial port on Windows. Simple read and write commands
34
+ email: hugo.benichi@m4x.org
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/rs232.rb
43
+ - test/test_rs232.rb
44
+ - rakefile.rb
45
+ - README
46
+ has_rdoc: true
47
+ homepage: http://github.com/hugobenichi/rs232
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.7
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Ruby interface to Windows Serial Port API
78
+ test_files: []
79
+