Today, we will use the STM32 Maple Mini to do an AC reading. In our example, we’ll get the RMS value of the power grid. This is very useful for those who want to monitor the electrical network for the Internet of Things. We will then create an application using the computational power of the Maple Mini, apply an electronic circuit capable of allowing the acquisition of a 127Vac signal, as well as apply the root mean square (RMS) calculation on the samples.
In our assembly today, we have the STM32, in addition to our analog circuit to make the input of 110. To avoid shocks, isolate the resistor that is entering by 110.
The circuit is quite sensitive. I'm getting in with 110, but I reduce it 168 times using the voltage divider and put it into the operational amplifier, which has several functions.
We also have some optional capacitors for source filtering. If your source is of good quality, you do not need to use them.
The AD input is calculated through the oscilloscope, in which you see a sinusoid, which is not 110 (but it is well formed). Another thing is that the voltage in our electrical network is not 110 (it’s actually 127 volts). But as we are undergoing a stabilizer, it will adjust to 115V.
The value displayed on the serial monitor is what is calculated in RMS, that is, the one identified by the Fluke Meter.
Source code - Definitions and constants
At first, we defined the pin reading as D11, as well as the various constants used in the calculations.
#define leituraTensao D11 //AD CH0 no pino PA0
//valor teórico divisor de tensão = 168.85714285714285714286 const float fatorDivisor = 168.40166345742404792461; //valor teórico do ganho de amplificação = 1.0 const float fatorAmplificador = 1.0; //Valor usado na multiplicação da leitura const float fatorMultiplicacao = fatorDivisor * fatorAmplificador; //Valor teórico da Tensão de alimentação Vcc = 3.3V const float Vcc = 3.3; //valor teórico do offset do amplificador = Vcc / 2.0; const float offSet = 1.66; //fator teórico da conversão do AD = 3.3 / 4095.0 const float fatorAD = Vcc / 4095.0; const int amostras = 71429; //resulta em 1,027 segundos para cada atualização //const int amostras = 35715; //resulta em 0,514 segundos para cada atualização
Source code - Global variables
Now, we define some global variables.
float Vrms = 0.0; //armazena o valor rms da tensão
float Vmax = 0.0; //armazena o valor máximo detectado float Vmin = 10000.0; //armazena o valor mínimo detectado float Vmed = 0.0; //armazena o valor médio entre Vmáx e Vmín
Source Code - Setup ()
Start the serial port at 1Mbps. We adjusted the AD port as input and waited 5 seconds before starting to collect data. Standby time is optional.
void setup() {
Serial.begin(1000000); //inicia a porta serial em 1Mbps pinMode(leituraTensao, INPUT); //ajusta a porta do AD como entrada delay(5000); //aguarda 5s antes de iniciar a coleta. (opcional) }
Source Code - Loop () - Starts the data collection variables
In the Loop, we have the variable for iteration. Here, we also store the readings of AD in 0.0 and restart the variable VRMS also in 0.0.
void loop() {
int i = 0; //variável para iteração float leitura = 0.0; //armazena as leituras do AD Vrms = 0.0; //reinicia a variável Vrms
Source Code - Captures and executes the individual calculations for each sample
At this stage, if i is smaller than the sample, we start a sampling cycle until i reaches the number of samples. We run analogRead to read the analog port and calculate the sum of the squares of the read voltages. Finally, we increment the iterator.
while (i < amostras) { //inicia um ciclo de amostragem até que i alcance o número de amostras
leitura = analogRead(leituraTensao); //lê a porta analógica //Serial.println(leitura); //Descomente se quiser ver o sinal bruto do AD Vrms = Vrms + pow(((leitura * fatorAD) - offSet), 2.0); //calcula a soma dos quadrados das tensões lidas i++; //incrementa o iterador }
Source code - General calculations of the samples and identification of maximum, minimum, and average
We apply the multiplication fact to determine the actual value of the voltages. We detect whether the value is maximum or minimum, and we calculate the average of the current maximum and minimum values.
//Aplicando fator de multiplicação para determinar o valor real das tensões
Vrms = (sqrt(Vrms / amostras)) * fatorMultiplicacao; //detecta se é um valor é máximo if (Vrms > Vmax) { Vmax = Vrms; } //detecta se é um valor mínimo if (Vrms < Vmin) { Vmin = Vrms; } //calcula a média dos valores máximo e mínimo atuais Vmed = (Vmax + Vmin) / 2.0;
Source Code - Output Options
We have three options for "plotting" the output value. We have output formatted to the Arduino IDE serial plotter, like CSV or Jason.
//saída formatada para plotter serial IDE Arduino
Serial.print(Vrms, 3); Serial.print(","); Serial.print(Vmax, 3); Serial.print(","); Serial.print(Vmin, 3); Serial.print(","); Serial.println(Vmed, 3); /* //saída formatada como json Serial.print("{\"instante(ms)\":"); Serial.print(millis()); Serial.print(","); Serial.print("\"Vrms(V)\":"); Serial.print(Vrms, 3); Serial.print(","); Serial.print("\"Vmax(V)\":"); Serial.print(Vmax, 3); Serial.print(","); Serial.print("\"Vmin(V)\":"); Serial.print(Vmin, 3); Serial.print(","); Serial.print("\"Vmed(V)\":"); Serial.print(Vmed, 3); Serial.println("}"); */ /* //saída formatada como CSV Serial.print(millis()); Serial.print(","); Serial.print(Vrms, 3); Serial.print(","); Serial.print(Vmax, 3); Serial.print(","); Serial.print(Vmin, 3); Serial.print(","); Serial.println(Vmed, 3); */ }