micro_aeth-ae51-ruby 0.0.1
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.
- checksums.yaml +7 -0
- data/lib/micro_aeth-ae51.rb +317 -0
- data/lib/micro_aeth-ae51/version.rb +8 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d6c48744a043423d468f6efbe381db6ed005d668
|
4
|
+
data.tar.gz: 992247eaeb80568221f19142cc0a0b3f8448417d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a49064b2b7c4ef853ce5eb51ce359ae3f1ebc9895205045e577f47986c72d2bb019d5e042d7f30ad91da2b2d7957d4ed0e162ede8af6a06b274b386837562dfa
|
7
|
+
data.tar.gz: e90dc640e49fa5f117ffa001c100db896a39bcfc2e31a642fd73c5bc13ec3d75733703c5cf59a31f2788750f443540eff9f7e1cdccc54d9e034fbaf43c6b1bee
|
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'serialport'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module MicroAethAE51
|
5
|
+
class ::String
|
6
|
+
###
|
7
|
+
# @return the first charater in the string as an integer
|
8
|
+
def byte
|
9
|
+
self.bytes[0]
|
10
|
+
end
|
11
|
+
|
12
|
+
###
|
13
|
+
# XOR two strings
|
14
|
+
# @str assumed to be a one byte string or integer
|
15
|
+
def ^ str
|
16
|
+
if str.class == String
|
17
|
+
str = str.byte
|
18
|
+
elsif str.class == Fixnum
|
19
|
+
nil
|
20
|
+
else
|
21
|
+
raise "invalid arg: #{str.class} \n Must be String or Fixnum"
|
22
|
+
end
|
23
|
+
self.bytes.each do |i|
|
24
|
+
str = str ^ i
|
25
|
+
end
|
26
|
+
str.chr.force_encoding( "ASCII-8BIT")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Instruction
|
31
|
+
###
|
32
|
+
# Message constants
|
33
|
+
STX = "\x02" # Start of each message
|
34
|
+
ETX = "\x03" # End of each message
|
35
|
+
M = "AE5X:" # What each message starts with
|
36
|
+
EraseFlash = "E" # Reply:ACK, after flash is erased anotherACK issent.(erasing should take 30 – 45 seconds)
|
37
|
+
StopWrite = "S" # stopswriting to flash
|
38
|
+
StartWrite = "W" # Startswriting to flash
|
39
|
+
Kill = "K" # Kill Shuts down microAeth
|
40
|
+
end
|
41
|
+
|
42
|
+
###
|
43
|
+
# Creates and parses messages to and from the MicroAeth.
|
44
|
+
# See MicroAeth::Com for specifications on the
|
45
|
+
# transmiting of messages.
|
46
|
+
class Message
|
47
|
+
attr :original_char_string,
|
48
|
+
:ref,
|
49
|
+
:sen1,
|
50
|
+
:sen2,
|
51
|
+
:flow,
|
52
|
+
:pcb_temp,
|
53
|
+
:time,
|
54
|
+
:status,
|
55
|
+
:battery
|
56
|
+
|
57
|
+
###
|
58
|
+
# @param data [String] conents between the `STX` "\x02"
|
59
|
+
# and the `ETX` "0x03"
|
60
|
+
def initialize data
|
61
|
+
raise "invalid data" unless data_valid? data
|
62
|
+
@original_char_string = data
|
63
|
+
parse_data( data[7..-1])
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def data_valid? d
|
68
|
+
crc = d[-1]
|
69
|
+
data = d[1..-2]
|
70
|
+
len = d[0]
|
71
|
+
(data ^ len).byte == crc.byte
|
72
|
+
end
|
73
|
+
def parse_data d
|
74
|
+
b = d.bytes
|
75
|
+
@ref = d[0..2].unpack("v")[0]
|
76
|
+
@sen1 = d[3..5].unpack("v")[0]
|
77
|
+
@sen2 = d[6..8].unpack("v")[0]
|
78
|
+
@flow = d[9..10].unpack("v")[0]
|
79
|
+
@pcb_temp = b[11]
|
80
|
+
@time = Time.new ('20' + b[12].to_s).to_i,
|
81
|
+
b[13],
|
82
|
+
b[14],
|
83
|
+
b[15],
|
84
|
+
b[16],
|
85
|
+
b[17]
|
86
|
+
@status = b[18]
|
87
|
+
@battery = d[19..20].unpack("v")[0]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
##
|
93
|
+
# Initializes a link between the the system and the device.
|
94
|
+
# used to transmit and recieive MicroAeth::Message
|
95
|
+
#
|
96
|
+
class Com
|
97
|
+
attr_accessor :com, :messages
|
98
|
+
attr_reader :com_thread
|
99
|
+
|
100
|
+
def initialize
|
101
|
+
port = '/dev/serial/by-id/usb-AethLabs_microAeth_Model_AE51_AE51-S4-649-1303-if00-port0'
|
102
|
+
baud = 500_000
|
103
|
+
bytesize = 8
|
104
|
+
stopbits = 1
|
105
|
+
parity = SerialPort::MARK
|
106
|
+
@com = SerialPort.new port, baud, bytesize, stopbits, parity
|
107
|
+
@messages = []
|
108
|
+
end
|
109
|
+
|
110
|
+
###
|
111
|
+
# @m The message to be written
|
112
|
+
def write instruction
|
113
|
+
data = MicroAeth::Instruction::M + instruction
|
114
|
+
len = data.length.chr
|
115
|
+
crc = data ^ len
|
116
|
+
@com.write (MicroAeth::Instruction::STX + len + data + crc + MicroAeth::Instruction::ETX).force_encoding("ASCII-8BIT")
|
117
|
+
end
|
118
|
+
|
119
|
+
def erase_flash
|
120
|
+
begin
|
121
|
+
clear_buffer
|
122
|
+
write MicroAeth::Instruction::EraseFlash
|
123
|
+
Timeout::timeout(60) { wait_for_acknowledge }
|
124
|
+
sleep 45
|
125
|
+
Timeout::timeout(60) { wait_for_acknowledge }
|
126
|
+
write MicroAeth::Instruction::StartWrite
|
127
|
+
Timeout::timeout(60) { wait_for_acknowledge }
|
128
|
+
rescue Timeout::Error
|
129
|
+
retry
|
130
|
+
end
|
131
|
+
end
|
132
|
+
def clear_buffer
|
133
|
+
begin
|
134
|
+
while true
|
135
|
+
Timeout::timeout(0.5) { read_message }
|
136
|
+
end
|
137
|
+
rescue Timeout::Error
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def wait_for_acknowledge
|
143
|
+
while read_message != "\u0006AE5X:A\u0014".force_encoding("ASCII-8BIT")
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def start
|
149
|
+
puts "The MicroAeth is starting!.."
|
150
|
+
begin
|
151
|
+
clear_buffer
|
152
|
+
rescue [EOFError, Timeout::Error]
|
153
|
+
raise "Problem stating the MicroAeth"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# @return A ruby thread which continually reads
|
159
|
+
# from the MicroAeth::Com#com instance
|
160
|
+
def read
|
161
|
+
@com_thread = Thread.new do
|
162
|
+
begin
|
163
|
+
while true
|
164
|
+
@messages << ( Message.new read_message )
|
165
|
+
end
|
166
|
+
# Intermitently, it the serialport library raises end of file...
|
167
|
+
rescue EOFError
|
168
|
+
sleep 1
|
169
|
+
retry
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
###
|
175
|
+
# @file a ruby file object
|
176
|
+
def start_write_to_file file_name
|
177
|
+
@stop_writing_to_file = false
|
178
|
+
@thread = Thread.new do
|
179
|
+
clear_buffer
|
180
|
+
m_prev = nil
|
181
|
+
while @stop_writing_to_file != true
|
182
|
+
begin
|
183
|
+
m = Message.new read_message
|
184
|
+
rescue RuntimeError
|
185
|
+
retry
|
186
|
+
end
|
187
|
+
erase_flash if m.status == 64
|
188
|
+
atn = Math.log( m.ref.to_f / m.sen1.to_f) * 100
|
189
|
+
file = File.new file_name, 'a'
|
190
|
+
print message = [Time.now, m.ref, m.sen1, atn, m.flow, m.pcb_temp, m.status, m.battery, sigma_ap( m, m_prev)].join(',') + "\n"
|
191
|
+
file << message
|
192
|
+
file.close
|
193
|
+
m_prev = m
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
def stop_write_to_file
|
198
|
+
@stop_writing_to_file = true
|
199
|
+
@thread.join 30
|
200
|
+
end
|
201
|
+
|
202
|
+
def sigma_ap m, m_prev
|
203
|
+
if m_prev.nil?
|
204
|
+
"NaN"
|
205
|
+
else
|
206
|
+
Math::PI * (0.3 ** 2) / 4.0 / m.flow.to_f * 60.0 *
|
207
|
+
Math.log( m_prev.sen1.to_f / m.sen1.to_f * m.ref.to_f / m_prev.ref.to_f) * (10.0 ** 8)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
def read_message
|
211
|
+
m, c = '',''
|
212
|
+
while c != "\x02"; c = @com.readchar; end
|
213
|
+
c = @com.readchar
|
214
|
+
m << c
|
215
|
+
len = c.byte
|
216
|
+
0.upto len do |i|
|
217
|
+
c = @com.readchar
|
218
|
+
m << c
|
219
|
+
end
|
220
|
+
while c != "\x03"
|
221
|
+
c = @com.readchar
|
222
|
+
m << c
|
223
|
+
end
|
224
|
+
m[0..-2]
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
=begin
|
230
|
+
You'll need to assemble a properly formed message, or it won't respond.
|
231
|
+
## Properly forming a messages as suggested by Karl Walter
|
232
|
+
|
233
|
+
Communication protocol is based on folowing syntax:
|
234
|
+
`STX LEN DATA CRC ETX` where:
|
235
|
+
|
236
|
+
* `STX` is one byte 0x02 (HEX values)
|
237
|
+
* `LEN` is one byte lenght of `DATA`
|
238
|
+
* `CRC` is XOR function between `LEN` byte and `DATA` bytes
|
239
|
+
* I'm assuming this is the last byte of data
|
240
|
+
* `ETX` is one byte 0x03
|
241
|
+
|
242
|
+
Every string of `DATA` that microAethCOM PC
|
243
|
+
software sends starts with `AE5X:` followed by one letter.
|
244
|
+
|
245
|
+
So you need to write some code to take the data you
|
246
|
+
want to send it, add the `STX` (`0x02`), calculate the
|
247
|
+
`LEN` and add it, Calculate the `CRC`, add that, then finally
|
248
|
+
add the `ETX` (`0x03`).
|
249
|
+
|
250
|
+
The `CRC` is always the hardest to get to work.
|
251
|
+
`CRC` is `XOR` function between `LEN` byte and `DATA` bytes.
|
252
|
+
http://en.wikipedia.org/wiki/Bitwise_operation#XOR
|
253
|
+
You'll probably have to make a best guess as how to `XOR` the `DATA` and `LEN`,
|
254
|
+
then try it on the messages that the MicroAeth sends, and see if you get the
|
255
|
+
`CRC` that it produced.
|
256
|
+
|
257
|
+
The `LEN` in the messages from it seemed like they where one longer than the
|
258
|
+
number of data bytes, so you'll have to experiment with that.
|
259
|
+
|
260
|
+
Also the `LEN` is only 1 byte, and the `DATA` can be any number, so I think you
|
261
|
+
are suppose to `XOR` the `LEN` with the first `DATA` byte, then take that and
|
262
|
+
XOR with the second, and so on, sort of like Xmodem. I'm not sure which is the
|
263
|
+
first `DATA` byte, though.
|
264
|
+
|
265
|
+
This may help: http://crcmod.sourceforge.net/crcmod.html
|
266
|
+
|
267
|
+
Also just google for `XOR CRC`, finds some good stuff like
|
268
|
+
[this](http://stackoverflow.com/questions/344961/how-do-you-compute-the-xor-remainder-used-in-crc)
|
269
|
+
|
270
|
+
You should also make sure that when you send /x02 that it sends 00000010 not
|
271
|
+
|
272
|
+
01011100 01111000 00110000 00110010.
|
273
|
+
|
274
|
+
# from http://aethlabs.com/sites/all/content/microaeth/microAeth%20Model%20AE51%20Operating%20Manual.pdf
|
275
|
+
- Data(index)
|
276
|
+
- Data(index+1)
|
277
|
+
- Data(index+2)
|
278
|
+
- ‘Sen1
|
279
|
+
- Data(index+3)
|
280
|
+
- Data(index+4)
|
281
|
+
- Data(index+5)
|
282
|
+
- ‘Sen2
|
283
|
+
- Data(index+6)
|
284
|
+
- Data(index+7)
|
285
|
+
- Data(index+8)
|
286
|
+
- ‘Flow
|
287
|
+
- Data(index+9)
|
288
|
+
- Data(index+10)
|
289
|
+
- ‘PCBTemp
|
290
|
+
- Data(index+11)
|
291
|
+
- ‘Date
|
292
|
+
- Data(index+12)
|
293
|
+
- Data(index+13)
|
294
|
+
- Data(index+14)
|
295
|
+
- ‘Time
|
296
|
+
- Data(index+15)
|
297
|
+
- Data(index+16)
|
298
|
+
- Data(index+17)
|
299
|
+
- ‘Status
|
300
|
+
- Data(index+18)
|
301
|
+
- ‘Battery
|
302
|
+
- Data(index+19)
|
303
|
+
- Data(index+20)
|
304
|
+
- ‘Reserved forGPS etc...
|
305
|
+
- Data(index+21)
|
306
|
+
- Data(index+22)
|
307
|
+
- Data(index+23)
|
308
|
+
- Data(index+24)
|
309
|
+
- Data(index+25)
|
310
|
+
- Data(index+26)
|
311
|
+
- Data(index+27)
|
312
|
+
- Data(index+28)
|
313
|
+
- Data(index+29)
|
314
|
+
- Data(index+30)
|
315
|
+
|
316
|
+
Acknowledge: "\u0006AE5X:A\u0014"
|
317
|
+
=end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: micro_aeth-ae51-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kurt R. Rudolph
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: serialport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.8'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.8'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rdoc
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.2'
|
55
|
+
description: This Gem provides and easy to use API for sending and receiving data
|
56
|
+
and commands for the microAeth model AE51. This library is not complete but encompasses
|
57
|
+
the most common commands for interfacing with the device.
|
58
|
+
email:
|
59
|
+
- kurt@rudycomputing.io
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- lib/micro_aeth-ae51/version.rb
|
65
|
+
- lib/micro_aeth-ae51.rb
|
66
|
+
homepage: https://github.com/RudyComputing/microAeth-AE51-Ruby
|
67
|
+
licenses:
|
68
|
+
- MIT
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.0.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements:
|
85
|
+
- a microAeth model AE51
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 2.0.3
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: Ruby API for AethLabs microAeth® model AE51
|
91
|
+
test_files: []
|
92
|
+
has_rdoc:
|