ekm-omnimeter 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81c9725bd9f827fc67d3b86adf622ba2cbd130a3
4
- data.tar.gz: 07f927b1852af3a02f5c5df84fcc78aa2fb791e6
3
+ metadata.gz: 8683533fedec1693dfa676899772275fe0ae881a
4
+ data.tar.gz: 0e2a8333af1cdedd8e41a4e8f5313f49124cd104
5
5
  SHA512:
6
- metadata.gz: cf5880321ac3ba5961df65c25ef49ad9339224de1a451ee34712e3e2cc55e00ede182a70253b183075da0199a1b903140066abf29b8f000beb0f15d3822a584d
7
- data.tar.gz: abd636ab283d8680a7256b3523f033be1370d70fd6d3f45c420bfaad0235335ee07c47de80cd4797bd67b97f80c4729d2fca8be3688ed623dd0be37b3eb638db
6
+ metadata.gz: 732818ce1410339b035a8c070bc8aab692aec256b29c9ecb7d0f4f46e92b6689097a26147706585767d5e0d72296f52db39cb736ce412a0ac3f6cdcb313b3843
7
+ data.tar.gz: ff897d4aa8a55847833cb7c6775f0947347ca0e48b30e04580139174fd570d95c638f66fe8088667bed2d6d2402c5d830cc86a9ab22407c60c267dd9c20dc8de
@@ -1,26 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ekm-omnimeter (0.2.2)
4
+ ekm-omnimeter (0.2.4)
5
5
  log4r (~> 1.1)
6
- xively-rb-connector (~> 0.1)
7
6
 
8
7
  GEM
9
8
  remote: https://rubygems.org/
10
9
  specs:
11
- bigdecimal (1.2.5)
12
10
  diff-lcs (1.2.5)
13
- httparty (0.13.0)
14
- json (~> 1.8)
15
- multi_xml (>= 0.5.2)
16
- json (1.8.1)
17
11
  log4r (1.1.10)
18
- mini_portile (0.5.3)
19
- multi_json (1.9.2)
20
- multi_xml (0.5.5)
21
- nokogiri (1.6.1)
22
- mini_portile (~> 0.5.0)
23
- ox (2.1.1)
24
12
  rake (10.2.2)
25
13
  rspec (2.14.1)
26
14
  rspec-core (~> 2.14.0)
@@ -30,18 +18,6 @@ GEM
30
18
  rspec-expectations (2.14.5)
31
19
  diff-lcs (>= 1.1.3, < 2.0)
32
20
  rspec-mocks (2.14.6)
33
- xively-rb (0.2.10)
34
- httparty (>= 0.10.0)
35
- multi_json (>= 1.3.6)
36
- multi_xml (>= 0.5.2)
37
- nokogiri (>= 1.5.6)
38
- ox (>= 1.5.9)
39
- yajl-ruby (>= 1.1.0)
40
- xively-rb-connector (0.1.3)
41
- bigdecimal (~> 1.2)
42
- log4r (~> 1.1)
43
- xively-rb (~> 0.2)
44
- yajl-ruby (1.2.0)
45
21
 
46
22
  PLATFORMS
47
23
  ruby
data/README.md CHANGED
@@ -49,16 +49,16 @@ m = EkmOmnimeter::Meter.new(
49
49
  :remote_port => 50000) # The port on which your iSerial device is listening
50
50
 
51
51
  # These values change based on the value of :power_configuration in the constructor
52
+ m.meter_number # 000300000234
53
+ m.remote_address # 192.168.0.125
54
+ m.remote_port # 50000
52
55
  m.volts # 248.7
53
56
  m.amps # 21.6
54
57
  m.watts # 2664
55
58
 
56
- # Warning: Dates seem off
57
- # My computer clock said 2014-04-03 3:32PM when these outputs came out
58
- m.meter_timestamp # 2014-04-03 05:15:32
59
- m.computer_timestamp # Time.now() = 2014-04-03 15:32:10 -0400
60
-
61
59
  # Read meter values
60
+ m.meter_timestamp # 2014-04-04 12:48:06
61
+ m.last_read_timestamp # 2014-04-04 12:48:17 -0400
62
62
  m.remote_address # 192.168.1.125
63
63
  m.remote_port # 50000
64
64
  m.meter_number # 000300000001
@@ -85,9 +85,9 @@ m.watts_l1 # 2276
85
85
  m.watts_l2 # 384
86
86
  m.watts_l3 # 0
87
87
  m.watts_total # 2664
88
- m.power_factor_1 # 1.0
89
- m.power_factor_2 # 0.0
90
- m.power_factor_3 # 0.0
88
+ m.power_factor_1 # 1.0
89
+ m.power_factor_2 # C0.99
90
+ m.power_factor_3 # C0.0
91
91
  m.maximum_demand # 22640.0
92
92
  m.maximum_demand_period # 15 (in minutes, can be 15, 30 or 60)
93
93
  m.ct_ratio # 400
@@ -25,7 +25,9 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  # Runtime dependencies
27
27
  spec.add_runtime_dependency "log4r", "~> 1.1"
28
- spec.add_runtime_dependency "xively-rb-connector", "~> 0.1"
29
28
 
29
+ #spec.add_runtime_dependency "xively-rb-connector", "~> 0.1"
30
+ #spec.add_runtime_dependency "trollop"
31
+ #spec.add_runtime_dependency "daemons"
30
32
 
31
33
  end
@@ -5,4 +5,5 @@ end
5
5
 
6
6
  require "ekm-omnimeter/version"
7
7
  require "ekm-omnimeter/logging"
8
+ require "ekm-omnimeter/crc16"
8
9
  require "ekm-omnimeter/meter"
@@ -0,0 +1,54 @@
1
+ module Crc16
2
+
3
+ # Returns boolean true or false if checksum of string s matches expected value
4
+ def self.check_crc16(s, expecting)
5
+ (Crc16.crc16(s) == expecting)
6
+ end
7
+
8
+ # Calculates CRC16 checksum of string buf
9
+ def self.crc16(buf)
10
+ crc = 0x00
11
+ buf.each_byte do |b|
12
+ crc = ((crc >> 8) & 0xff) ^ CRC_LOOKUP[(crc ^ b) & 0xff]
13
+ end
14
+ crc
15
+ end
16
+
17
+ private
18
+
19
+ CRC_LOOKUP = [
20
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
21
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
22
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
23
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
24
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
25
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
26
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
27
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
28
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
29
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
30
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
31
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
32
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
33
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
34
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
35
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
36
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
37
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
38
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
39
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
40
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
41
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
42
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
43
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
44
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
45
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
46
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
47
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
48
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
49
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
50
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
51
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
52
+ ]
53
+
54
+ end
@@ -13,7 +13,7 @@ module EkmOmnimeter
13
13
  VALID_POWER_CONFIGURATIONS = [:single_phase_2wire, :single_phase_3wire, :three_phase_3wire, :three_phase_4wire]
14
14
 
15
15
  # Initialization attributes
16
- attr_reader :meter_number, :remote_address, :remote_port, :power_configuration, :last_read_timestamp
16
+ attr_reader :meter_number, :remote_address, :remote_port, :verify_checksums, :power_configuration, :last_read_timestamp
17
17
 
18
18
  # Request A
19
19
  #attr_reader :meter_type, :meter_firmware, :address, :total_active_kwh, :total_kvarh, :total_rev_kwh, :three_phase_kwh, :three_phase_rev_kwh, :resettable_kwh, :resettable_reverse_kwh, :volts_l1, :volts_l2, :volts_l3, :amps_l1, :amps_l2, :amps_l3, :watts_l1, :watts_l2, :watts_l3, :watts_total, :cosϴ_l1, :cosϴ_l2, :cosϴ_l3, :var_l1, :var_l2, :var_l3, :var_total, :freq, :pulse_count_1, :pulse_count_2, :pulse_count_3, :pulse_input_hilo, :direction_of_current, :outputs_onoff, :kwh_data_decimal_places,
@@ -21,6 +21,9 @@ module EkmOmnimeter
21
21
  # Request B
22
22
  #attr_reader :t1_t2_t3_t4_kwh, :t1_t2_t3_t4_rev_kwh, :maximum_demand, :maximum_demand_time, :pulse_ratio_1, :pulse_ratio_2, :pulse_ratio_3, :ct_ratio, :auto_reset_md, :settable_imp_per_kWh_constant
23
23
 
24
+ # iSerial v4 Spec From http://documents.ekmmetering.com/Omnimeter-Pulse-v.4-Protocol.pdf
25
+ # %w(01 52 31 02 30 30 31 31 28 29 03 13 16).map{|a| a.to_i(16).chr}.join
26
+
24
27
  # Mix in the ability to log
25
28
  include Logging
26
29
 
@@ -32,16 +35,19 @@ module EkmOmnimeter
32
35
  @logger.info "Initializing Meter"
33
36
 
34
37
  # Prepend the meter number with the correct amount of leading zeros
35
- @meter_number = options[:meter_number].to_s.rjust(12, '0')
36
- @remote_address = options[:remote_address] || '192.168.0.125'
37
- @remote_port = options[:remote_port] || 50000
38
+ @meter_number = options[:meter_number].to_s.rjust(12, '0')
39
+ @remote_address = options[:remote_address] || '192.168.0.125'
40
+ @remote_port = options[:remote_port] || 50000
41
+ @verify_checksums = options[:verify_checksums] || false
38
42
  @logger.debug "meter_number: #{meter_number}"
39
43
  @logger.debug "remote_address: #{remote_address}"
40
44
  @logger.debug "remote_port: #{remote_port}"
45
+ @logger.debug "verify_checksums: #{verify_checksums}"
41
46
 
42
47
  # Collect the power configurations
43
48
  if VALID_POWER_CONFIGURATIONS.index(options[:power_configuration])
44
49
  @power_configuration = options[:power_configuration]
50
+ @logger.debug "power_configuration: #{@power_configuration}"
45
51
  else
46
52
  raise EkmOmnimeterError, "Invalid power configuration #{options[:power_configuration]}. Valid values are #{VALID_POWER_CONFIGURATIONS.join(', ')}"
47
53
  end
@@ -68,29 +74,21 @@ module EkmOmnimeter
68
74
  @values
69
75
  end
70
76
 
71
- # Formatted datetime reported by meter during last read
72
- def meter_timestamp
73
- "20#{current_time[0,2]}-#{current_time[2,2]}-#{current_time[4,2]} #{current_time[6,2]}:#{current_time[ 8,2]}:#{current_time[10,2]}"
74
- end
75
77
 
76
78
  # Attribute handler that delegates attribute reads to the values hash
77
79
  def method_missing(method_sym, *arguments, &block)
78
-
79
- #@logger.debug "method_missing #{method_sym.inspect}"
80
-
81
80
  # Only refresh data if its more than 0.25 seconds old
82
81
  et = @last_read_timestamp.nil? ? 0 : (Time.now - @last_read_timestamp)
83
- #logger.debug "Elapsed time since last read #{et}"
82
+ @logger.debug "Elapsed time since last read #{et}"
84
83
  if et > 250
85
- @logger.info "More than 250 milliseconds have passed, updating data"
84
+ @logger.info "More than 250 milliseconds have passed since last read. Triggering refresh."
86
85
  read()
87
86
  end
88
87
 
89
88
  if @values.include? method_sym
90
- #logger.debug "Found #{method_sym}"
91
89
  @values[method_sym]
92
90
  else
93
- #logger.debug "Didn't find #{method_sym}"
91
+ @logger.debug "method_missing failed to find #{method_sym} in the Meter.values cache"
94
92
  super
95
93
  end
96
94
  end
@@ -105,59 +103,6 @@ module EkmOmnimeter
105
103
  end
106
104
 
107
105
 
108
- ## Request A
109
- #d[:meter_type] # 2 Byte Meter Type
110
- #d[:meter_firmware] # 1 Byte Meter Firmware
111
- #d[:address] # 12 Bytes Address
112
- #d[:total_active_kwh] # 8 Bytes total Active kWh
113
- #d[:total_kvarh] # 8 Bytes Total kVARh
114
- #d[:total_rev_kwh] # 8 Bytes Total Rev.kWh
115
- #d[:three_phase_kwh] # 24 Bytes 3 phase kWh
116
- #d[:three_phase_rev_kwh] # 24 Bytes 3 phase Rev.kWh
117
- #d[:resettable_kwh] # 8 Bytes Resettable kWh
118
- #d[:resettable_reverse_kwh] # 8 bytes Resettable Reverse kWh
119
- #d[:volts_l1] # 4 Bytes Volts L1
120
- #d[:volts_l2] # 4 Bytes Volts L2
121
- #d[:volts_l3] # 4 Bytes Volts L3
122
- #d[:amps_l1] # 5 Bytes Amps L1
123
- #d[:amps_l2] # 5 Bytes Amps L2
124
- #d[:amps_l3] # 5 Bytes Amps L3
125
- #d[:watts_l1] # 7 Bytes Watts L1
126
- #d[:watts_l2] # 7 Bytes Watts L2
127
- #d[:watts_l3] # 7 Bytes Watts L3
128
- #d[:watts_total] # 7 Bytes Watts Total
129
- #d[:cosϴ_l1] # 4 Bytes Cosϴ L1
130
- #d[:cosϴ_l2] # 4 Bytes Cosϴ L2
131
- #d[:cosϴ_l3] # 4 Bytes Cosϴ L3
132
- #d[:var_l1] # 7 Bytes VAR L1
133
- #d[:var_l2] # 7 Bytes VAR L2
134
- #d[:var_l3] # 7 Bytes VAR L3
135
- #d[:var_total] # 7 Bytes VAR Total
136
- #d[:freq] # 4 Bytes Freq
137
- #d[:pulse_count_1] # 8 Bytes Pulse Count 1
138
- #d[:pulse_count_2] # 8 Bytes Pulse Count 2
139
- #d[:pulse_count_3] # 8 Bytes Pulse Count 3
140
- #d[:pulse_input_hilo] # 1 Byte Pulse Input Hi/Lo
141
- #d[:direction_of_current] # 1 Bytes direction of current
142
- #d[:outputs_onoff] # 1 Byte Outputs On/Off
143
- #d[:kwh_data_decimal_places] # 1 Byte kWh Data Decimal Places
144
-
145
- ## Request B
146
- #d[:t1_t2_t3_t4_kwh] # 32 Bytes T1, T2, T3, T4 kwh
147
- #d[:t1_t2_t3_t4_rev_kwh] # 32 Bytes T1, T2, T3, T4 Rev kWh
148
- #d[:maximum_demand] # 8 Bytes Maximum Demand
149
- #d[:maximum_demand_time] # 1 Byte Maximum Demand Time
150
- #d[:pulse_ratio_1] # 4 Bytes Pulse Ratio 1
151
- #d[:pulse_ratio_2] # 4 Bytes Pulse Ratio 2
152
- #d[:pulse_ratio_3] # 4 Bytes Pulse Ratio 3
153
- #d[:ct_ratio] # 4 Bytes CT Ratio
154
- #d[:auto_reset_md] # 1 Bytes Auto Reset MD
155
- #d[:settable_imp_per_kWh_constant] # 4 Bytes Settable Imp/kWh Constant
156
-
157
-
158
- # iSerial v4 Spec From http://documents.ekmmetering.com/Omnimeter-Pulse-v.4-Protocol.pdf
159
- # %w(01 52 31 02 30 30 31 31 28 29 03 13 16).map{|a| a.to_i(16).chr}.join
160
-
161
106
  # Returns the correct measurement for voltage, current, and power based on the corresponding power_configuration
162
107
  def calculate_measurement(m1, m2, m3)
163
108
  if power_configuration == :single_phase_2wire
@@ -185,6 +130,20 @@ module EkmOmnimeter
185
130
  end
186
131
  end
187
132
 
133
+ # Power factor values come back as C099 which need to be cast to C0.99
134
+ def cast_power_factor(s)
135
+ "#{s[0]}#{s[1,3].to_f / 100.0}"
136
+ end
137
+
138
+ # Formatted datetime reported by meter during last read.
139
+ # Raw string is formatted as YYMMDDWWHHMMSS where YY is year without century, and WW is week day with Sunday as the first day of the week
140
+ #"20#{current_time[0,2]}-#{current_time[2,2]}-#{current_time[4,2]} #{current_time[8,2]}:#{current_time[ 10,2]}:#{current_time[12,2]}"
141
+ def as_datetime(s)
142
+ DateTime.new("20#{s[0,2]}".to_i, s[2,2].to_i, s[4,2].to_i, s[8,2].to_i, s[10,2].to_i, s[12,2].to_i, '-4')
143
+ end
144
+
145
+ # All values are returned without decimals. This method loops over all
146
+ # the values and sets them to the correct precision
188
147
  def cast_response_to_correct_types(d)
189
148
 
190
149
  # Integers
@@ -224,10 +183,7 @@ module EkmOmnimeter
224
183
  end
225
184
 
226
185
  # Floats with precision 2
227
- [:power_factor_1,
228
- :power_factor_2,
229
- :power_factor_3,
230
- :frequency
186
+ [:frequency
231
187
  ].each do |k|
232
188
  logger.debug "Casting #{k}"
233
189
  d[k] = to_f_with_decimal_places(d[k], 2) if d.has_key?(k)
@@ -329,20 +285,31 @@ module EkmOmnimeter
329
285
  d[:outputs_onoff] = a.shift(1) # 1 Byte Outputs On/Off
330
286
  d[:kwh_data_decimal_places] = a.shift(1) # 1 Byte kWh Data Decimal Places
331
287
  a.shift(2) # 2 Bytes Reserved
332
- d[:current_time] = a.shift(14) # 14 Bytes Current Time
288
+ meter_timestamp = a.shift(14).join('') # 14 Bytes Current Time
333
289
  a.shift(6) # 30 30 21 0D 0A 03
334
- d[:CRC16] = a.shift(2) # 2 Bytes CRC16
290
+ d[:checksum] = a.shift(2) # 2 Bytes CRC16
335
291
 
336
292
  # Smash arrays into strungs
337
293
  d.each {|k,v| d[k] = v.join('')}
338
294
 
295
+ if verify_checksums
296
+ if Crc16.check_crc16(response, d[:checksum])
297
+ @logger.debug "Checksum matches"
298
+ else
299
+ @logger.error "CRC16 Checksum doesn't match. Expecting #{d[:checksum]} but was #{Crc16.crc16(response)}"
300
+ #raise EkmOmnimeterError, "Checksum doesn't match"
301
+ end
302
+ end
303
+
339
304
  # Cast types
340
305
  @values[:kwh_data_decimal_places] = d[:kwh_data_decimal_places].to_i
306
+ d[:power_factor_1] = cast_power_factor(d[:power_factor_1])
307
+ d[:power_factor_2] = cast_power_factor(d[:power_factor_2])
308
+ d[:power_factor_3] = cast_power_factor(d[:power_factor_3])
309
+ d[:meter_timestamp] = as_datetime(meter_timestamp)
341
310
  cast_response_to_correct_types(d)
342
311
 
343
312
  # Lookup mapped values
344
- puts "d[:pulse_input_hilo] = #{d[:pulse_input_hilo].inspect}"
345
-
346
313
  d[:pulse_1_input], d[:pulse_2_input], d[:pulse_3_input] = lookup_pulse_input_states(d[:pulse_input_hilo])
347
314
  d[:current_direction_l1], d[:current_direction_l2], d[:current_direction_l3] = lookup_direction_of_current(d[:direction_of_current])
348
315
  d[:output_1], d[:output_2] = lookup_output_states(d[:outputs_onoff])
@@ -352,7 +319,6 @@ module EkmOmnimeter
352
319
  @last_read_timestamp = Time.now
353
320
 
354
321
  # Calculate totals based on wiring configuration
355
- @values[:meter_timestamp] = meter_timestamp
356
322
  @values[:volts] = calculate_measurement(volts_l1, volts_l2, volts_l3)
357
323
  @values[:amps] = calculate_measurement(amps_l1, amps_l2, amps_l3)
358
324
  @values[:watts] = calculate_measurement(watts_l1, watts_l2, watts_l3)
@@ -364,7 +330,6 @@ module EkmOmnimeter
364
330
 
365
331
  end
366
332
 
367
-
368
333
  # Request B
369
334
  # TODO: Instead of pre-parsing and casting everything, refactor this so that only the response string gets saved, and parse out values that are accessed.
370
335
  def request_b
@@ -428,14 +393,27 @@ module EkmOmnimeter
428
393
  d[:settable_pulse_per_kwh_ratio] = a.shift(4) # 4 Bytes Settable Imp/kWh Constant
429
394
  # Diff from request A end
430
395
  a.shift(56) # 56 Bytes Reserved
431
- d[:current_time] = a.shift(14) # 14 Bytes Current Time
396
+ meter_timestamp = a.shift(14).join('') # 14 Bytes Current Time
432
397
  a.shift(6) # 30 30 21 0D 0A 03
433
- d[:checksum] = a.shift(2) # 2 Bytes CRC16
398
+ d[:checksum] = a.shift(2) # 2 Bytes CRC16
434
399
 
435
400
  # Smash arrays into strungs
436
401
  d.each {|k,v| d[k] = v.join('')}
437
402
 
403
+ if verify_checksums
404
+ if Crc16.check_crc16(response, d[:checksum])
405
+ @logger.debug "Checksum matches"
406
+ else
407
+ @logger.error "CRC16 Checksum doesn't match. Expecting #{d[:checksum]} but was #{Crc16.crc16(response)}"
408
+ #raise EkmOmnimeterError, "Checksum doesn't match"
409
+ end
410
+ end
411
+
438
412
  # Cast types
413
+ d[:power_factor_1] = cast_power_factor(d[:power_factor_1])
414
+ d[:power_factor_2] = cast_power_factor(d[:power_factor_2])
415
+ d[:power_factor_3] = cast_power_factor(d[:power_factor_3])
416
+ d[:meter_timestamp] = as_datetime(meter_timestamp)
439
417
  cast_response_to_correct_types(d)
440
418
 
441
419
  # Lookup mapped values
@@ -447,7 +425,6 @@ module EkmOmnimeter
447
425
  @last_read_timestamp = Time.now
448
426
 
449
427
  # Calculate totals based on wiring configuration
450
- @values[:meter_timestamp] = meter_timestamp
451
428
  @values[:volts] = calculate_measurement(volts_l1, volts_l2, volts_l3)
452
429
  @values[:amps] = calculate_measurement(amps_l1, amps_l2, amps_l3)
453
430
  @values[:watts] = calculate_measurement(watts_l1, watts_l2, watts_l3)
@@ -458,7 +435,6 @@ module EkmOmnimeter
458
435
  end
459
436
 
460
437
 
461
-
462
438
  # Gets remote EKM meter data using iSerial defaults
463
439
  # meter_number is the meters serial number. leading 0s not required.
464
440
  # remote_address is the IP address of the ethernet-RS485 converter
@@ -473,24 +449,24 @@ module EkmOmnimeter
473
449
  begin
474
450
 
475
451
  socket = TCPSocket.new(remote_address, remote_port)
476
- logger.debug "Socket open" unless logger.nil?
452
+ @logger.debug "Socket open"
477
453
 
478
454
  # Send request to the meter
479
- logger.debug "Request: #{request}" unless logger.nil?
455
+ @logger.debug "Request: #{request}"
480
456
  socket.write(request)
481
457
 
482
458
  # Receive a response of 255 bytes
483
459
  response = socket.read(read_bytes)
484
- logger.debug "Socket response #{response.length}" unless logger.nil?
485
- logger.debug response unless logger.nil?
460
+ @logger.debug "Socket response #{response.length}"
461
+ @logger.debug response
486
462
 
487
463
  rescue Exception => ex
488
- logger.error "Exception\n#{ex.message}\n#{ex.backtrace.join("\n")}" unless logger.nil?
464
+ @logger.error "Exception\n#{ex.message}\n#{ex.backtrace.join("\n")}"
489
465
  ensure
490
466
  # EKM Meter software sends this just before closing the connection, so we will too
491
467
  socket.write "\x0a\x03\x32\x3d"
492
468
  socket.close
493
- logger.debug "Socket closed" unless logger.nil?
469
+ @logger.debug "Socket closed"
494
470
  end
495
471
 
496
472
  return response
@@ -7,7 +7,7 @@ module EkmOmnimeter
7
7
  module VERSION #:nodoc:
8
8
  MAJOR = 0
9
9
  MINOR = 2
10
- PATCH = 3
10
+ PATCH = 4
11
11
 
12
12
  STRING = [MAJOR, MINOR, PATCH].join('.')
13
13
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ekm-omnimeter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Duggan
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
- - !ruby/object:Gem::Dependency
70
- name: xively-rb-connector
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ~>
74
- - !ruby/object:Gem::Version
75
- version: '0.1'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ~>
81
- - !ruby/object:Gem::Version
82
- version: '0.1'
83
69
  description: Ruby interface to the EKM Omnimeter Pulse
84
70
  email:
85
71
  - Jordan.Duggan@gmail.com
@@ -96,6 +82,7 @@ files:
96
82
  - ekm-omnimeter.gemspec
97
83
  - lib/ekm-omnimeter.rb
98
84
  - lib/ekm-omnimeter/configuration.rb
85
+ - lib/ekm-omnimeter/crc16.rb
99
86
  - lib/ekm-omnimeter/logging.rb
100
87
  - lib/ekm-omnimeter/meter.rb
101
88
  - lib/ekm-omnimeter/version.rb