rs232 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+