Saturday, May 28, 2016

Reading and Storing Sensor Data

This Python script was written to read data from a Honeywell HMR2300 magnetometer, graph the incoming data, and store it as a CSV file.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# Script for logging and displaying magnetometer serial data 
# Libraries required:
#  - pySerial 
# - matplotlib
#   - numPy 
#   - python-dateutil
# Written for the Honeywell HMR2300 magnetometer 
# Author: Zac DeMeo

import serial
import atexit
import time
import binascii
import numpy as np

### Begin script settings ###

# Serial communication settings
serialPort = 'COM22'
baud = 9600
par = serial.PARITY_NONE
sbits = serial.STOPBITS_ONE
bsize = serial.EIGHTBITS

# Terminal settings
displayOutput = True

# Data logging settings
logOutput = True

# Real time graph settings
graphOutput = False # Experimental
windowSize = 60 # Samples/window

### End script settings ###

### Functions ###
def onExit(s, f):
 try:
  s.write(chr(27)) # Send escape character; stop polling HMR2300 sensors
  s.close()
  if logOutput:
   f.close()
 except:
  print "Something went wrong..."

# Assumes HMR2300 ID is 00  
def setupMagnetometer(s):
 s.write('*00WE\r') # Activate "Write Enable"
 time.sleep(0.05)
 s.write('*00B\r') # Set to binary output (this allows for faster sampling rates)
 time.sleep(0.05)
 s.write('*00WE\r') # Activate "Write Enable"
 time.sleep(0.05)
 s.write('*00R=100\r') # Set device to 100 samples/s
 time.sleep(0.05)
 s.write('*00C\r') # Set HMR2300 to output data continuously
 time.sleep(0.05)
 
# Setup CSV logging 
def setupLogOutput(): 
 # Get system time and create CSV filename string
 t = time.localtime(time.time())
 filename = time.strftime('%d%b%y_%H;%M;%S', t) + '.csv'
 
 # Initialize CSV object and writer object
 f = open(filename, 'wb')
 
 return f
 
# Setup Writer object for writing CSV files 
def setupWriter(f):
 writer = csv.writer(f, dialect='excel')
 writer.writerow(('Sample', 'X (nT)', 'Y (nT)', 'Z (nT)')) # First row in CSV output
 
 return writer
 
# Initialize output plot variables
def setupGraphOutput():
 import matplotlib.pyplot as plt
 
 x = []
 y = []
 z = []
 tx = []
 
 fig, (ax1, ax2, ax3) = plt.subplots(3, sharex=True, sharey=False) 
 plt.xlim(0, windowSize)
 ax1.set_ylabel('X (nT)')
 ax2.set_ylabel('Y (nT)')
 ax3.set_ylabel('Z (nT)')
 ax3.set_xlabel('Sample #')
 plt.ion() # Interactive mode on
 plt.show() # Show plot


 
### Main loop ###
if (displayOutput == False and logOutput == False and graphOutput == False):
 print "What exactly are you trying to accomplish?"
 exit()

# Initialize pySerial library 
s = serial.Serial(port=serialPort, baudrate=baud, parity=par, stopbits=sbits, bytesize=bsize)
setupMagnetometer(s)

if logOutput:
 import time
 import csv
 
 f = setupLogOutput() 
 writer = setupWriter(f)
else:
 f = None
 
if graphOutput:
 setupGraphOutput() 

# Register onExit() method to be called upon script termination
atexit.register(onExit, s, f) 
 
# Counter to keep track of how many samples have been collected
i = 0

# Now, start data acquisition
while True:
 line = ''
 #s.write('*00P\r') # Poll HMR2300 once
  
 while True:
  # Read byte from serial buffer
  char = s.read()
 
  # When a carriage return is read from buffer, convert line from binary to hex
  if char == '\r':
   line = binascii.hexlify(line)
   break
  else:
   line += char 
  
 # Break serial data into respective x-, y-, and z-components 
 hexLine = [line[:4], line[4:8], line[8:12]] # (X, Y, Z)
 
 # Convert each hex component to decimal. Python doesn't seem to preserve the sign of each hex value. Work around this.
 try:
  decLine = [int(hexLine[0], 16), int(hexLine[1], 16), int(hexLine[2], 16)] # (X, Y, Z)
 except:
  continue
 
 # Restore the sign of each measurement
 for j in range(0, 3):
  if decLine[j] >= 35536:
   decLine[j] -= 65536
   
 #print 'X: ' + str(decLine[0]) + ' Y: ' + str(decLine[1]) + ' Z: ' + str(decLine[2]) 
 
 # Update graph as serial data is processed
 if graphOutput:
  if n == windowSize * i:
   x = []
   y = []
   z = []
   tx = []
   
   i += 1
   plt.xlim(n, windowSize * i)
  
  tx.append(n)
  x.append(fParsedLine[0])
  y.append(fParsedLine[1])
  z.append(fParsedLine[2])
  ax1.plot(tx, x, 'b-')
  ax2.plot(tx, y, 'g-')
  ax3.plot(tx, z, 'r-')
  plt.draw()
  plt.pause(0.001)
 
 # Now, wait before polling HMR2300 again
 #time.sleep(.001) 

 # The first reading is usually incorrect. Throw the first sample out
 if i == 0:
  i += 1
  continue

 # Convert HMR2300 raw output to nT and round
 x = np.around(decLine[0] * 6.667, 1)
 y = np.around(decLine[1] * 6.667, 1)
 z = np.around(decLine[2] * 6.667, 1)

 # Print output to console
 if displayOutput:
  print 'X: ' + str(x) + ' Y: ' + str(y) + ' Z: ' + str(z) 

 # Write line to CSV file
 if logOutput:
  writer.writerow((i, x, y, z))
  
 i += 1