/duestat

OK potentiostat, one resistor

Primary LanguageC++

duestat


Overview

A long time ago I made the ardustat. This device used a simple closed loop control circuit and few DACS, resistors, and ADCS to do electrochemical experiments with an arduino. It worked pretty well, but in 2006

  1. I was young and overambitious and it had some features that traded bandwidth for accuracy in a way I now consider unacceptable (e.g. it used a digital potentiometer to set and measure current, which is not the best)
  2. It was primarily for battery cycling, and in the coming decade I got to know the just-good-enough performance that is Neware
  3. The initial interface was written in Java, which I now hate.

After I graduated and got a faculty position a few students improved the C code, but the interface and circuit design languished. I was hopeful that the nonolith CEE (now the ADALM1000) would obviate the hardware entirely. While I would gladly welcome that event, it seems to not be a priority for ADI. There have been a bunch of open source potentiostats as BS/MS projects over the last decade, but, to be honest, the part count and not-using-proto-hardware (e.g. not using arduino like things) are a turn off.

So here we are. It's 2021 and electrochemistry at a ~1 mA current and lower is still not as accessible as it should be. But! The new era of arduinos and its successors have SAMD51 M4's for like $20, and this means

  1. ADC resolution has gone to 14 bits! 👍
  2. There's now a few built-in 12 bit DACS! 👍
  3. Bus potential knocked down to 3.3 V 👎, but well, we can do some tricks and if we stick to half cells that should be OK until we need to do cathode work.....

The initial part count of the ardustat can be knocked down, since we're not going to use an off-board DAC and we're going to avoid relays for now. With something like an adafruit M4 class board, a resistor, and some patch cable we can likely do something. So let's see how it goes.

Circuit Theory

The duestat, like the ardustat before it, is closed-loop control circuit based on the simple voltage divider circuit below


DAC_set ---|                    ->  A0 (set), A3 (read)
           |
           Rfix                 
           |
           |------- ADC_cell    ->  A4 (read)
           |
           Cell
           |
DAC_gnd ---|                    ->  A1 (set), A2 (read)


ADC_ref                         -> A5

In the duestat.ino code most of the action happens at DAC_set, regardless what we're trying to do. For example:

OCV

We set DAC_set to INPUT, ideally making current flow between Cell and everything zero. We say ideally because even in INPUT the resistance isn't infinite, but order ~10M ohm. We may just need to use a relay later. For now I think it's start.

OCV is set via the JSON command

{'mode':'ocv'}

Potentiostat vs. DAC_gnd

In a two electrode electrochemical cell the goal of a potentiostatic hold (e.g. constant voltage) is to, well, set a constant voltage. In this scenario the duestat.ino code just moves DAC_set such that ADC_cell - DAC_gnd is equal to the desired value.

Note that both DAC_set and DAC_gnd can be anything between 0 V and 3.3 V on a 12 bit basis (0 to 4095 counts). So we can target ADC_cell < 0 if we set DAC_gnd above DAC_set. The code does not goal seek DAC_gnd but rather has the user set the relative range. For example for an effective range of -1.65 V to 1.65 V vs. GND DAC_gnd should be set to ~2000.

The current for the system is calculated by (DAC_set-ADC_cell)/Rfix.

This potentiostat mode is invoked by physically tying A5 to A4 and setting via the JSON command

{
    'mode':'pstat',
    'target':1.8,
    'DAC_set' : .2
}

where target is in volts and DAC_set is the DAC setting in volts.

Potentiostat vs. ADC_ref

In a three electrode electrochemical cell the goal of a potentiostatic hold is to, well, set the voltage of the working electrode (i.e. A4 @ ADC_cell) against a reference (A5 @ ADC_ref), where DAC_gnd is the counter electrode. In this scenario the duestat.ino code just moves DAC_set such that ADC_cell - ADC_ref is equal to the desired value.

The same suggestions for DAC_gnd as above hold.

Again, the current for the system is calculated by (DAC_set-ADC_cell)/Rfix.

This potentiostat mode is invoked setting via the JSON command

{
    'mode':'pstat_3',
    'target':1.8,
    'DAC_set' : .2
}

where target is in volts and DAC_set is the DAC setting in volts.

Galvanostat

In our constant current mode, we goal set given value as (DAC_set-ADC_cell)/Rfix and measure ADC_cell-DAC_gnd and/or ADC_cell-ADC_ref.

This mode is set by

{
    'mode':'gstat',
    'target': 0.000004,
    'DAC_set' : .2
}

where target is in volts (where V_target = I_target*R_fix) and DAC_set is the DAC setting in volts.

Control Method

The duestat goal seeks via P without the I or D right now because, for whatever reason, the AutoPID by Ryan Downing is failing me. It was working but now it's not. I can't even.

pid = {
    'setpid':True,
    'kp':.1,
    }

Control Server and Simple Plotting Interface

A basic interface is provided for debugging purposes in interface, it needs to be documented.

To use it have node ~16 or greater installed and then

bash first_run.sh #to get the mood right
node server.js /your/duestat/port #COMX , /dev/tty.usbmodemXXXXX, etc

You'll see an insane amount of data flowing in the console. You're smart though and know you can just send that to your file of choice by saying:

node server.js /your/duestat/port > out.tsv

You now have a server going at http://localhost:3200. You can send/get commands from the server via your favorite REST doer (I like python requests). See example_scripts/example.py for a fairly complete trial/error script.

FAQ

Q: Why put ADCs on the DACs? We could use those ADCs for other things.

A: Because I've learned the hard way (TM) to double check what the DACs are doing, especially under load. If you need more ADCs it's really easy to add those via I2C. Fast well integrated DACs are worth their weight in gold.