Skip to content

Commit ae7485d

Browse files
committed
Posting EC Solution Scripts
1 parent 18b1fde commit ae7485d

12 files changed

+5390
-0
lines changed
Binary file not shown.

Application_Specific/Electrochemistry/Chronoamperometry.tsp

Lines changed: 502 additions & 0 deletions
Large diffs are not rendered by default.

Application_Specific/Electrochemistry/Chronopotentiometry.tsp

Lines changed: 504 additions & 0 deletions
Large diffs are not rendered by default.

Application_Specific/Electrochemistry/CurrentPulseAndSquareWave.tsp

Lines changed: 609 additions & 0 deletions
Large diffs are not rendered by default.

Application_Specific/Electrochemistry/CyclicVoltammetry.tsp

Lines changed: 791 additions & 0 deletions
Large diffs are not rendered by default.

Application_Specific/Electrochemistry/EC_Framework.tsp

Lines changed: 995 additions & 0 deletions
Large diffs are not rendered by default.

Application_Specific/Electrochemistry/EC_Images.tsp

Lines changed: 1022 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
--[[Last Updated: $Date: 2020/03/06 $ $Change: 189427 $
2+
3+
Copyright © Keithley Instruments, LLC. All rights reserved.
4+
5+
Part of the Keithley Instruments Potentiostat System.
6+
Users are permitted to modify but not distribute the software without prior written permission from Keithley.
7+
8+
THIS SOFTWARE IS PROVIDED “AS-IS,” WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES OF ANY KIND, INCLUDING
9+
BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
10+
NON-INFRINGEMENT OF INTELLECTUAL PROPERTY. IN NO EVENT SHALL KEITHLEY INSTRUMENTS, ITS AFFILIATES,
11+
OFFICERS, EMPLOYEES, DIRECTORS, AGENTS, SUPPLIERS, OR OTHER THIRD PARTIES BE LIABLE FOR ANY DIRECT,
12+
INDIRECT, INCIDENTAL, PUNITIVE, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT
13+
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14+
15+
This program measures the open circuit potential of a battery over time.
16+
17+
All recordings must be 4-wire measurements.
18+
19+
Using this test script requires the Model 2450, 2460, or 2461 to have firmware 1.5.0 or greater.
20+
The latest firmware can be found on the Keithley website, tek.com/keithley.
21+
22+
]]
23+
24+
--[General variables]--
25+
changelist = "$Change: 189427 $"
26+
local MIN_EC_FRAMEWORK_VERSION = "1.5"
27+
stats = {}
28+
local eventNums = {2203, 2728, 5076, 5084}
29+
SAVED_SETUP_BASE_FILENAME = "KI_"..localnode.model.."-EC_OP_Setup"
30+
SCRIPT_TITLE = "Open Circuit Potential"
31+
X_UNIT = "s"
32+
Y_UNIT = "V"
33+
34+
local measperiod = {min = 0.75, val = 1, max = 100} --s/pt
35+
local numsamples = {min = 1, val = 1, max = 100000}
36+
37+
---------------------------------------------------------------------------------------------------------------------------------
38+
--All these functions are related to the USB drive and saving data to that drive
39+
40+
--Save data points with their relative timestamps from the buffer to a file that the user names
41+
function saveData()
42+
local stringData = { }
43+
44+
table.insert(stringData, "\nSampling Period,"..data.meas_interval..",s/pt")
45+
table.insert(stringData, "Potential Range,"..V_RANGES.range[data.v_range]..",V")
46+
table.insert(stringData, "\nQuick Stats")
47+
table.insert(stringData, stats.numPts..",points recorded,,"..stats.avg..",V average")
48+
table.insert(stringData, stats.min..",V minimum,,"..stats.stdev..",V standard deviation")
49+
table.insert(stringData, stats.median..",V median,,"..stats.time..",seconds to run experiment")
50+
table.insert(stringData, stats.max..",V maximum")
51+
table.insert(stringData, "\nRaw Data")
52+
table.insert(stringData, "Time(s),Current(A),Potential(V)")
53+
for i = 1, defbuffer1.n do
54+
table.insert(stringData, defbuffer1.relativetimestamps[i]..","..defbuffer1.sourcevalues[i]..","..defbuffer1.readings[i])
55+
end
56+
57+
save_data_to_usb_with_prompt(stringData)
58+
end
59+
60+
--------------------------------------------------------------------------------------------------------------------------
61+
function basicSettings()
62+
--Setup the instrument source and measure settings for Eoc measurement
63+
smu.source.func = smu.FUNC_DC_CURRENT
64+
smu.measure.func = smu.FUNC_DC_VOLTAGE
65+
smu.source.offmode = smu.OFFMODE_HIGHZ
66+
smu.measure.sense = smu.SENSE_4WIRE
67+
smu.source.range = 1e-6
68+
smu.source.level = 0
69+
smu.source.autodelay = smu.ON
70+
smu.measure.range = V_RANGES.range[data.v_range]
71+
smu.source.vlimit.level = V_RANGES.range[data.v_range]
72+
smu.measure.nplc = 10
73+
smu.measure.autozero.enable = smu.ON
74+
end
75+
------------------------------------------------------------------------------------------------------------------------------
76+
--This function measures the open circuit potential, providing the user with information about the battery potential directly from the Main Menu.
77+
function runTest()
78+
defbuffer1.clear()
79+
defbuffer1.capacity = 100000
80+
81+
display.graph.removeall(ui.home.graph)
82+
display.graph.add(ui.home.graph, defbuffer1, display.ELEMENT_DATA, defbuffer1, display.ELEMENT_TIME)
83+
display.graph.drawstyle(ui.home.graph, display.STYLE_BOTH)
84+
display.graph.scalex(ui.home.graph, display.XSCALE_SMART)
85+
display.graph.scaley(ui.home.graph, display.YSCALE_SMART)
86+
87+
--[[Initialize the trigger timer that creates events which cause the instrument to take a measurement.
88+
The max amount of events that will be created is 100,000, which matches the max capacity of the buffer]]
89+
trigger.timer[1].reset()
90+
trigger.timer[1].start.generate = trigger.ON
91+
trigger.timer[1].delaylist = {data.meas_interval, data.meas_interval}
92+
trigger.timer[1].count = 100000
93+
94+
--Initialize the trigger timer that creates events which cause the animation to change.
95+
trigger.timer[2].reset()
96+
trigger.timer[2].start.generate = trigger.OFF
97+
trigger.timer[2].delaylist = {0.5, 0.5} --s
98+
trigger.timer[2].count = 500000
99+
100+
--[Experiment]--
101+
trigger.model.setblock(1, trigger.BLOCK_BUFFER_CLEAR)
102+
trigger.model.setblock(2, trigger.BLOCK_SOURCE_OUTPUT, smu.ON)
103+
trigger.model.setblock(3, trigger.BLOCK_WAIT, trigger.EVENT_TIMER1)
104+
trigger.model.setblock(4, trigger.BLOCK_MEASURE, defbuffer1)
105+
trigger.model.setblock(5, trigger.BLOCK_BRANCH_LIMIT_CONSTANT, trigger.LIMIT_ABOVE, 0, V_RANGES.range[data.v_range], 7, 4)
106+
trigger.model.setblock(6, trigger.BLOCK_BRANCH_COUNTER, data.num_samples, 3)
107+
trigger.model.setblock(7, trigger.BLOCK_SOURCE_OUTPUT, smu.OFF) --Done, turn the source output off.
108+
109+
display.setstate(ui.home.start_stop, display.STATE_ENABLE)
110+
display.waitevent(0.001) --Consume any previous events
111+
112+
--start the trigger model and timers
113+
trigger.timer[1].enable = trigger.ON
114+
timer.cleartime()
115+
trigger.model.initiate()
116+
117+
while trigger.model.state() == trigger.STATE_RUNNING or trigger.model.state() == trigger.STATE_WAITING do
118+
update_progress_bar(defbuffer1.n / data.num_samples * 100)
119+
delay(0.5)
120+
if display.waitevent(0.001) == ui.home.start_stop then
121+
trigger.model.abort()
122+
stats.time = timer.gettime()
123+
test_cleanup()
124+
return
125+
end
126+
end
127+
128+
waitcomplete()
129+
--Determine how long the test lasted
130+
stats.time = timer.gettime()
131+
if defbuffer1.n < data.num_samples then
132+
display.input.prompt(display.BUTTONS_OK, "Experiment terminated because compliance was reached.")
133+
end
134+
calculateStats()
135+
update_stats(stats_table, stats)
136+
end
137+
138+
-----------------------------------------------------------------------------------------------------------------
139+
--The following functions are related to statistics of the results
140+
141+
--This function calculates the number of points, the minimum potential, the maximum potential, the median potential, the average potential, and the standard deviation of the potential
142+
function calculateStats()
143+
local prompt
144+
if defbuffer1.n >= 10000 then prompt = display.prompt(display.BUTTONS_NONE, "Calculating Statistics...") end
145+
bufferCopy = {}
146+
local bufStats = buffer.getstats(defbuffer1)
147+
if bufStats.n > 0 then
148+
149+
--copy the buffer
150+
for i=1, defbuffer1.n do
151+
bufferCopy[i] = defbuffer1.readings[i]
152+
end
153+
154+
--number of data points
155+
stats.numPts = defbuffer1.n
156+
157+
--find average
158+
stats.avg = bufStats.mean
159+
160+
--find standard deviation
161+
stats.stdev = bufStats.stddev or 0
162+
163+
table.sort(bufferCopy)
164+
--find minimum
165+
stats.min = bufStats.min.reading
166+
--find maximum
167+
stats.max = bufStats.max.reading
168+
--find median
169+
if math.mod(table.getn(bufferCopy), 2) == 1 then
170+
stats.median = bufferCopy[math.ceil(table.getn(bufferCopy)/2)]
171+
elseif table.getn(bufferCopy) > 0 then
172+
stats.median = (bufferCopy[table.getn(bufferCopy)/2] + bufferCopy[table.getn(bufferCopy)/2 + 1])/2
173+
else
174+
stats.median = 0
175+
end
176+
177+
if prompt ~= nil then display.delete(prompt) end
178+
else
179+
stats = { }
180+
end
181+
end
182+
183+
function measure_event_handler(eventID, value)
184+
sync_ui_to_data(ui_data_event_table, eventID)
185+
end
186+
187+
function start_stop_event_handler(eventID)
188+
test_start()
189+
basicSettings()
190+
runTest()
191+
test_cleanup()
192+
end
193+
194+
local function create_screens()
195+
ui.measure = { }
196+
ui.measure.root = display.create(display.ROOT, display.OBJ_SCREEN, "Measure Settings")
197+
198+
end
199+
200+
local function create_controls()
201+
--Setup ui.home controls
202+
display.settext(ui.home.version, "version "..get_version_number(changelist))
203+
display.settext(ui.home.root, SCRIPT_TITLE)
204+
display.setevent(ui.home.start_stop, display.EVENT_PRESS, "start_stop_event_handler(%id)")
205+
hide_home_stats() --We're not using stats here, so hide them
206+
display.setevent(ui.home.save, display.EVENT_PRESS, "saveData()")
207+
208+
--Create ui.menu controls
209+
local offset = 0 -- button offset
210+
if display.EVENT_ENDAPP then offset = 1 end
211+
--ui.menu.root = display.create(display.ROOT, display.OBJ_SCREEN_MENU, "Settings", "Views", "Save/Load", "System")
212+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 0 + offset, 0 + offset, ui.measure.root, "Measure Settings", "meas_settings")
213+
214+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 1 + offset, 0 + offset, ui.home.root, "Run/Graph", "graph")
215+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 1 + offset, 1 + offset, display.SCREEN_READING_TABLE, "Reading Table")
216+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 1 + offset, 2 + offset, ui.stats.root, "Statistics", "stats")
217+
218+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 2 + offset, 0 + offset, ui.save.root, "Save Settings", "save")
219+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 2 + offset, 1 + offset, ui.load.root, "Load Settings", "load")
220+
221+
display.create(ui.menu.root, display.OBJ_BUTTON_MENU, 3 + offset, 0 + offset, display.SCREEN_SYS_EVENT_LOG, "Event Log")
222+
223+
--Create ui.measure controls
224+
ui.measure.potential_range = create_edit_option(ui.measure.root, 200, option_height*0 + 20, "Potential Range", V_RANGES.value[1].."..."..V_RANGES.value[table.getn(V_RANGES.value)], V_RANGES)
225+
table.insert(ui_data_event_table, {ui = "ui.measure.potential_range", data = "data.v_range", event = "measure_event_handler"})
226+
display.setvalue(ui.measure.potential_range, get_index_from_range(V_RANGES, 2)) --Set default range to 2V
227+
measure_event_handler(ui.measure.potential_range, 2) --Set default range to 2V
228+
display.setevent(ui.measure.potential_range, display.EVENT_PRESS, "measure_event_handler(%id, %value)")
229+
230+
ui.measure.num_samples = display.create(ui.measure.root, display.OBJ_EDIT_NUMBER, 600, option_height*0 + 20, "Sample Count", numsamples.min.." to "..numsamples.max, display.NFORMAT_INTEGER, numsamples.val, numsamples.min, numsamples.max)
231+
display.setevent(ui.measure.num_samples, display.EVENT_PRESS, "measure_event_handler(%id, %value)")
232+
table.insert(ui_data_event_table, {ui = "ui.measure.num_samples", data = "data.num_samples", event = "measure_event_handler"})
233+
234+
ui.measure.meas_interval = display.create(ui.measure.root, display.OBJ_EDIT_NUMBER, 200, option_height*1 + 20, "Sample Interval (s/pt)", display.format(measperiod.min, "s", display.NFORMAT_USER, 4).." to "..display.format(measperiod.max, "s", display.NFORMAT_USER, 4), display.NFORMAT_USER, measperiod.val, measperiod.min, measperiod.max, "s")
235+
display.setevent(ui.measure.meas_interval, display.EVENT_PRESS, "measure_event_handler(%id, %value)")
236+
table.insert(ui_data_event_table, {ui = "ui.measure.meas_interval", data = "data.meas_interval", event = "measure_event_handler"})
237+
238+
sync_ui_to_data(ui_data_event_table)
239+
240+
--Create ui.stats controls
241+
tbl_settings = { }
242+
tbl_settings.screen_id = ui.stats.root
243+
tbl_settings.rows = {"-Time", "Points", "-Minimum", "Maximum", "Median", "Average", "Stddev-"}
244+
tbl_settings.cols = {"-", "-"}
245+
tbl_settings.table = {x = 200-14*2, y = 75}
246+
tbl_settings.cell = { width = 200 }
247+
tbl_settings.font = display.FONT_MEDIUM
248+
249+
--Create ui.stats controls
250+
stats_table = tbl.make(tbl_settings)
251+
252+
sync_ui_to_data(ui_data_event_table)
253+
end
254+
255+
local function create_global_tables()
256+
--NOTE: Use find and replace when changing *.value strings to avoid breaking lookups
257+
258+
259+
end
260+
261+
--[[------------------------------------------------------------------------------------------------------------------------------
262+
=============================
263+
--Program is executed here
264+
=============================
265+
]]
266+
267+
local function main()
268+
if not EC_FRAMEWORK_VERSION then
269+
if EC_Framework then
270+
EC_Framework.run()
271+
elseif file.usbdriveexists() == 1 then
272+
EC_Framework = script.load("/usb1/EC_Framework.tsp")
273+
if EC_Framework then
274+
EC_Framework.run()
275+
end
276+
end
277+
278+
if not EC_Framework then
279+
print("Unable to load EC_Framework.tsp!")
280+
display.input.prompt(display.BUTTONS_OK, "Unable to load EC_Framework.tsp!")
281+
exit()
282+
end
283+
end
284+
285+
if EC_FRAMEWORK_VERSION < MIN_EC_FRAMEWORK_VERSION then
286+
print("EC_Framework version "..MIN_EC_FRAMEWORK_VERSION.." or greater required, please update.")
287+
display.input.prompt(display.BUTTONS_OK, "EC_Framework version "..MIN_EC_FRAMEWORK_VERSION.." or greater required, please update.")
288+
exit()
289+
end
290+
291+
reset()
292+
for i = 1,table.getn(eventNums) do eventlog.suppress(eventNums[i]) end
293+
smu.source.offmode = smu.OFFMODE_HIGHZ
294+
smu.measure.sense = smu.SENSE_4WIRE
295+
296+
init_toplevel()
297+
data.vertexPotential = { }
298+
create_global_tables()
299+
300+
create_top_screens()
301+
create_screens()
302+
create_loading_screen()
303+
304+
create_top_controls()
305+
create_controls()
306+
307+
destroy_loading_screen()
308+
end
309+
main()

0 commit comments

Comments
 (0)