We will control a series RC circuit as shown here.

Writing the circuit equation we have: $$C \frac{dv_c} {dt} = \frac{v_{in}}{RC} -\frac{v_c}{RC}$$ which in state form gives us $$\dot v_c = [-1/(RC)]v_c + [1/(RC)]v_{in}$$ and from this we see that $\mathbf A = [-1/(RC)]$, $\mathbf B = [1/(RC)]$, and $\mathbf C = 1$. The eigenvalue of the system is $-1/(RC)$. The input $\mathbf u = v_{in}$, and the output is taken to be the capacitor voltage. We know analytically the capacitor voltage time response to the initial condition, is $$v_c(t) = v_c(0) e^{-t/(RC)}$$. We simulate that below in python.

In [1]:

```
import matplotlib.pyplot as plt
import numpy as np
import control as ctrl
R = 22e3
C = 470e-6
circuit = ctrl.ss(-1/(R*C), 1/(R*C), 1, 0)
t = np.arange(0, 30, 0.01)
u = np.zeros(len(t))
x0 = 1.5
t, vc = ctrl.input_output_response(circuit, t, u, x0)
plt.plot(t,vc)
plt.grid()
plt.title("Response of RC Circuit")
plt.legend(["$v_c(t)$"])
plt.xlabel("time (seconds)")
plt.ylabel("Voltage (Volts)")
plt.show()
print("The eigenvalue is: ",-1/(R*C))
print("numpy gives the eigenvalue, and eigen vector respectively as: ")
np.linalg.eig(circuit.A)
```

The eigenvalue is: -0.09671179883945842 numpy gives the eigenvalue, and eigen vector respectively as:

Out[1]:

(array([-0.0967118]), array([[1.]]))

Suppose we want to make the response ten times as fast. $|s\mathbf I - (\mathbf {A -BG}) | = 0 = s+10/(RC)$ which means $G = 2/RC$. So $$s+1/(RC) + [9/(RC)]G = s+10/(RC)$$ which simplifies to $$ 1 +G = 10$$ or $$G = 9$$ We will use the ESP32 which has an ADC and a DAC. The ADC gives 12 bits and we will used ADC0 on pin 36. DAC1 and DAC2 are 8 bits on pin 25 and 26 respectively. We will use one to do the control and the other as a quasi-ground, "GND" at $3.3V/2$. The MCU will only put out and measure positive voltages, but we need to have bipolar capability for our feedback to coast back to zero output, so we will use one DAC for the driving voltage source, $v_{in}$ and another DAC to supply $3.3V/2$ to the "GND" side of our capacitor, thus raising all the voltages up by $3.3V/2$, so the MCU can have an essentially bipolar output. We will use the 6302view software to set the sample rate and to collect and view all the variables we care about.

Note: It is tempting to try and use a square wave to set up the initial condition, and let it coast between the two values of the square wave. This works for a simple RC circuit, because the DC gain is unity, however, it will not work when we apply state feedback. With state feedback, the DC gain is a function of the amount of feedback we apply. Because of this, we will set up the initial condition without feedback using a square wave from the MCU, and then let the system coast from that initial condition to our zero, "GND", the $3.3V/2$ voltage with or without feedback as desired. Let's simulate what happens without feedback, so we know what to expect, with our real system and 6302view without feedback.

The bump at about 50 seconds is an error. I submitted a bug report to the python control package at this URL. https://github.com/python-control/python-control/issues/890

Now let's get python to give us the gain matrix, $\mathbf G$.

In [2]:

```
G = ctrl.place(circuit.A, circuit.B, -10/R/C)
print(G)
```

[[9.]]

We need to think about this carefully, because the DAC can only put out voltages in the range $(0,3.3)$ volts and not source more that 12mA of current. These restrictions mean the resistance must be bigger than $3.3V/12mA = 275 \Omega$. This is why we feed in a square wave above with an amplitude about $1.4$ volts centered on $3.3V/2$, and why we picked $R = 22k\Omega$ and $C = 470 uF$ . The DAC voltages are set by the integers in the interval $(0, 255)$, so the scale factor for DAC number to voltage is $256/3.3$ counts per volt.

Here is the code for that drives the RC circuit with the appropriate square wave voltage:

/* For this demo, make sure the `#define S302_SERIAL` macro is chosen in the library. */ #include // These values are in microseconds #define STEP_TIME 10000 #define REPORT_TIME 100000 #define SQ_WAVE_PEAK 228 #define SQ_PERIOD 30000 // Period of driving square wave cycle in ms. #define SQ_WAVE_ZERO 128 #define DAC1PIN 25 #define DAC2PIN 26 #define ADCPIN 36 #define LED 2 CommManager cm(STEP_TIME, REPORT_TIME); float adcValue, floatU; int32_t initMillis, myMilliseconds, sqAmp = SQ_WAVE_PEAK, u = 128; bool state = true; // Start with no state feedback float R = 22000.0; float C = 470e-6; void setup() { pinMode(LED, OUTPUT); dacWrite(DAC2PIN, 128); // Setup our "GND" /* Add modules */ cm.addPlot(&adcValue, "ADC Voltage", -0.1, 3.3 / 2., 2000, 1); cm.addNumber(&myMilliseconds, "Milliseconds"); cm.addNumber(&u, "u"); cm.addToggle(&state, "Apply State Feedback"); cm.addPlot(&floatU, "u", 0, 256, 2000, 1); initMillis = millis(); /* Ready to communicate over serial */ cm.connect(&Serial, 115200); } void loop() { if (millis() - initMillis > SQ_PERIOD) { initMillis = millis(); if (sqAmp == SQ_WAVE_PEAK) { sqAmp = SQ_WAVE_ZERO; } else { sqAmp = SQ_WAVE_PEAK; } } adcValue = (analogRead(ADCPIN) - 4096 / 2) * 3.3 / 4096; // Read the difference between "GND" and v_c. // This should place the pole as -2/(R*C). if (u >= 255) { u = 255; digitalWrite(LED, HIGH); } else if (u <= 0) { u = 0; digitalWrite(LED, HIGH); } else { digitalWrite(LED, LOW); } if (!state | (sqAmp == SQ_WAVE_PEAK)) { u = sqAmp; } else { u = sqAmp - (int)(adcValue * 9 / R / C * 256.0 / 3.3); // G = 1,u = - B*G*v_c. The conversion from DAC1 value to volts is (256/3.3V). } floatU = float(u); dacWrite(DAC1PIN, u); myMilliseconds = (int32_t)millis(); cm.step(); };