Impedance Spectroscopy and Manual Control Sampling Rate
@Will-Dickson Thank you for your response. The software limitation makes sense. Ideally we're looking to measure the impedance of the cell in response to a 1000Hz sine wave, so I think you're right: our best bet will be to modify the firmware to achieve a higher sampling rate via the Teensy board. Where do you think would be the best place in the firmware to start? I'll take a look now and see what I can do.
I have changed the MinimumSamplePeriod value in the ps_constants.cpp file in the firmware. However, I'm running into problems trying to upload the modified firmware to the Teensy.
I have Arduino and Teensy installed. I put all of the header files and .cpp files from the firmware/libraries/potentiostat folder into the /firmware/potentiostat folder with the potentiostat.ino file. The Arduino IDE compiles this file, and successfully uploads it via Teensyduino. However, when I attempt to run the example CV script from python, I get an error stating that “No JSON Object Could Be Decoded”.
I went back and made sure to zip the Array and ArduinoJson libraries and include them via the Arduino IDE in the potentiostat.ino file. However, when these libraries are included in that file the Arduino IDE will no longer compile the potentiostat.ino file, returning multiple errors about redefining various structs related to JSON.
This may seem dumb, but what is the correct procedure on how to upload new firmware for the Rodeostat?
When re-programming the firmware make sure the "tools -> optimize" is set to FAST. Right now if it is set to FASTER, which is the default, there will be an issue with the ArduinoJson library.
This is something I'm going to fix soon - just need to upgrade to the latest version of the ArduionJson library and the problem goes away. Then the optimize setting won't matter. There is a little bit of an API change between the new and old version of the library - so I want to test a bit first.
My apologies for the late update.
Changing the optimization to FAST from FASTER allowed for the firmware to correctly update.
After some more testing, changing the MinimumSamplePeriod value allows me to manually set the sampling period/rate to be much faster than before without the command being rejected for being too small. However, when I run the sinusoid test, I still cannot apply a waveform of a frequency higher than 500Hz.
I will continue looking through the firmware to see where I can change this, as well as do further sampling frequency tests with different firmware tests. Any recommendations on where else in the firmware to increase sampling frequency? I’ve begun looking into the firmware files for the periodic test and the sinusoid test.
I think that to get to higher frequencies you made need to do a bit more than just change the MinimumSamplePeriod. The MinimumSamplePeriod just sets how fast data is streamed back to the PC.
First, the voltage is set via the DAC in the testTimer callback. This timer runs are a frequency set by TestTimerPeriod (set in ps_constants.cpp). The value for this in the stock firmware is 200us - so 5000Hz. So with a 500Hz sine wave you will only have about 10 points per cycle. So you will probably want to decrease the TestTimerPeriod in order to get more points per sinewave.
Second, currently the output voltages for the sinewave test are set in a lookup table (see ps_sinusoid_test.h and .cpp) which is interpolated at intermediate time points. This isn't really optimized for speed - it is meant for lower frequencies where frequency of the sinewave is much less than the TestTimerPeriod. In the higher frequency case you may need to do this more efficiently - maybe just have the lookup table store the output values directly - so you don't have to do any computation and can just set the output voltage from the lookup table value.
Third, at higher frequencies streaming back the values during the test might not be feasible. The way we are doing it now - as JSON - definitely isn't the most efficient method. It is nice and clear, but not really meant for speed. So you might need to modify things so either the data is packaged more efficiently. Or maybe buffer the data and send it back after the test is finished or stream it in chunks consisting of data for multiple samples. Also, note, USB frames are 1ms so when sending back samples one at a time the best you could probably do would be 1kHz - and you might be able to achieve that.
This post is deleted!
I’ve followed your first point and changed the testTimerPeriod to be much lower. In addition, I noticed that for periodic tests the period was limited to an integer in units of milliseconds, which would limit the period of the applied wave to 1 ms, for a maximum applied frequency of 1000 Hz. So in the ps_periodic_test.cpp file I removed the call to convertMstoUs() in order to fix this.
In the further testing I’ve done, I’ve seen strange results. The Start and End time are correct for my plots, and the correct number of points are plotted based on my sampling rate, even if the sampling period is set to be quite low. However, in the returned t vector, there are multiple time points. e.g., we’ll see two 0.0s, two 0.001s, and so on until the end of the test.
This appears to be a sort of delay of 1ms, where the program attempts to sample but can’t move forward. I believe you are correct: and this has to do with the data streaming over USB.
For our purposes, we do not need to send data back to the computer until the test is complete. Thus, we would be happy to modify the data transmission to send the time, voltage, and current arrays over USB after the test is over. So essentially, we want to do like you said an buffer the data, sending it after the test is over. What would we need to change to do this?
Where is the current data streaming controlled? Is this within the Python library, JSON, or the Arduino code? What specific files or scripts would I need to modify?
EDIT: If we do need to modify the JSON library, how would we incorporate these changes to the library? I’m not all that familiar with JSON to be honest.
We ended up encapsulating the everything in the SystemState::serviceDataBuffer() in the ps_system_state.cpp file after the comment "//Empty data buffer" in an if statement. The data buffer is only emptied once the test is finished running.
After making this change, the t vectors returned by the test were still delayed, each point was a minimum of 1ms apart, and thus we had repeated points. However, after creating our own t vectore with the know t step, the plots came out quite nicely, and we were able to run waveforms up to 10kHz.
Due to the limitation on data buffer size, slower waveforms must be analyzed with a sample rate such that the number of points doesn’t exceed the data buffer. But this is not big deal, as for our purposes we only need the high sampling rate for the fast waveforms.. I may do some further modifications to change the buffer size to allow for high sampling rates on slow waves, but for now the above modifications have sufficed to allow us to run waveforms of up to 10kHz.
Next we’ll be working on calculating impedance and phase from these output data arrays.
After buffering the data, the limitation in sampling frequency appears to come from the testTimer function. This function has a specific runtime, and runs on a timer interrupt. If the timer interrupt's period (testTimerPeriod in ps_constants.cpp) is smaller than the actual time it takes for the function to run, the function does not run correctly, yielding incorrect and strange results.
For those looking to increase their sampling frequency, this seems to be the next bottleneck after the data streaming. Perhaps some alteration/optimization of this function could increase the sampling rate slightly, perhaps not.
Ultimately, to increase the sampling frequency of the Rodeostat, you can:
Decrease MinimumSamplingPeriod within ps_constants.cpp in order to remove the software limitation on sampling frequency Remove calls to convertMstoUs in ps_periodic test and in ps_system_state.cpp, allowing for periods to be passed directly in units of us, rather than being limited to a sampling period of an integer in ms In serviceDataBuffer (in ps_system_state.cpp) , modify the function so that it does not transmit data until test done flag has been set (by putting an if statement around most of the function).
With this we were able to achieve higher than the stock sampling frequency for a small number of periods (this was limited by the memory, since the data buffer would get filled with too many points since we did not clear it until the end). Unfortunately, high frequencies like 10kHz or 100kHz did not seem to be reachable.
In addition, thank you @Will-Dickson for your help figuring this out.
I'm attempting to implement something similar to perform AC voltammetry at higher sample rates and frequency but am struggling with my understanding of Arduino code. Where exactly in serviceDataBuffer (in ps_system_state.cpp) does the If statement go?
I've tried inserting
if (run_complete)into a few places that seeem logical to me but either it has no effect or everything breaks.
Could anyone enlighten me as to how to implement these changes in greater details?