Skip to content

Commit f565cc2

Browse files
committed
Initial version of the raw data collection script.
- Should be cross platform, requires pyserial
1 parent 6c85fab commit f565cc2

File tree

2 files changed

+253
-0
lines changed

2 files changed

+253
-0
lines changed

fcv/KiibohdForce.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
#!/usr/bin/python2
2+
#| KiibohdForce
3+
#| Reads in the force curve data from the KiibohdForce microcontroller
4+
5+
# Copyright (C) 2014 by Jacob Alexander
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in
15+
# all copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
# THE SOFTWARE.
24+
25+
26+
27+
28+
#| Initial Imports
29+
#| Keep this list at a bare minimum for faster script execution
30+
#| Delay the imports to their respective function/class if possible
31+
import argparse
32+
import os
33+
import re
34+
import sys
35+
36+
37+
38+
#| Print Decorator Variables
39+
ERROR = '\033[5;1;31mERROR\033[0m:'
40+
41+
42+
43+
#| Python Text Formatting Fixer...
44+
#| Because the creators of Python are averse to proper capitalization.
45+
textFormatter_lookup = {
46+
"usage: " : "Usage: ",
47+
"optional arguments" : "Optional Arguments",
48+
}
49+
50+
def textFormatter_gettext( s ):
51+
return textFormatter_lookup.get( s, s )
52+
53+
argparse._ = textFormatter_gettext
54+
55+
56+
57+
#| Variable Container Class
58+
class VariableContainer:
59+
"""Class for storing variables with a dictionary-like interface"""
60+
def __init__( self ):
61+
# Initialize default variables
62+
self.setDefaults()
63+
64+
65+
# Item Operators
66+
def __getitem__( self, index ):
67+
retVal = self.getitem( index )
68+
69+
# Could not find the variable
70+
if retVal == None:
71+
print "{0} Could not find specified variables: \033[35m{1}\033[0m".format( ERROR, index )
72+
73+
return retVal
74+
75+
76+
def __setitem__( self, index, value ):
77+
# Search the settings list of tuples to see if the variable already exists
78+
for varset in self.settings:
79+
if varset[0] == index:
80+
varset[1] = value
81+
return
82+
83+
# Otherwise append variable set to the end of the list
84+
self.settings.append( [ index, value ] )
85+
86+
87+
def getitem( self, index ):
88+
# Search the settings list of tuples for the requested item
89+
for varset in self.settings:
90+
if varset[0] == index:
91+
return varset[1]
92+
return None
93+
94+
95+
# Set default variables
96+
def setDefaults( self ):
97+
self.settings = [
98+
#['',''], # List format
99+
]
100+
101+
102+
# List of keys
103+
def keys( self ):
104+
return tuple( x[0] for x in self.settings )
105+
106+
107+
# String representation of variable list
108+
def __str__( self ):
109+
stringVer = ""
110+
for varset in self.settings:
111+
stringVer += '{0} | "{1}"\n'.format( varset[0], varset[1] )
112+
113+
return stringVer
114+
115+
116+
117+
#| Argument Processing
118+
def processCommandLineArgs( varContainer ):
119+
# Setup argument processor
120+
pArgs = argparse.ArgumentParser(
121+
usage="%(prog)s [options] <serial device> <switch name>",
122+
description="This script records the data from a KiibohdForce gauge and outputs it as an .raw file.\n"
123+
"The gauge should be powered on at distance 0 and before starting the script, position the gauge at the start position.",
124+
epilog="Example: {0} /dev/ttyACM0 switch.raw".format( os.path.basename( sys.argv[0] ) ),
125+
formatter_class=argparse.RawTextHelpFormatter,
126+
add_help=False,
127+
)
128+
129+
# Positional Arguments
130+
pArgs.add_argument( 'serial_port', help=argparse.SUPPRESS ) # Suppressed help output, because Python output is verbosely ugly
131+
pArgs.add_argument( 'switch_name', help=argparse.SUPPRESS ) # Suppressed help output, because Python output is verbosely ugly
132+
133+
# Optional Arguments
134+
pArgs.add_argument( '-h', '--help', action="help",
135+
help="This message." )
136+
137+
# Process Arguments
138+
args = pArgs.parse_args()
139+
140+
# Parameters
141+
varContainer['SwitchName'] = args.switch_name
142+
varContainer['SerialPort'] = args.serial_port
143+
varContainer['Filename'] = "{0}.raw".format( varContainer['SwitchName'] )
144+
145+
# Check file existance, and rename if necessary
146+
counter = 1
147+
while os.path.isfile( varContainer['Filename'] ):
148+
varContainer['Filename'] = "{0}-{1}.raw".format( args.switch_name, counter )
149+
counter += 1
150+
151+
152+
153+
def sendCmd( serialPort, cmdName ):
154+
serialPort.write( cmdName )
155+
serialPort.write( chr( 0x0D ) )
156+
serialPort.flush()
157+
inStr = serialPort.read( 1 )
158+
while serialPort.inWaiting() > 0:
159+
inStr += serialPort.read( 1 )
160+
161+
return inStr
162+
163+
164+
165+
def recordData( varContainer, forceData ):
166+
# Delayed imports
167+
import serial
168+
169+
# Initialize serial port, USB serial port is used, speed does not have to be negotiated
170+
ser = serial.Serial( varContainer['SerialPort'], timeout=2 )
171+
172+
# Mark the start/end position of the reading
173+
cmdOut = sendCmd( ser, "start" )
174+
175+
# Start the free recording
176+
print "Initiating force curve reading..."
177+
cmdOut = sendCmd( ser, "free" )
178+
179+
# Read from the serial port until receiving the ::End::
180+
inputList = []
181+
while True:
182+
inStr = ser.read( 1 )
183+
while not inStr[-2:] == "\r\n":
184+
inStr += ser.read( 1 )
185+
inStr = inStr[:-2]
186+
print inStr
187+
inputList.append( inStr )
188+
189+
if "::End::" in inStr:
190+
break
191+
192+
# Once the ::End:: marker is received, stop the free recording
193+
sendCmd( ser, "stop" )
194+
print "Force curve reading has finished."
195+
196+
varContainer['forceData'] = inputList
197+
198+
199+
200+
#| Standalone Execution (Main)
201+
if __name__ == '__main__':
202+
203+
## Process Args ##
204+
varContainer = VariableContainer()
205+
processCommandLineArgs( varContainer )
206+
207+
## Record Data ##
208+
forceData = VariableContainer()
209+
recordData( varContainer, forceData )
210+
211+
## Write raw data to file ##
212+
outFile = open( varContainer['Filename'], 'w' )
213+
for line in varContainer['forceData']:
214+
outFile.write( "{0}\n".format( line ) )
215+
outFile.close()
216+
217+
# Successful Execution
218+
sys.exit( 0 )
219+

fcv/example.fcv

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# <Title>
2+
# <Author>
3+
# <Description>
4+
#
5+
# Created: <Date>
6+
# FCV Version: 0.2
7+
#
8+
9+
# Graph Bounds
10+
X Minimum: 0
11+
X Maximum: 250
12+
X Resolution: 1
13+
Y Minimum: -250
14+
Y Maximum: 250
15+
Y Resolution: 1
16+
17+
# Statistics
18+
Force Minimum: -10
19+
Force Maximum: 38
20+
Distance Minimum: 0
21+
Distance Maximum: 4.43
22+
23+
# Recorded Data (distance, force)
24+
Distance Units: mm
25+
Force Units: gf
26+
27+
[Recorded Forward]
28+
10,0
29+
9,3
30+
31+
[Recorded Reverse]
32+
0,3
33+
10,0
34+

0 commit comments

Comments
 (0)