For use with: Alpaca Kernel
17B - Testing and Calibration#
Overview#
Before you begin assembling your potentiostat, in this assignment, you will learn about the limitations of Alpaca and Pico by measuring noise, resolution, and differences between requested and generated signal.
We expect you to eventually make a voltammogram with positive and negative currents and voltages. The location of the voltammogram peak depends on the substance you measure, but can be influenced by limitations of your system. In this 17B assignment you will explore those limitations.
If you are still unsure which design to choose, feel free to reach out to the TAs and discuss which option would be the most suitable for you and your partner.
Goals#
Learn:
how to save and read files with Pico, and make a habit of saving data regularly
how to handle errors effectively
more about the noise in Alpaca
Calibrate your DAC Assistant
Understand the limits of Alpaca+Pico measurements
Requirements#
βββ = Mandatory
π = Entirely optional. Interesting and useful.
ππ = Optional. Recommended.
πππ = Recommended.
ππππ = Challenging. Surely, itβs worth it!
Outline#
Implement 1 - Introduction
I1.1 βββ - Jumpers
I1.2 πππ - Picotools
I1.3 πππ - Saving files
I1.4 πππ - Handling errors
Implement 2 - ADCs
I2.1 π - Offset
I2.2 ππ - Noise
I2.3 πππ - -12Vin to Cria
Implement 3 - DAC
I3.1 βββ - Accuracy
I3.2 ππ - Noise
I3.3 πππ - Resolution
Implement 4 ππ - Timing accuracy
Implement 5B - Basic design only - DAC Assistant
I5B.1 βββ - Calibration
I5B.2 πππ - Resolution
I5B.3 ππ - Noise
Implement 6B - Basic design only - GAIN 1:3
I6B.1 βββ - Configure GAIN 1:3
I6B.2 π - Noise
Implement 7 ππππ - Helper functions
Compare and Conclude βββ
Implement 1: Introduction#
βββ I1.1: Jumpers#
Check your Alpaca - are all jumpers present?
The Fritzing just below shows how your Alpaca should look like at the beginning of this project.
Make sure that your Alpaca matches this layout.
Double check the four jumpers at SPI DIRECT TO DAQ and the two jumpers (or wires) at AMPLIFIER DIRECT TO NANO
Default ALPACA configuration for the Final Project
πππ I1.2: Picotools - Module for testing the potentiostat#
In the previous assignments, we defined all our custom functions in the cell of the notebook, which makes Jupyter notebooks so handy for prototyping, but for a larger project, this approach creates notebooks that are cumbersome to navigate. So, when your custom function is tested and ready for use later, or simply to have a better overview in your notebook, you may store its definition in an external Python file - a library, which is a simple, and a good practice to improve your workflow.
As an example, we prepared a small library with some familiar and some new functions for automating the testing of the potentiostat, the picotools.py.
Download this file from Brightspace and save it in the folder with the notebook 17B.
This file has to be sent to Pico using the Alpaca kernel, and imported like any other module that you have been using so far. This procedure will be explained throughout this notebook.
Feel free to take a peek into this file to learn more about the function that we prepared for you to speed up and simplify some of the procedures.
At some points throughout the Final Project, you might need to tailor some functions for your application. If you want to write your own, improved functions, you may simply add them to picotools.py, when theyβre ready for βdeploymentβ. You could also create your own library(-ies) to store some of the advanced Voltammetry procedures of your Final Project. So, consider the use case of picotools as a basic example and an inspiration towards advanced programming practices with Python.
Please, bear in mind that Picotools is currently in version 0.2, so it is not a production-level module. It wasnβt thoroughly tested by a large community, like for example
numpy, so prepare to encounter many constraints, minimal documentation, and potentially some serious bugs. If that happens, please report them to us or take the matter in your hands and improve them right away.
Import picotools#
Follow the procedure in the cells below to import picotools.
This procedure assumes that you have already downloaded
picotools.pyfrom Brightspace and placed it in the same folder with this notebook.
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import os
import machine
import time
Now, all functions and variables specified in the picotools.py are available under as methods of the pico. object, so for example:
# Example of a function in picotools
pico.test_ADC()
Note that we also have to additionally import all the usual modules.
πππ I1.3: Saving files#
Because of the very limited storage in Picoβs memory, you will have to develop a habit to regularly save your measurements.
It is crucial in this project!
Because of the amount of data that will be generated during your measurements, you will very often overload Picoβs memory, and most likely have to reset it regularly. In this notebook, we will show you how to do it safely and to avoid frustration of lost data.
Follow the recipe below to learn how to shuttle files between Pico and your computer. It is a little cumbersome, so we prepared some examples for you.
Saving textfiles#
In the example below, you can also learn how to make a timestamp for your measurement.
# Initialize the RTC (Real-time Clock)
rtc = machine.RTC()
# Get the current date and time
year, month, day, weekday, hours, minutes, seconds, subseconds = rtc.datetime()
# Define the file path
file_path = 'Greeting.txt'
# Create the greeting message
file_content = (
"Hello from Pico Pi and Alpaca!\n"
f"File saved on: {year:04d}-{month:02d}-{day:02d} {hours:02d}:{minutes:02d}:{seconds:02d}.\n"
)
# Writing to the file
with open(file_path, 'w') as file:
file.write(file_content)
print("Greeting saved to Greeting.txt")
# This is the essential command to fetch your file from Pico to your computer
%fetchfile --binary "Greeting.txt" "Greeting_from_Pico.txt"
# This is a helper function to list all files currently in Pico's storage
pico.list_files()
# This is how you can remove a file
pico.remove_file('Greeting.txt')
pico.list_files()
Saving numerical data#
Once you record the signal from multiple ADCs during a single measurement, always save it in a temporary array on Pico, i.e. temporary_data.npy and then fetch it from Pico to your computer.
There is usually only enough storage on Pico for just one such aggregated array, so every time you run a new, long measurement, you must overwrite the already existing temporary_data.npy file. You could also, always remove it after use and create a new one after the next measurement.
How long is a long measurement?
Anything with more than
NUM_SAMPLES > 500might cause some trouble for storage or plotting. Remember that Pico will most likely have to handle multiple arrays of that size internally.
For the regular measurements, we recommendNUM_SAMPLES=1000
and only if you are confident that your code works, go for longer arrays in your final measurement.
In our experience, and with the most optimised code, we managed to succesfully record and handle three arrays ofNUM_SAMPLES=3600.
This procedure is again a little cumbersome, but we will push Pico to its limits in this project to run high quality measurements.
You may use the code below as a template for later use.
DATA=np.zeros((3,3))
DATA[0,:]=1
DATA[1,:]=2
DATA[2,:]=3
np.save('temporary_data.npy', DATA)
del(DATA)
%fetchfile --binary "temporary_data.npy" "Greeting_DATA_from_Pico.npy"
π‘ Always check if the Fetched XXX=XXX bytes. There are known issues with large files. Retrying often works, but for very large files, different solutions must be found.
pico.remove_file('temporary_data.npy')
πππ I1.4:Handling Errors#
Saving data and optimising storage is one thing, but Picoβs internal memory is another, and managing that is a very difficult task, so in order to prevent trouble here, we propose that you regularly reset your Pico.
What is the
machine.soft_reset()?
In short, Soft reset mimics disconnecting and connecting the USB cable between Pico and your computer.
It restarts Pico, which means that all your variables, i.e. you unsaved data, will be lost. We perform this reset to easily clear Picoβs internal memory (not the storage, not your files). This means that all typical errors with plotting and handling data will be resolved.
It also means that you will have to re-import all usual modules.
Use the two cells below to restart your Pico and start anew.
Resetting Pico#
# Note that you can execute machine.soft_reset() only when Pico is already connected.
machine.soft_reset()
# Default cell for re-connecting Pico, re-importing picotools and the usual modules.
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
# If you want to disconnect Pico from this notebook, use this command:
# %disconnect
Implement 2: Exploring the limits of the ADCs#
Did you know that measuring very low voltages with Pico might be problematic? This is especially relevant for the Basic Design. Can you argue why it is so?
Goal 1: Measure the baseline noise level, without any signal on the ADCβs.
Goal 2: Observe ADC ground error and the improvement with-12Vconnected to Cria
π‘Fritzing: ADC Test
ADC Baseline Test - Do not connect anything to ADCs
π I2.1: Baseline offset#
First, find out whatβs the baseline offset of the ADCs, so without any inputs
Behind the scenes, the
ADC0andADC1will be measured via the Alpacaβs amplifiers. Letβs see if theyβre really 0V! In detail:
Ain0viaADC0(Jumpers on AMPLIFIER DIRECT TO NANO, directly in the cut on the right side of the Cria)
Ain1viaADC1
Ain2directly
Letβs start with the reset to develop a habit:
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
# Run the test:
AMP0, AMP1, Ain2 = pico.test_ADC()
# Remember to convert ADC samples to Volts!
AMP0v = pico.convert_samples_to_volts(AMP0, gain=1)
AMP1v = pico.convert_samples_to_volts(AMP1, gain=1)
Ain2v = pico.convert_samples_to_volts(Ain2, gain=1)
# Plot the baseline offset
plt.plot(AMP0v, label='AMP0')
plt.plot(AMP1v, label='AMP1')
plt.plot(Ain2v, label='Ain2')
plt.xlabel('Sample')
plt.ylabel('Signal [V]')
plt.legend()
Itβs most likely not 0V!
Letβs run some statistics and investigate this further.
ππ I2.2: ADC - Noise#
Run the cells below to compute the errors and find out more about the nature of this noise.
avg_signal_Ain0, std_dev_Ain0 = pico.compute_noise_statistics(AMP0v)
avg_signal_Ain1, std_dev_Ain1 = pico.compute_noise_statistics(AMP1v)
avg_signal_Ain2, std_dev_Ain2 = pico.compute_noise_statistics(Ain2v)
Make a note of the magnitude of the error. Will it affect your Voltammetry measurements?
What is the magnitude of the noise? How does it compare to the resolution of ADCs?
### Notes
pico.plot_noise_spectrum(AMP0v, label="AMP0")
pico.plot_noise_spectrum(AMP1v, label="AMP1")
pico.plot_noise_spectrum(Ain2v, label="Ain2")
plt.legend()
Are there any significantly dominanting frequencies?
Letβs save the original data for practice, and for reference!
DATA=np.zeros((3,pico.NUM_SAMPLES))
DATA[0,:]=AMP0
DATA[1,:]=AMP1
DATA[2,:]=Ain2
np.save('temporary_data.npy', DATA)
del(DATA)
%fetchfile --binary "temporary_data.npy" "ADC_Baseline_Test_DATA.npy" # for I2.2
#%fetchfile --binary "temporary_data.npy" "ADC_Baseline_Test_DATA-12V.npy" #for I2.3 (the next section)
πππ I2.3: Test ADCs with -12V connected to Cria#
β οΈ Warning: Use the Fritzing below to carefully connect -12V to Cria: Orange LED will light up!
π‘ Fritzing: -12V to Cria
Connecting -12V source in Alpaca to Cria's J5:-12V in
Re-run the test from I2.2 with
-12Vconnected to the β-12V inβ pin on the multifunction connector.Use a different name for your data file on your laptop (ADC_Baseline_Test_DATA), otherwise you overwrite previous data. Use our hint in the comments.
In the cell below, compare the average noise signal without any input signal, in the two cases: with and without
-12V inArgue whether it is better to work with
-12V inor without?
### Notes
Implement 3: Exploring the limits of the DAC#
In this section, you will have a closer look at the accuracy, noise and the resolution of the DAC. Use the provided Fritzing for this test.
Goal: Measure the noise in the signal applied from
DACAto theADCs.
You will be using amplifiers (attenuations 1:1 and 1:3) and measuring directly to Ain2.
π‘ Fritzing: DAC Test
DAC Test
Letβs again start with a reset to clear Picoβs memory.
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
βββ I3.1 DAC - Accuracy#
The function in the cell below performs a sweep over all values of DACA and measures it with all three ADCs to check the accuracy of the measurement.
ref, Ain0, Ain1, Ain2, err_out_0, err_out_1, err_out_2 = pico.test_DAC_A(NUM_SAMPLES = 100, gain0=1, gain1=0.3333)
# Plot the measured DACA output vs Expected DACA output
plt.plot(ref, Ain0, label='AMP0')
plt.plot(ref, Ain1, label='AMP1')
plt.plot(ref, Ain2, label='Ain2')
plt.xlabel("Expected value from the DACA output [V]")
plt.ylabel("DACA measured output [V]")
plt.legend()
# Plot the DACA Error vs DACA expected output
plt.plot(ref, err_out_0*1e3, label='AMP0')
plt.plot(ref, err_out_1*1e3, label='AMP1')
plt.plot(ref, err_out_2*1e3, label='Ain2')
plt.xlabel("Expected value from the DACA output [V]")
plt.ylabel("DACA measured error [mV]")
plt.legend()
Make a note of the magnitude of the error. Will it affect your Voltammetry measurements?
Does it change when you disconnect -12V from Cria? Run the measurements in the section with and without this connection.
# Notes
ππ I3.2 DAC - Noise#
Run the cells below to measure the noise of the DAC output.
DCsetpoint = 1 # Set the DACA output value to test its accuracy
AMP0,AMP1,Ain2 = pico.SetDAC_and_MeasureADC0andADC1andADC2(DCsetpoint)
AMP0v = pico.convert_samples_to_volts(AMP0)
AMP1v = pico.convert_samples_to_volts(AMP1, gain=0.333)
Ain2v = pico.convert_samples_to_volts(Ain2)
plt.plot(AMP0v[:500], label='AMP0')
plt.plot(AMP1v[:500], label='AMP1')
plt.plot(Ain2v[:500], label='Ain2')
plt.xlabel('Sample')
plt.ylabel('Signal [V]')
plt.legend()
Itβs probably not as stable as you would expect it!
Letβs run some familiar statistics in the next section.
avg_signal_AMP0v, std_dev_AMP0v = pico.compute_noise_statistics(AMP0v, DC=DCsetpoint)
avg_signal_AMP1v, std_dev_AMP1v = pico.compute_noise_statistics(AMP1v, DC=DCsetpoint)
avg_signal_Ain2v, std_dev_Ain2v = pico.compute_noise_statistics(Ain2v, DC=DCsetpoint)
pico.plot_noise_spectrum(AMP0v, label="AMP0")
pico.plot_noise_spectrum(AMP1v, label="AMP1")
pico.plot_noise_spectrum(Ain2v, label="Ain2")
plt.legend()
Conclude#
Just like in the ADC Test:
Make a note of the magnitude of the error. Will it affect your Voltammetry measurements?
Is there any significantly dominant frequency? Does it change when you disconnect -12V from Cria?
What is the magnitude of the noise? How does it compare to the resolution of DAC?
The last answer is especially important for finding out the limits for the Voltammetry measurements.
### Notes
Letβs save the acquired data - for practice, and for future reference!
DATA=np.zeros((3,pico.NUM_SAMPLES))
DATA[0,:]=Ain0v
DATA[1,:]=Ain1v
DATA[2,:]=Ain2v
np.save('temporary_data.npy', DATA)
del(DATA)
%fetchfile --binary "temporary_data.npy" "DAC_Setpoint_Test_DATA_-12V.npy"
# %fetchfile --binary "temporary_data.npy" "DAC_Setpoint_Test_DATA.npy"
πππ I3.3 Resolution#
In this section, you will experimentally find the resolution of the DAC. Use the same setup as before.
The provided test function: pico.test_dac_resolution measures a voltage ramp from DACA over very fine steps of step_mV=0.1mV over a range of span_mV=10mV using the ADC0 via amplifier AMP0.
In the first attempt, DACA is set to a desired value, and it is measured only once at each step.
Run the code below
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
voltages, ADC0, ADC0_errors, mean_error = pico.test_dac_resolution(adc=pico.adc0,gain=1,step_mV=0.1,set_initial_voltage_mV=2500,span_mV=10, N_iter=1)
plt.plot(voltages, ADC0, label="ADC0 Measurement")
plt.xlabel("DAC Voltage (mV)")
plt.ylabel("ADC Reading (mV)")
plt.title("DAC Voltage vs ADC Reading from a single measurement")
plt.legend()
plt.grid(True)
Have a look at the value of the Absolute mean error printed above the plot. Does it look familiar?
Most likely itβs similar to the ADC noise and DAC accuracy that you measured before. Confirm with the statistics plot below.
plt.plot(voltages, ADC0_errors, label="ADC0 Measurement")
plt.xlabel("DAC Voltage (mV)")
plt.ylabel("ADC Reading Error (mV)")
plt.title("DAC Voltage vs ADC Reading Error from a single measurement")
plt.legend()
plt.grid(True)
We must therefore average over several measurements to resolve the finer steps. It turns out, that we must average a lot!
In the next cell, each step is measured 4000 times! So, it will take a few seconds to complete the measurement, but be patient - Itβs worth it!
If by any chance, this large number of iterations causes a problem with Pico, you just learned why we need all those cumbersome save-and-reset procedures.
Try lowering theN_iterto 1000 or so and reset Pico to resolve this issue. Then, re-run this entire section.
voltages, ADC0, ADC0_errors, noise = pico.test_dac_resolution(adc=pico.adc0,gain=1,step_mV=0.1,set_initial_voltage_mV=2500,span_mV=10, N_iter=4000)
plt.plot(voltages, ADC0, label="ADC0 Measurement")
plt.xlabel("DAC Voltage (mV)")
plt.ylabel("ADC Reading (mV)")
plt.title("DAC Voltage vs ADC Reading from averaged measurement")
plt.legend()
plt.grid(True)
If everything went well, and you see a staircase, you should be able to deduce the DACβs resolution from the plot!
And you can also confirm it from the amplitude of the error plotted below.
plt.plot(voltages, ADC0_errors, label="ADC0 Measurement")
plt.xlabel("DAC Voltage (mV)")
plt.ylabel("ADC Reading Error (mV)")
plt.title("DAC Voltage vs ADC Reading Error from averaged measurement")
plt.legend()
plt.grid(True)
Conclusions#
Think about the following questions and make some notes:
What can you conclude about:
the accuracy of the DAC?
the noise when measuring no input and the noise when measuring the DAC signal?
What happens when you try to take smaller steps than the smallest detectable increment?
In the actual measurement, you wonβt be able to measure 4000 times at each step to average over the noise. You just learned that it takes a relatively long time, but you can afford to measure a few times at each step.
In the next section, youβll explore the effect of multiple measurements on timing of the experiment.
ππ Implement 4: Timing accuracy#
The function provided below pico.test_pico_timing_with_for_loop() measures the time between each step of a measurement with averaging.
You can adjust two parameters:
N_itersets the number of measurements for all three ADCs that are then averaged at each step of your experimentdelay_mssets the delay between each step of your experiment
By default, NUM_SAMPLES=512 for demonstration.
Run the code below for different values, for example:
N_iter=1,3,10delay_ms=1,2,5,10
Make notes from your observations, and feel free to reach out to the TAs for a discussion.
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
times = pico.test_pico_timing_with_for_loop(N_iter=1,delay_ms=1)
plt.plot(times*1e-3)
plt.xlabel("Sample")
plt.ylabel("Duration of the measurement [ms]")
plt.grid(True)
### Notes
π‘ Hints
Note that pico.test_pico_timing_with_for_loop() uses a for loop for averaging over multiple measurements. It has an if-statement, and the averaging happens between the steps. Also, note that time-keeping takes some time too, so there are many operations additional to setting the DAC value and reading the ADCs.
The results above demonstrate how essential it is to optimise your code for the final measurements, especially if you are using averaging and DELAY_MS < 10msβ
Implement 5-Basic: DAC Assistant + Dual ADC#
Implement 5 is required only for the Basic Design, and optional for the Advanced Design.
Goal 1: Calibrate your DAC Assistant and save the setting to a file
Goal 2: Measure the the accuracy, and the effective resolution of the controls used in the Basic Design
Goal 3: Practice setting attenuation 1:3 for positive and negative signals
In the Alpaca Manual, you can find a formula for the output DAC Assistant:
In reality, the expected offset 2.048V might have a significant error making your potentiostat unusable.
Follow the procedure below to calibrate your DAC Assistant.
βββ I5Basic.1: Calibration#
Build the calibration setup as shown in the Fritzing below.
π‘ Fritzing: DAC Assistant Calibration
DAC Assistant Calibration Setup
Run the code in the cells below to measure the accuracy of your DAC Assistant.
This calibration procedure performs a voltage sweep from -3V to +3V and checks for the accuracy of the generated signal.
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
ref, Ain0, Ain1, Ain2, err_in, err_out = pico.test_DAC_Assistant()
# Plot the DAC Assistant measured DAC Assistant output vs DAC A output
plt.plot((5 * (ref - 2.048)), Ain0+Ain1)
plt.xlabel("Expected value converted from the DAC_A output [V]")
plt.ylabel("DAC Assistant measured output [V]")
Do you observe a large offset error in the figure above?
### Notes:
Also, check the accuracy of
DACA. Could it be causing such a large deviation?
# Plot Error on the DAC_A (SET vs GET)
plt.plot(ref, 1e3*err_in)
plt.xlabel("DAC_A voltage [V]")
plt.ylabel("DAC_A output error [mV]")
# Plot Error of the DAC_Assistant OUT (SET vs GET)
plt.plot((5 * (ref - 2.048)), err_out*1e3)
plt.xlabel("Expected DAC Assistant output [V]")
plt.ylabel("DAC Assistant output error [mV]")
It is rather unlikely that DACA inaccuracy leads to such a large error in the DAC Assistant OUT.
We measured it before already, but keep in mind that
DACAoutput error is magnified five times in theDAC Assistant OUT, so its effect here could be very significant.
This is one of the main causes of the Basic Design potentiostatβs low performance in some experiments.
At this point, we can only calibrate the offset, but the error that you see in the first from the two plots above is not likely to go away.
Run the calibration procedure in the cell below.
calibrated_offset = pico.Calibrate_DAC_Assistant(1.5)
If the calibration was successful, go ahead and SAVE THIS VALUE! β¦ and if it is not working, please report this problem to the TAs!
Optionally, come up with a way to save this value in a file!
You can also run this calibration, on demand, without any modifications - as long as your setup matches the one required for this section. This wonβt be the case for long in this project, so it is recommended that you find a practical way to include this offset in your code.
Letβs confirm that it worked by testing the accuracy again.
ref, Ain0, Ain1, Ain2, err_in, err_out = pico.test_DAC_Assistant(offset=calibrated_offset)
plt.plot((5 * (ref - calibrated_offset)), Ain0+Ain1)
plt.xlabel("DAC_A output [V]")
plt.ylabel("DAC Assistant measured output [V]")
You should see a straight line here!
Letβs check the
DACAerror again. It should stay mostly the same, but shifted a bit.
# plot Error on the DAC_A (SET vs GET)
plt.plot(ref, 1e3*err_in)
plt.xlabel("DAC_A voltage [V]")
plt.ylabel("DAC_A output error [mV]")
Itβs the moment of truth! π₯ The accuracy of the calibrated DAC Assistant isβ¦.
plt.plot((5 * (ref - calibrated_offset)), err_out*1e3)
plt.xlabel("DAC Assistant output [V]")
plt.ylabel("DAC Assistant output error [mV]")
How does it compare to the
DACAoutput error?
#Notes
πππ I5Basic.2: Resolution - DAC Assistant#
Here, once again, you will test the resolution - This time, the one of the DAC Assistant.
The insights of this section are essential for finding the limits for parameters to run Cyclic and Squarewave Voltammetry with the Basic Design
Use the same setup as in the section above and follow the steps of the familiar procedure.
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
calibrated_offset = pico.Calibrate_DAC_Assistant(1.5)
Letβs first try with just one measurement at each step.
N_iter = 1
voltages, ADC0, ADC0_errors, mean_error = pico.test_dac_assistant_resolution(adc=pico.adc0,gain=1,step_mV=0.1,set_initial_voltage_mV=2000,span_mV=10, N_iter=N_iter, calibrated_offset=calibrated_offset)
plt.plot(voltages, ADC0, label="ADC0 Measurement")
plt.xlabel("DAC Assistant Voltage (mV)")
plt.ylabel("ADC Reading (mV)")
plt.title(f'DAC Assistant Voltage vs ADC Reading avg from {N_iter} measurement(s)')
plt.legend()
plt.grid(True)
Is the Absolute mean error printed above the plot similar as for DAC in Implement3.3?
Letβs get a better idea of it with the statistics plot:
plt.plot(voltages, ADC0_errors, label="ADC0 Measurement")
plt.xlabel("DAC Assistant Voltage (mV)")
plt.ylabel("ADC Reading Error (mV)")
plt.title(f'DAC Assistant Voltage vs ADC Reading avg from {N_iter} measurement(s)')
plt.legend()
plt.grid(True)
Most likely, it is much bigger, which is expected.
Think about the gain factor in the
DAC Assistant OUTformula. Could it be related to the magnitude of this error?
So, letβs again average over many samples to remove the noise.
N_iter = 5000
voltages, ADC0, ADC0_errors, mean_error = pico.test_dac_assistant_resolution(adc=pico.adc0,gain=1,step_mV=0.1,set_initial_voltage_mV=2000,span_mV=10, N_iter=N_iter, calibrated_offset=calibrated_offset)
plt.plot(voltages, ADC0, label="ADC0 Measurement")
plt.xlabel("DAC Assistant Voltage (mV)")
plt.ylabel("ADC Reading (mV)")
plt.title("DAC Assistant Voltage vs ADC Reading from averaged measurement")
plt.legend()
plt.grid(True)
Do you still see a staircase?
In this case, the steps are most likely much bigger, and therefore, the resolution lower.
You can also confirm with the amplitude of the error plotted below.
plt.plot(voltages, ADC0_errors, label="ADC0 Measurement")
plt.xlabel("DAC Voltage (mV)")
plt.ylabel("ADC Reading Error (mV)")
plt.title("DAC Voltage vs ADC Reading Error from averaged measurement")
plt.legend()
plt.grid(True)
Conclude#
What is therefore the smallest step for the
Ucellincrement in your Voltammetry measurements with the Basic Design?And taking into considerations that in your measurements, you wonβt be able to average over thoursands of samples, but only a few. What is the realistic resolution?
### Notes:
ππ I5Basic.3: Noise - Optional#
This section is optional, but it is recommended! You can learn more about the magnitude of noise in the Basic Design to be able to identify problems later in your design
Use the same test setup as for Implement 5B.1: Calibration, follow the steps, and write down your conclusions.
This test takes a few seconds
NUM_SAMPLESsets the number of points across the test range , which is -3V to 3V by default
interationssets the number of samples at each step to computer the noise statisticsFor large values, you might expect problems with Picoβs memory and plotting.
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
calibrated_offset = pico.Calibrate_DAC_Assistant(1.5)
ref, Ain0, Ain1, Ain2, Ain0std, Ain1std, Ain2std, err_in, err_out = pico.test_DAC_Assistant_noise(offset=calibrated_offset, NUM_SAMPLES=600, iterations = 100, gain0=1, gain1=-1)
plt.plot((5 * (ref - calibrated_offset)), Ain0std*1e3, label='Noise via ADC0')
plt.plot((5 * (ref - calibrated_offset)), -Ain1std*1e3, label='Noise via ADC1')
plt.xlabel("DAC Assistant output [V]")
plt.ylabel("ADC average readout error [mV]")
plt.legend()
### Notes
Implement 6-Basic: ADC Gain 1:3#
The section I6B.1 is mandatory for Basic Design: Learn how to implement attenuation 1:3 with Basic Design.
βββ I6Basic.1: Configure and test ADC Gain 1:3#
Implement the setup presented in the Fritzing below and run the test below to find out if you got it right!
π‘ Fritzing: Basic Design - Gain 1:3
DAC Test
machine.soft_reset()
%serialconnect to --port="COM5"
%sendtofile /picotools.py --source picotools.py
import picotools as pico
import numpy as np
import matplotlib.pyplot as plt
import machine
calibrated_offset = 2.2826
# GAIN (1:3)
ref, Ain0v, Ain1v, Ain2v, err_in, err_out = pico.test_DAC_Assistant(offset=calibrated_offset, gain0=0.333, gain1=-0.333)
# Plot the DAC Assistant measured DAC Assistant output vs DAC A output
plt.plot((5 * (ref - calibrated_offset)), Ain0v+Ain1v)
plt.xlabel("Expected value converted from the DAC_A output [V]")
plt.ylabel("DAC Assistant measured output [V]")
### Notes:
π I6Basic.2: Noise - (GAIN 1:3) - Optional#
This section is entirely optional. Use it for debugging and to understand the cumulative effect of noise due to amplification and attenuation.
There is an interesting difference between the results of GAIN 1:1 (in section I5B.3) in and GAIN 1:3 (here). Can you argue whatβs behind that?
# GAIN (1:3)
ref, Ain0, Ain1, Ain2, Ain0std, Ain1std, Ain2std, err_in, err_out = pico.test_DAC_Assistant_noise(offset=calibrated_offset, NUM_SAMPLES=240, iterations = 50, gain0=0.3333, gain1=-0.3333)
plt.plot((5 * (ref - calibrated_offset)), Ain0std*1e3, label='Noise via ADC0')
plt.plot((5 * (ref - calibrated_offset)), -Ain1std*1e3, label='Noise via ADC1')
plt.xlabel("DAC Assistant output [V]")
plt.ylabel("ADC readout error [mV]")
plt.legend()
ππππ Optional - Implement 7: Write Helper Functions#
Write some helper functions to make your further steps easier. Here are some ideas:
ππππ Function to calculate the detectable
Icellrange based on theUcell,Rfand the values of attenuation. Its form will vary per design.ππππ Function to calculate the cutoff frequency of the tamed integrator.
ππ Advanced Design only - Function for using the Alpacaβs Relay for switching between two different
Rfresistors.ππ Advanced Design only - Function for including the use of DAC-B
ππππππ Your own ideas!
# Task for the students!
βββ Compare and Conclude#
To check-off with the TA: From DAC-accuracy:
Make a note of the magnitude of the error. Will it affect your Voltammetry measurements?
Does it change when you disconnect -12V from Cria? Run the measurements in the section with and without this connection.
(for the Basic Model): discuss the outcome and necessity of the DAC assistant calibration
(for the Advanced Model): discuss which additional picotools functions you will/did write for use of DAC-B
Mandatory to think about (for the report):
Argue how significant; - the ADC noise - the DAC noise - the DAC Assitant noise are for your measurements.
How can you reduce the influence of each noise?
What can you conclude about the smallest detectable increment of the:
DAC Assistant?
DAC A measured directly?
What happens when you try to take smaller steps than the smallest detectable increment?
How does it relate to the resolution of the ADCs?
Can you already predict what will be the lowest effective
Ucellincrement for Voltammetry with your design?
Challenging:
What is the estimated effective resolution in each design when you can take only a few measurements at once for averaging
How does it affect the electrochemical reaction in the measurement cell?
Very challenging:
How does the
Icellresolution depend on the ADC gain?